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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: apps/saved_files_service.cc
diff --git a/apps/saved_files_service.cc b/apps/saved_files_service.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b3713e3d0ad90fe38bc313d1f5e7fd504bb0e006
--- /dev/null
+++ b/apps/saved_files_service.cc
@@ -0,0 +1,406 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "apps/saved_files_service.h"
+
+#include <algorithm>
+
+#include "apps/saved_files_service_factory.h"
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/value_conversions.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/common/extensions/permissions/api_permission.h"
+#include "chrome/common/extensions/permissions/permission_set.h"
+
+namespace apps {
+
+using extensions::APIPermission;
+using extensions::Extension;
+using extensions::ExtensionHost;
+using extensions::ExtensionPrefs;
+
+namespace {
+
+// Preference keys
+
+// The file entries that an extension has permission to access.
+const char kFileEntries[] = "file_entries";
+
+// The path to a file entry that an extension had permission to access.
+const char kFileEntryPath[] = "path";
+
+// Whether or not an extension had write access to a file entry.
+const char kFileEntryWritable[] = "writable";
+
+// The sequence number in the LRU of the file entry.
+const char kFileEntrySequenceNumber[] = "sequence_number";
+
+const size_t kMaxSavedFileEntries = 500;
+const int kMaxSequenceNumber = kint32max;
+
+// These might be different to the constant values in tests.
+size_t g_max_saved_file_entries = kMaxSavedFileEntries;
+int g_max_sequence_number = kMaxSequenceNumber;
+
+void AddSavedFileEntry(ExtensionPrefs* prefs,
+ const std::string& extension_id,
+ const SavedFileEntry& file_entry) {
+ ExtensionPrefs::ScopedDictionaryUpdate update(
+ prefs, extension_id, kFileEntries);
+ DictionaryValue* file_entries = update.Get();
+ if (!file_entries)
+ file_entries = update.Create();
+ DCHECK(!file_entries->GetDictionaryWithoutPathExpansion(file_entry.id, NULL));
+
+ DictionaryValue* file_entry_dict = new DictionaryValue();
+ file_entry_dict->Set(kFileEntryPath, CreateFilePathValue(file_entry.path));
+ file_entry_dict->SetBoolean(kFileEntryWritable, file_entry.writable);
+ file_entry_dict->SetInteger(kFileEntrySequenceNumber,
+ file_entry.sequence_number);
+ file_entries->SetWithoutPathExpansion(file_entry.id, file_entry_dict);
+}
+
+void UpdateSavedFileEntry(ExtensionPrefs* prefs,
+ const std::string& extension_id,
+ const SavedFileEntry& file_entry) {
+ ExtensionPrefs::ScopedDictionaryUpdate update(
+ prefs, extension_id, kFileEntries);
+ DictionaryValue* file_entries = update.Get();
+ DCHECK(file_entries);
+ DictionaryValue* file_entry_dict = NULL;
+ file_entries->GetDictionaryWithoutPathExpansion(file_entry.id,
+ &file_entry_dict);
+ DCHECK(file_entry_dict);
+ file_entry_dict->SetInteger(kFileEntrySequenceNumber,
+ file_entry.sequence_number);
+}
+
+void RemoveSavedFileEntry(ExtensionPrefs* prefs,
+ const std::string& extension_id,
+ const std::string& file_entry_id) {
+ ExtensionPrefs::ScopedDictionaryUpdate update(
+ prefs, extension_id, kFileEntries);
+ DictionaryValue* file_entries = update.Get();
+ if (!file_entries)
+ file_entries = update.Create();
+ file_entries->RemoveWithoutPathExpansion(file_entry_id, NULL);
+}
+
+void ClearSavedFileEntries(ExtensionPrefs* prefs,
+ const std::string& extension_id) {
+ prefs->UpdateExtensionPref(extension_id, kFileEntries, NULL);
+}
+
+void GetSavedFileEntries(ExtensionPrefs* prefs,
+ const std::string& extension_id,
+ std::vector<SavedFileEntry>* out) {
+ const DictionaryValue* file_entries = NULL;
+ if (!prefs->ReadPrefAsDictionary(extension_id, kFileEntries, &file_entries))
+ return;
+
+ for (DictionaryValue::Iterator it(*file_entries); !it.IsAtEnd();
+ it.Advance()) {
+ const DictionaryValue* file_entry = NULL;
+ if (!it.value().GetAsDictionary(&file_entry))
+ continue;
+ const base::Value* path_value;
+ if (!file_entry->Get(kFileEntryPath, &path_value))
+ continue;
+ base::FilePath file_path;
+ if (!GetValueAsFilePath(*path_value, &file_path))
+ continue;
+ bool writable = false;
+ if (!file_entry->GetBoolean(kFileEntryWritable, &writable))
+ continue;
+ int sequence_number = 0;
+ if (!file_entry->GetInteger(kFileEntrySequenceNumber, &sequence_number))
+ continue;
+ if (!sequence_number)
+ continue;
+ out->push_back(
+ SavedFileEntry(it.key(), file_path, writable, sequence_number));
+ }
+}
+
+} // namespace
+
+class SavedFilesService::SavedFiles {
+ public:
+ SavedFiles(Profile* profile, const std::string& extension_id);
+ ~SavedFiles();
+
+ void RetainFileEntry(const std::string& id,
+ const base::FilePath& file_path,
+ bool writable);
+ void MoveEntryToFrontOfQueue(const std::string& id);
+ bool IsRetained(const std::string& id) const;
+ bool GetFileEntry(const std::string& id, SavedFileEntry* out) const;
+ std::vector<SavedFileEntry> GetAllFileEntries() const;
+
+ private:
+ void MaybeCompactSequenceNumbers();
+
+ Profile* profile_;
+ const std::string extension_id_;
+
+ // Owns values.
+ base::hash_map<std::string, SavedFileEntry*> file_id_to_file_entry_map_;
+
+ STLValueDeleter<base::hash_map<std::string, SavedFileEntry*> >
+ file_id_to_file_entry_map_deleter_;
+
+ // Values are a subset of values in file_id_to_file_entry_map_.
+ std::map<int, SavedFileEntry*> saved_file_lru_;
+
+ DISALLOW_COPY_AND_ASSIGN(SavedFiles);
+};
+
+// static
+SavedFilesService* SavedFilesService::Get(Profile* profile) {
+ return SavedFilesServiceFactory::GetForProfile(profile);
+}
+
+SavedFilesService::SavedFilesService(Profile* profile)
+ : extension_id_to_saved_files_deleter_(&extension_id_to_saved_files_),
+ profile_(profile) {
+ registrar_.Add(this,
+ chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
+ content::NotificationService::AllSources());
+ registrar_.Add(this,
+ chrome::NOTIFICATION_APP_TERMINATING,
+ content::NotificationService::AllSources());
+}
+
+SavedFilesService::~SavedFilesService() {}
+
+void SavedFilesService::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
+ ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
+ const Extension* extension = host->extension();
+ if (extension)
+ ClearRetainedFiles(extension);
+ break;
+ }
+
+ case chrome::NOTIFICATION_APP_TERMINATING: {
+ // Stop listening to NOTIFICATION_EXTENSION_HOST_DESTROYED in particular
+ // as all extension hosts will be destroyed as a result of shutdown.
+ registrar_.RemoveAll();
+ break;
+ }
+ }
+}
+
+void SavedFilesService::RetainFileEntry(const std::string& extension_id,
+ const std::string& id,
+ const base::FilePath& file_path,
+ bool writable) {
+ GetOrInsert(extension_id)->RetainFileEntry(id, file_path, writable);
+}
+
+void SavedFilesService::MoveEntryToFrontOfQueue(const std::string& extension_id,
+ const std::string& id) {
+ GetOrInsert(extension_id)->MoveEntryToFrontOfQueue(id);
+}
+
+std::vector<SavedFileEntry> SavedFilesService::GetAllFileEntries(
+ const std::string& extension_id) {
+ return GetOrInsert(extension_id)->GetAllFileEntries();
+}
+
+bool SavedFilesService::IsRetained(const std::string& extension_id,
+ const std::string& id) {
+ return GetOrInsert(extension_id)->IsRetained(id);
+}
+
+bool SavedFilesService::GetFileEntry(const std::string& extension_id,
+ const std::string& id,
+ SavedFileEntry* out) {
+ return GetOrInsert(extension_id)->GetFileEntry(id, out);
+}
+
+void SavedFilesService::ClearRetainedFiles(const Extension* extension) {
+ Cleanup(extension->id());
+ if (!extension->GetActivePermissions()->HasAPIPermission(
+ APIPermission::kFileSystemRetainFiles)) {
+ ClearSavedFileEntries(ExtensionPrefs::Get(profile_), extension->id());
+ }
+}
+
+SavedFilesService::SavedFiles* SavedFilesService::GetOrInsert(
+ const std::string& extension_id) {
+ std::map<std::string, SavedFiles*>::iterator it =
+ extension_id_to_saved_files_.find(extension_id);
+ if (it != extension_id_to_saved_files_.end())
+ return it->second;
+
+ SavedFiles* saved_files = new SavedFiles(profile_, extension_id);
+ extension_id_to_saved_files_.insert(
+ std::make_pair(extension_id, saved_files));
+ return saved_files;
+}
+
+void SavedFilesService::Cleanup(const std::string& extension_id) {
+ std::map<std::string, SavedFiles*>::iterator it =
+ extension_id_to_saved_files_.find(extension_id);
+ if (it != extension_id_to_saved_files_.end()) {
+ delete it->second;
+ extension_id_to_saved_files_.erase(it);
+ }
+}
+
+SavedFilesService::SavedFiles::SavedFiles(Profile* profile,
+ const std::string& extension_id)
+ : profile_(profile),
+ extension_id_(extension_id),
+ file_id_to_file_entry_map_deleter_(&file_id_to_file_entry_map_) {
+ std::vector<SavedFileEntry> saved_entries;
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
+ GetSavedFileEntries(prefs, extension_id_, &saved_entries);
+ for (std::vector<SavedFileEntry>::iterator it = saved_entries.begin();
+ it != saved_entries.end(); ++it) {
+ SavedFileEntry* file_entry = new SavedFileEntry(*it);
+ file_id_to_file_entry_map_.insert(
+ std::make_pair(file_entry->id, file_entry));
+ saved_file_lru_.insert(
+ std::make_pair(file_entry->sequence_number, file_entry));
+ }
+}
+
+SavedFilesService::SavedFiles::~SavedFiles() {}
+
+void SavedFilesService::SavedFiles::RetainFileEntry(
+ const std::string& id,
+ const base::FilePath& file_path,
+ bool writable) {
+ if (ContainsKey(file_id_to_file_entry_map_, id))
+ return;
+
+ file_id_to_file_entry_map_.insert(
+ std::make_pair(id, new SavedFileEntry(id, file_path, writable, 0)));
+}
+
+void SavedFilesService::SavedFiles::MoveEntryToFrontOfQueue(
+ const std::string& id) {
+ base::hash_map<std::string, SavedFileEntry*>::iterator it =
+ file_id_to_file_entry_map_.find(id);
+ if (it == file_id_to_file_entry_map_.end())
+ return;
+
+ SavedFileEntry* file_entry = it->second;
+ int old_sequence_number = file_entry->sequence_number;
+ if (!saved_file_lru_.empty()) {
+ std::map<int, SavedFileEntry*>::reverse_iterator it =
+ saved_file_lru_.rbegin();
+ if (it->second == file_entry)
+ return;
+
+ file_entry->sequence_number = it->first + 1;
+ } else {
+ file_entry->sequence_number = 1;
+ }
+ saved_file_lru_.insert(
+ std::make_pair(file_entry->sequence_number, file_entry));
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
+ if (old_sequence_number) {
+ saved_file_lru_.erase(old_sequence_number);
+ UpdateSavedFileEntry(prefs, extension_id_, *file_entry);
+ } else {
+ AddSavedFileEntry(prefs, extension_id_, *file_entry);
+ if (saved_file_lru_.size() > g_max_saved_file_entries) {
+ std::map<int, SavedFileEntry*>::iterator it = saved_file_lru_.begin();
+ it->second->sequence_number = 0;
+ RemoveSavedFileEntry(prefs, extension_id_, it->second->id);
+ saved_file_lru_.erase(it);
+ }
+ }
+ MaybeCompactSequenceNumbers();
+}
+
+bool SavedFilesService::SavedFiles::IsRetained(const std::string& id) const {
+ return ContainsKey(file_id_to_file_entry_map_, id);
+}
+
+bool SavedFilesService::SavedFiles::GetFileEntry(const std::string& id,
+ SavedFileEntry* out) const {
+ base::hash_map<std::string, SavedFileEntry*>::const_iterator it =
+ file_id_to_file_entry_map_.find(id);
+ if (it == file_id_to_file_entry_map_.end())
+ return false;
+
+ *out = *it->second;
+ return true;
+}
+
+std::vector<SavedFileEntry>
+SavedFilesService::SavedFiles::GetAllFileEntries() const {
+ std::vector<SavedFileEntry> result;
+ for (base::hash_map<std::string, SavedFileEntry*>::const_iterator it =
+ file_id_to_file_entry_map_.begin();
+ it != file_id_to_file_entry_map_.end(); ++it) {
+ result.push_back(*it->second);
+ }
+ return result;
+}
+
+void SavedFilesService::SavedFiles::MaybeCompactSequenceNumbers() {
+ std::map<int, SavedFileEntry*>::reverse_iterator it =
+ saved_file_lru_.rbegin();
+ if (it == saved_file_lru_.rend())
+ return;
+
+ if (it->first < g_max_sequence_number)
+ return;
+
+ int sequence_number = 0;
+ ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
+ for (std::map<int, SavedFileEntry*>::iterator it = saved_file_lru_.begin();
+ it != saved_file_lru_.end(); ++it) {
+ sequence_number++;
+ if (it->second->sequence_number == sequence_number)
+ continue;
+
+ SavedFileEntry* file_entry = it->second;
+ file_entry->sequence_number = sequence_number;
+ UpdateSavedFileEntry(prefs, extension_id_, *file_entry);
+ if (it == saved_file_lru_.begin()) {
+ saved_file_lru_.erase(it);
+ it = saved_file_lru_.insert(std::make_pair(file_entry->sequence_number,
+ file_entry)).first;
+ } else {
+ saved_file_lru_.erase(it--);
+ it = saved_file_lru_.insert(
+ it, std::make_pair(file_entry->sequence_number, file_entry));
+ }
+ }
+}
+
+// static
+void SavedFilesService::SetMaxSequenceNumberForTest(int max_value) {
+ g_max_sequence_number = max_value;
+}
+
+// static
+void SavedFilesService::ClearMaxSequenceNumberForTest() {
+ g_max_sequence_number = kMaxSequenceNumber;
+}
+
+// static
+void SavedFilesService::SetLruSizeForTest(int size) {
+ g_max_saved_file_entries = size;
+}
+
+// static
+void SavedFilesService::ClearLruSizeForTest() {
+ g_max_saved_file_entries = kMaxSavedFileEntries;
+}
+
+} // namespace apps

Powered by Google App Engine
This is Rietveld 408576698