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