Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(290)

Side by Side Diff: apps/saved_files_service.cc

Issue 14607023: Add support for persistent file access in apps. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 <algorithm>
8
9 #include "apps/saved_files_service_factory.h"
10 #include "base/basictypes.h"
11 #include "base/hash_tables.h"
12 #include "base/value_conversions.h"
13 #include "chrome/browser/extensions/extension_host.h"
14 #include "chrome/browser/extensions/extension_prefs.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_system.h"
17 #include "chrome/common/extensions/permissions/api_permission.h"
18 #include "chrome/common/extensions/permissions/permission_set.h"
19
20 namespace apps {
21
22 using extensions::APIPermission;
23 using extensions::Extension;
24 using extensions::ExtensionHost;
25
26 namespace {
27
28 using extensions::ExtensionPrefs;
29 using extensions::ExtensionSystem;
30
31 // Preference keys
32
33 // The file entries that an extension has permission to access.
34 const char kFileEntries[] = "file_entries";
35
36 // The path to a file entry that an extension had permission to access.
37 const char kFileEntryPath[] = "path";
38
39 // Whether or not an extension had write access to a file entry.
40 const char kFileEntryWritable[] = "writable";
41
42 // The sequence number in the LRU of the file entry.
43 const char kFileEntrySequenceNumber[] = "sequence_number";
44
45 const size_t kDefaultMaxSavedFileEntries = 500;
Matt Giuca 2013/05/17 08:28:45 I think that the only way to get a non-default siz
Sam McNally 2013/05/20 01:17:13 Done.
46 const int kDefaultMaxSequenceNumber = kint32max;
Matt Giuca 2013/05/17 08:28:45 And this to kMaxSequenceNumber.
Sam McNally 2013/05/20 01:17:13 Done.
47
48 size_t g_max_saved_file_entries = kDefaultMaxSavedFileEntries;
Matt Giuca 2013/05/17 08:28:45 ... and add a comment here that this might be diff
Sam McNally 2013/05/20 01:17:13 Done.
49 int g_max_sequence_number = kDefaultMaxSequenceNumber;
50
51 void AddSavedFileEntry(ExtensionPrefs* prefs,
52 const std::string& extension_id,
53 const SavedFileEntry& file_entry) {
54 ExtensionPrefs::ScopedDictionaryUpdate update(
55 prefs, extension_id, kFileEntries);
56 DictionaryValue* file_entries = update.Get();
57 if (!file_entries)
58 file_entries = update.Create();
59 DCHECK(!file_entries->GetDictionaryWithoutPathExpansion(file_entry.id, NULL));
60
61 DictionaryValue* file_entry_dict = new DictionaryValue();
62 file_entry_dict->Set(kFileEntryPath, CreateFilePathValue(file_entry.path));
63 file_entry_dict->SetBoolean(kFileEntryWritable, file_entry.writable);
64 file_entry_dict->SetInteger(kFileEntrySequenceNumber,
65 file_entry.sequence_number);
66 file_entries->SetWithoutPathExpansion(file_entry.id, file_entry_dict);
67 }
68
69 void UpdateSavedFileEntry(ExtensionPrefs* prefs,
70 const std::string& extension_id,
71 const SavedFileEntry& file_entry) {
72 ExtensionPrefs::ScopedDictionaryUpdate update(
73 prefs, extension_id, kFileEntries);
74 DictionaryValue* file_entries = update.Get();
75 DCHECK(file_entries);
76 DictionaryValue* file_entry_dict = NULL;
77 file_entries->GetDictionaryWithoutPathExpansion(file_entry.id,
78 &file_entry_dict);
79 DCHECK(file_entry_dict);
80 file_entry_dict->SetInteger(kFileEntrySequenceNumber,
81 file_entry.sequence_number);
82 }
83
84 void RemoveSavedFileEntry(ExtensionPrefs* prefs,
85 const std::string& extension_id,
86 const std::string& file_entry_id) {
87 ExtensionPrefs::ScopedDictionaryUpdate update(
88 prefs, extension_id, kFileEntries);
89 DictionaryValue* file_entries = update.Get();
90 if (!file_entries)
91 file_entries = update.Create();
92 file_entries->RemoveWithoutPathExpansion(file_entry_id, NULL);
93 }
94
95 void ClearSavedFileEntries(ExtensionPrefs* prefs,
96 const std::string& extension_id) {
97 prefs->UpdateExtensionPref(extension_id, kFileEntries, NULL);
98 }
99
100 void GetSavedFileEntries(ExtensionPrefs* prefs,
101 const std::string& extension_id,
102 std::vector<SavedFileEntry>* out) {
103 const DictionaryValue* file_entries = NULL;
104 if (!prefs->ReadPrefAsDictionary(extension_id, kFileEntries, &file_entries))
105 return;
106
107 for (DictionaryValue::Iterator it(*file_entries); !it.IsAtEnd();
108 it.Advance()) {
109 const DictionaryValue* file_entry = NULL;
110 if (!it.value().GetAsDictionary(&file_entry))
111 continue;
112 const base::Value* path_value;
113 if (!file_entry->Get(kFileEntryPath, &path_value))
114 continue;
115 base::FilePath file_path;
116 if (!GetValueAsFilePath(*path_value, &file_path))
117 continue;
118 bool writable = false;
119 if (!file_entry->GetBoolean(kFileEntryWritable, &writable))
120 continue;
121 int sequence_number = 0;
122 if (!file_entry->GetInteger(kFileEntrySequenceNumber, &sequence_number))
123 continue;
124 if (!sequence_number)
125 continue;
126 out->push_back(
127 SavedFileEntry(it.key(), file_path, writable, sequence_number));
128 }
129 }
130
131 } // namespace
132
133 class SavedFilesService::SavedFiles {
134 public:
135 SavedFiles(Profile* profile, const std::string& extension_id);
136 ~SavedFiles();
137
138 void RetainFileEntry(const std::string& id,
139 const base::FilePath& file_path,
140 bool writable);
141 void MoveEntryToFrontOfQueue(const std::string& id);
142 bool IsRetained(const std::string& id) const;
143 bool GetFileEntry(const std::string& id, SavedFileEntry* out) const;
144 std::vector<SavedFileEntry> GetAllFileEntries() const;
145
146 private:
147 void MaybeCompactSequenceNumbers();
148
149 Profile* profile_;
150 const std::string extension_id_;
151
152 // Owns values.
153 base::hash_map<std::string, SavedFileEntry*> file_id_to_file_entry_map_;
154
155 STLValueDeleter<base::hash_map<std::string, SavedFileEntry*> >
156 file_id_to_file_entry_map_deleter_;
157
158 // Values are a subset of values in file_id_to_file_entry_map_.
159 std::map<int, SavedFileEntry*> saved_file_lru_;
160
161 DISALLOW_COPY_AND_ASSIGN(SavedFiles);
162 };
163
164 // static
165 SavedFilesService* SavedFilesService::Get(Profile* profile) {
166 return SavedFilesServiceFactory::GetForProfile(profile);
167 }
168
169 SavedFilesService::SavedFilesService(Profile* profile)
170 : extension_id_to_saved_files_deleter_(&extension_id_to_saved_files_),
171 profile_(profile) {
172 registrar_.Add(this,
173 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
174 content::NotificationService::AllSources());
175 registrar_.Add(this,
176 chrome::NOTIFICATION_APP_TERMINATING,
177 content::NotificationService::AllSources());
178 }
179
180 SavedFilesService::~SavedFilesService() {}
181
182 void SavedFilesService::Observe(int type,
183 const content::NotificationSource& source,
184 const content::NotificationDetails& details) {
185 switch (type) {
186 case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
187 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
188 const Extension* extension = host->extension();
189 if (extension)
190 ClearExtension(extension->id());
191 break;
192 }
193
194 case chrome::NOTIFICATION_APP_TERMINATING: {
195 // Stop listening to NOTIFICATION_EXTENSION_HOST_DESTROYED in particular
196 // as all extension hosts will be destroyed as a result of shutdown.
197 registrar_.RemoveAll();
198 break;
199 }
200 }
201 }
202
203 void SavedFilesService::RetainFileEntry(const std::string& extension_id,
204 const std::string& id,
205 const base::FilePath& file_path,
206 bool writable) {
207 GetOrInsert(extension_id)->RetainFileEntry(id, file_path, writable);
208 }
209
210 void SavedFilesService::MoveEntryToFrontOfQueue(const std::string& extension_id,
211 const std::string& id) {
212 GetOrInsert(extension_id)->MoveEntryToFrontOfQueue(id);
213 }
214
215 std::vector<SavedFileEntry> SavedFilesService::GetAllFileEntries(
216 const std::string& extension_id) {
217 return GetOrInsert(extension_id)->GetAllFileEntries();
218 }
219
220 bool SavedFilesService::IsRetained(const std::string& extension_id,
221 const std::string& id) {
222 return GetOrInsert(extension_id)->IsRetained(id);
223 }
224
225 bool SavedFilesService::GetFileEntry(const std::string& extension_id,
226 const std::string& id,
227 SavedFileEntry* out) {
228 return GetOrInsert(extension_id)->GetFileEntry(id, out);
229 }
230
231 void SavedFilesService::ClearExtensionForTest(const std::string& extension_id) {
232 ClearExtension(extension_id);
233 }
234
235 SavedFilesService::SavedFiles* SavedFilesService::GetOrInsert(
236 const std::string& extension_id) {
237 std::map<std::string, SavedFiles*>::iterator it =
238 extension_id_to_saved_files_.find(extension_id);
239 if (it != extension_id_to_saved_files_.end())
240 return it->second;
241
242 SavedFiles* saved_files = new SavedFiles(profile_, extension_id);
243 extension_id_to_saved_files_.insert(
244 std::make_pair(extension_id, saved_files));
245 const Extension* extension = ExtensionSystem::Get(profile_)->
246 extension_service()->extensions()->GetByID(extension_id);
247 DCHECK(extension);
248 if (!extension->GetActivePermissions()->HasAPIPermission(
249 APIPermission::kFileSystemRetainFiles)) {
250 extensions_to_clear_.insert(extension_id);
251 }
252 return saved_files;
253 }
254
255 void SavedFilesService::ClearExtension(const std::string& extension_id) {
256 std::map<std::string, SavedFiles*>::iterator it =
257 extension_id_to_saved_files_.find(extension_id);
258 if (it != extension_id_to_saved_files_.end()) {
259 delete it->second;
260 extension_id_to_saved_files_.erase(it);
261 }
262 std::set<std::string>::iterator jt = extensions_to_clear_.find(extension_id);
263 if (jt != extensions_to_clear_.end()) {
264 extensions_to_clear_.erase(jt);
265 ClearSavedFileEntries(extensions::ExtensionSystem::Get(profile_)->
266 extension_service()->extension_prefs(),
267 extension_id);
268 }
269 }
270
271 SavedFilesService::SavedFiles::SavedFiles(Profile* profile,
272 const std::string& extension_id)
273 : profile_(profile),
274 extension_id_(extension_id),
275 file_id_to_file_entry_map_deleter_(&file_id_to_file_entry_map_) {
276 std::vector<SavedFileEntry> saved_entries;
277 ExtensionPrefs* prefs = extensions::ExtensionSystem::Get(profile)->
278 extension_service()->extension_prefs();
279 GetSavedFileEntries(prefs, extension_id_, &saved_entries);
280 for (std::vector<SavedFileEntry>::iterator it = saved_entries.begin();
281 it != saved_entries.end(); ++it) {
282 SavedFileEntry* file_entry = new SavedFileEntry(*it);
283 file_id_to_file_entry_map_.insert(
284 std::make_pair(file_entry->id, file_entry));
285 saved_file_lru_.insert(
286 std::make_pair(file_entry->sequence_number, file_entry));
287 }
288 }
289
290 SavedFilesService::SavedFiles::~SavedFiles() {}
291
292 void SavedFilesService::SavedFiles::RetainFileEntry(
293 const std::string& id,
294 const base::FilePath& file_path,
295 bool writable) {
296 if (ContainsKey(file_id_to_file_entry_map_, id))
297 return;
298
299 file_id_to_file_entry_map_.insert(
300 std::make_pair(id, new SavedFileEntry(id, file_path, writable, 0)));
301 }
302
303 void SavedFilesService::SavedFiles::MoveEntryToFrontOfQueue(
304 const std::string& id) {
305 base::hash_map<std::string, SavedFileEntry*>::iterator it =
306 file_id_to_file_entry_map_.find(id);
307 if (it == file_id_to_file_entry_map_.end())
308 return;
309
310 SavedFileEntry* file_entry = it->second;
311 int old_sequence_number = file_entry->sequence_number;
312 if (!saved_file_lru_.empty()) {
313 std::map<int, SavedFileEntry*>::reverse_iterator it =
314 saved_file_lru_.rbegin();
315 if (it->second == file_entry)
316 return;
317
318 file_entry->sequence_number = it->first + 1;
319 } else {
320 file_entry->sequence_number = 1;
321 }
322 saved_file_lru_.insert(
323 std::make_pair(file_entry->sequence_number, file_entry));
324 ExtensionPrefs* prefs = extensions::ExtensionSystem::Get(profile_)->
325 extension_service()->extension_prefs();
326 if (old_sequence_number) {
327 saved_file_lru_.erase(old_sequence_number);
328 UpdateSavedFileEntry(prefs, extension_id_, *file_entry);
329 } else {
330 AddSavedFileEntry(prefs, extension_id_, *file_entry);
331 if (saved_file_lru_.size() > g_max_saved_file_entries) {
332 std::map<int, SavedFileEntry*>::iterator it = saved_file_lru_.begin();
333 it->second->sequence_number = 0;
334 RemoveSavedFileEntry(prefs, extension_id_, it->second->id);
335 saved_file_lru_.erase(it);
336 }
337 }
338 MaybeCompactSequenceNumbers();
339 }
340
341 bool SavedFilesService::SavedFiles::IsRetained(const std::string& id) const {
342 return ContainsKey(file_id_to_file_entry_map_, id);
343 }
344
345 bool SavedFilesService::SavedFiles::GetFileEntry(const std::string& id,
346 SavedFileEntry* out) const {
347 base::hash_map<std::string, SavedFileEntry*>::const_iterator it =
348 file_id_to_file_entry_map_.find(id);
349 if (it == file_id_to_file_entry_map_.end())
350 return false;
351
352 *out = *it->second;
353 return true;
354 }
355
356 std::vector<SavedFileEntry>
357 SavedFilesService::SavedFiles::GetAllFileEntries() const {
358 std::vector<SavedFileEntry> result;
359 for (std::map<int, SavedFileEntry*>::const_iterator it =
360 saved_file_lru_.begin(); it != saved_file_lru_.end(); ++it) {
361 result.push_back(*it->second);
362 }
363 return result;
364 }
365
366 void SavedFilesService::SavedFiles::MaybeCompactSequenceNumbers() {
367 std::map<int, SavedFileEntry*>::reverse_iterator it =
368 saved_file_lru_.rbegin();
369 if (it == saved_file_lru_.rend())
370 return;
371
372 if (it->first < g_max_sequence_number)
373 return;
374
375 int sequence_number = 0;
376 ExtensionPrefs* prefs = extensions::ExtensionSystem::Get(profile_)->
377 extension_service()->extension_prefs();
378 for (std::map<int, SavedFileEntry*>::iterator it = saved_file_lru_.begin();
379 it != saved_file_lru_.end(); ++it) {
380 sequence_number++;
381 if (it->second->sequence_number == sequence_number)
382 continue;
383
384 SavedFileEntry* file_entry = it->second;
385 file_entry->sequence_number = sequence_number;
386 UpdateSavedFileEntry(prefs, extension_id_, *file_entry);
387 if (it == saved_file_lru_.begin()) {
388 saved_file_lru_.erase(it);
389 it = saved_file_lru_.insert(std::make_pair(file_entry->sequence_number,
390 file_entry)).first;
391 } else {
392 saved_file_lru_.erase(it--);
393 it = saved_file_lru_.insert(
394 it, std::make_pair(file_entry->sequence_number, file_entry));
395 }
396 }
397 }
398
399 // static
400 void SavedFilesService::SetMaxSequenceNumberForTest(int max_value) {
401 g_max_sequence_number = max_value;
402 }
403
404 // static
405 void SavedFilesService::ClearMaxSequenceNumberForTest() {
406 g_max_sequence_number = kDefaultMaxSequenceNumber;
407 }
408
409 // static
410 void SavedFilesService::SetLruSizeForTest(int size) {
411 g_max_saved_file_entries = size;
412 }
413
414 // static
415 void SavedFilesService::ClearLruSizeForTest() {
416 g_max_saved_file_entries = kDefaultMaxSavedFileEntries;
417 }
418
419 } // namespace apps
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698