OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/extension_settings.h" | 5 #include "chrome/browser/extensions/extension_settings.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/compiler_specific.h" |
| 8 #include "base/file_util.h" |
8 #include "base/json/json_reader.h" | 9 #include "base/json/json_reader.h" |
9 #include "base/json/json_writer.h" | 10 #include "base/json/json_writer.h" |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/memory/linked_ptr.h" |
11 #include "base/memory/scoped_ptr.h" | 13 #include "base/memory/scoped_ptr.h" |
12 #include "content/browser/browser_thread.h" | |
13 #include "chrome/browser/extensions/extension_settings_leveldb_storage.h" | 14 #include "chrome/browser/extensions/extension_settings_leveldb_storage.h" |
14 #include "chrome/browser/extensions/extension_settings_noop_storage.h" | 15 #include "chrome/browser/extensions/extension_settings_noop_storage.h" |
15 #include "chrome/browser/extensions/extension_settings_storage_cache.h" | 16 #include "chrome/browser/extensions/extension_settings_storage_cache.h" |
| 17 #include "chrome/browser/extensions/extension_settings_sync_util.h" |
| 18 #include "chrome/common/extensions/extension.h" |
| 19 #include "content/browser/browser_thread.h" |
16 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" | 20 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" |
17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 21 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
18 | 22 |
19 ExtensionSettings::ExtensionSettings(const FilePath& base_path) | 23 ExtensionSettings::ExtensionSettings(const FilePath& base_path) |
20 : base_path_(base_path) {} | 24 : base_path_(base_path), |
| 25 sync_processor_(NULL) { |
| 26 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 27 } |
21 | 28 |
22 ExtensionSettings::~ExtensionSettings() { | 29 ExtensionSettings::~ExtensionSettings() { |
23 std::map<std::string, ExtensionSettingsStorage*>::iterator it; | 30 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
24 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { | |
25 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, it->second); | |
26 } | |
27 } | 31 } |
28 | 32 |
29 ExtensionSettingsStorage* ExtensionSettings::GetStorage( | 33 ExtensionSettingsStorage* ExtensionSettings::GetStorage( |
30 const std::string& extension_id) { | 34 const std::string& extension_id) const { |
31 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 35 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 36 DictionaryValue empty; |
| 37 return GetOrCreateStorageWithSyncData(extension_id, empty); |
| 38 } |
32 | 39 |
33 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | 40 SyncableExtensionSettingsStorage* |
34 storage_objs_.find(extension_id); | 41 ExtensionSettings::GetOrCreateStorageWithSyncData( |
35 if (existing != storage_objs_.end()) { | 42 const std::string& extension_id, const DictionaryValue& sync_data) const { |
36 return existing->second; | 43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 44 SyncableExtensionSettingsStorage* storage = GetOrCreateAndInitStorage( |
| 45 ExtensionSettingsStorage::LEVELDB, |
| 46 true, |
| 47 extension_id, |
| 48 sync_data); |
| 49 if (!storage) { |
| 50 // Fall back to an in-memory storage area (cached NOOP). |
| 51 // It's ok for these to be synced, it just means that on next starting up |
| 52 // extensions will see the "old" settings, then overwritten (and notified) |
| 53 // when the sync changes come through. |
| 54 storage = GetOrCreateAndInitStorage( |
| 55 ExtensionSettingsStorage::NOOP, |
| 56 true, |
| 57 extension_id, |
| 58 sync_data); |
| 59 DCHECK(storage); |
37 } | 60 } |
38 | 61 return storage; |
39 ExtensionSettingsStorage* new_storage = | |
40 CreateStorage(extension_id, ExtensionSettingsStorage::LEVELDB, true); | |
41 if (new_storage == NULL) { | |
42 // Failed to create a leveldb storage for some reason. Use an in memory | |
43 // storage area (no-op wrapped in a cache) instead. | |
44 new_storage = | |
45 CreateStorage(extension_id, ExtensionSettingsStorage::NOOP, true); | |
46 DCHECK(new_storage != NULL); | |
47 } | |
48 | |
49 storage_objs_[extension_id] = new_storage; | |
50 return new_storage; | |
51 } | 62 } |
52 | 63 |
53 ExtensionSettingsStorage* ExtensionSettings::GetStorageForTesting( | 64 ExtensionSettingsStorage* ExtensionSettings::GetStorageForTesting( |
54 ExtensionSettingsStorage::Type type, | 65 ExtensionSettingsStorage::Type type, |
55 bool cached, | 66 bool cached, |
56 const std::string& extension_id) { | 67 const std::string& extension_id) const { |
57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
58 | 69 DictionaryValue empty; |
59 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | 70 ExtensionSettingsStorage* storage = |
60 storage_objs_.find(extension_id); | 71 GetOrCreateAndInitStorage(type, cached, extension_id, empty); |
61 if (existing != storage_objs_.end()) { | 72 DCHECK(storage); |
62 return existing->second; | 73 return storage; |
63 } | |
64 | |
65 ExtensionSettingsStorage* new_storage = | |
66 CreateStorage(extension_id, type, cached); | |
67 DCHECK(new_storage != NULL); | |
68 storage_objs_[extension_id] = new_storage; | |
69 return new_storage; | |
70 } | 74 } |
71 | 75 |
72 ExtensionSettingsStorage* ExtensionSettings::CreateStorage( | 76 SyncableExtensionSettingsStorage* ExtensionSettings::GetOrCreateAndInitStorage( |
| 77 ExtensionSettingsStorage::Type type, |
| 78 bool cached, |
73 const std::string& extension_id, | 79 const std::string& extension_id, |
| 80 const DictionaryValue& initial_sync_data) const { |
| 81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 82 StorageObjMap::iterator existing = storage_objs_.find(extension_id); |
| 83 if (existing != storage_objs_.end()) { |
| 84 return existing->second.get(); |
| 85 } |
| 86 return CreateAndInitStorage(type, cached, extension_id, initial_sync_data); |
| 87 } |
| 88 |
| 89 SyncableExtensionSettingsStorage* ExtensionSettings::CreateAndInitStorage( |
74 ExtensionSettingsStorage::Type type, | 90 ExtensionSettingsStorage::Type type, |
75 bool cached) { | 91 bool cached, |
| 92 const std::string& extension_id, |
| 93 const DictionaryValue& initial_sync_data) const { |
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 95 DCHECK_EQ(0u, storage_objs_.count(extension_id)); |
77 ExtensionSettingsStorage* storage = NULL; | 96 ExtensionSettingsStorage* storage = NULL; |
78 switch (type) { | 97 switch (type) { |
79 case ExtensionSettingsStorage::NOOP: | 98 case ExtensionSettingsStorage::NOOP: |
80 storage = new ExtensionSettingsNoopStorage(); | 99 storage = new ExtensionSettingsNoopStorage(); |
81 break; | 100 break; |
82 case ExtensionSettingsStorage::LEVELDB: | 101 case ExtensionSettingsStorage::LEVELDB: |
83 storage = ExtensionSettingsLeveldbStorage::Create( | 102 storage = ExtensionSettingsLeveldbStorage::Create( |
84 base_path_, extension_id); | 103 base_path_, extension_id); |
85 break; | 104 break; |
86 default: | 105 default: |
87 NOTREACHED(); | 106 NOTREACHED(); |
88 } | 107 } |
89 if (storage != NULL && cached) { | 108 if (!storage) { |
| 109 return NULL; |
| 110 } |
| 111 if (cached) { |
90 storage = new ExtensionSettingsStorageCache(storage); | 112 storage = new ExtensionSettingsStorageCache(storage); |
91 } | 113 } |
92 return storage; | 114 |
| 115 SyncableExtensionSettingsStorage* synced_storage = |
| 116 new SyncableExtensionSettingsStorage(extension_id, storage); |
| 117 if (sync_processor_) { |
| 118 // TODO(kalman): do something if StartSyncing fails. |
| 119 ignore_result( |
| 120 synced_storage->StartSyncing(initial_sync_data, sync_processor_)); |
| 121 } |
| 122 storage_objs_[extension_id] = |
| 123 linked_ptr<SyncableExtensionSettingsStorage>(synced_storage); |
| 124 return synced_storage; |
93 } | 125 } |
| 126 |
| 127 std::set<std::string> ExtensionSettings::GetKnownExtensionIDs() const { |
| 128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 129 std::set<std::string> result; |
| 130 |
| 131 // TODO(kalman): we will need to do something to disambiguate between app |
| 132 // settings and extension settings, since settings for apps should be synced |
| 133 // iff app sync is turned on, ditto for extensions. |
| 134 |
| 135 // Extension IDs live in-memory and/or on disk. The cache will contain all |
| 136 // that are in-memory. |
| 137 for (StorageObjMap::iterator it = storage_objs_.begin(); |
| 138 it != storage_objs_.end(); ++it) { |
| 139 result.insert(it->first); |
| 140 } |
| 141 |
| 142 // Leveldb databases are directories inside base_path_. |
| 143 file_util::FileEnumerator::FindInfo find_info; |
| 144 file_util::FileEnumerator extension_dirs( |
| 145 base_path_, false, file_util::FileEnumerator::DIRECTORIES); |
| 146 while (!extension_dirs.Next().empty()) { |
| 147 extension_dirs.GetFindInfo(&find_info); |
| 148 FilePath extension_dir(file_util::FileEnumerator::GetFilename(find_info)); |
| 149 DCHECK(!extension_dir.IsAbsolute()); |
| 150 // Extension IDs are created as std::strings so they *should* be ASCII. |
| 151 std::string maybe_as_ascii(extension_dir.MaybeAsASCII()); |
| 152 if (!maybe_as_ascii.empty()) { |
| 153 result.insert(maybe_as_ascii); |
| 154 } |
| 155 } |
| 156 |
| 157 return result; |
| 158 } |
| 159 |
| 160 SyncDataList ExtensionSettings::GetAllSyncData( |
| 161 syncable::ModelType type) const { |
| 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 163 DCHECK_EQ(type, syncable::EXTENSION_SETTINGS); |
| 164 |
| 165 // For all extensions, get all their settings. |
| 166 // This has the effect of bringing in the entire state of extension settings |
| 167 // in memory; sad. |
| 168 SyncDataList all_sync_data; |
| 169 std::set<std::string> known_extension_ids(GetKnownExtensionIDs()); |
| 170 |
| 171 for (std::set<std::string>::const_iterator it = known_extension_ids.begin(); |
| 172 it != known_extension_ids.end(); ++it) { |
| 173 ExtensionSettingsStorage::Result maybe_settings = GetStorage(*it)->Get(); |
| 174 if (maybe_settings.HasError()) { |
| 175 LOG(WARNING) << "Failed to get settings for " << *it << ": " << |
| 176 maybe_settings.GetError(); |
| 177 continue; |
| 178 } |
| 179 |
| 180 DictionaryValue* settings = maybe_settings.GetSettings(); |
| 181 for (DictionaryValue::key_iterator key_it = settings->begin_keys(); |
| 182 key_it != settings->end_keys(); ++key_it) { |
| 183 Value *value = NULL; |
| 184 settings->GetWithoutPathExpansion(*key_it, &value); |
| 185 all_sync_data.push_back( |
| 186 extension_settings_sync_util::CreateData(*it, *key_it, *value)); |
| 187 } |
| 188 } |
| 189 |
| 190 return all_sync_data; |
| 191 } |
| 192 |
| 193 SyncError ExtensionSettings::MergeDataAndStartSyncing( |
| 194 syncable::ModelType type, |
| 195 const SyncDataList& initial_sync_data, |
| 196 SyncChangeProcessor* sync_processor) { |
| 197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 198 DCHECK_EQ(type, syncable::EXTENSION_SETTINGS); |
| 199 DCHECK(!sync_processor_); |
| 200 sync_processor_ = sync_processor; |
| 201 |
| 202 // Group the initial sync data by extension id. |
| 203 std::map<std::string, linked_ptr<DictionaryValue> > grouped_sync_data; |
| 204 for (SyncDataList::const_iterator it = initial_sync_data.begin(); |
| 205 it != initial_sync_data.end(); ++it) { |
| 206 ExtensionSettingSyncData data(*it); |
| 207 linked_ptr<DictionaryValue> sync_data = |
| 208 grouped_sync_data[data.extension_id()]; |
| 209 if (!sync_data.get()) { |
| 210 sync_data = linked_ptr<DictionaryValue>(new DictionaryValue()); |
| 211 grouped_sync_data[data.extension_id()] = sync_data; |
| 212 } |
| 213 DCHECK(!sync_data->HasKey(data.key())) << |
| 214 "Duplicate settings for " << data.extension_id() << "/" << data.key(); |
| 215 sync_data->Set(data.key(), data.value().DeepCopy()); |
| 216 } |
| 217 |
| 218 // Start syncing all existing storage areas. Any storage areas created in |
| 219 // the future will start being synced as part of the creation process. |
| 220 for (StorageObjMap::iterator it = storage_objs_.begin(); |
| 221 it != storage_objs_.end(); ++it) { |
| 222 std::map<std::string, linked_ptr<DictionaryValue> >::iterator |
| 223 maybe_sync_data = grouped_sync_data.find(it->first); |
| 224 if (maybe_sync_data != grouped_sync_data.end()) { |
| 225 // TODO(kalman): do something if StartSyncing fails. |
| 226 ignore_result( |
| 227 it->second->StartSyncing(*maybe_sync_data->second, sync_processor)); |
| 228 grouped_sync_data.erase(it->first); |
| 229 } else { |
| 230 DictionaryValue empty; |
| 231 // TODO(kalman): do something if StartSyncing fails. |
| 232 ignore_result(it->second->StartSyncing(empty, sync_processor)); |
| 233 } |
| 234 } |
| 235 |
| 236 // Eagerly create and init the rest of the storage areas that have sync data. |
| 237 // Under normal circumstances (i.e. not first-time sync) this will be all of |
| 238 // them. |
| 239 for (std::map<std::string, linked_ptr<DictionaryValue> >::iterator it = |
| 240 grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { |
| 241 GetOrCreateStorageWithSyncData(it->first, *it->second); |
| 242 } |
| 243 |
| 244 return SyncError(); |
| 245 } |
| 246 |
| 247 SyncError ExtensionSettings::ProcessSyncChanges( |
| 248 const tracked_objects::Location& from_here, |
| 249 const SyncChangeList& sync_changes) { |
| 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 251 DCHECK(sync_processor_); |
| 252 |
| 253 // Group changes by extension, to pass all changes in a single method call. |
| 254 std::map<std::string, ExtensionSettingSyncDataList> grouped_sync_data; |
| 255 for (SyncChangeList::const_iterator it = sync_changes.begin(); |
| 256 it != sync_changes.end(); ++it) { |
| 257 ExtensionSettingSyncData data(*it); |
| 258 grouped_sync_data[data.extension_id()].push_back(data); |
| 259 } |
| 260 |
| 261 DictionaryValue empty; |
| 262 for (std::map<std::string, ExtensionSettingSyncDataList>::iterator |
| 263 it = grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { |
| 264 // TODO(kalman): do something if ProcessSyncChanges fails. |
| 265 ignore_result( |
| 266 GetOrCreateStorageWithSyncData(it->first, empty)-> |
| 267 ProcessSyncChanges(it->second)); |
| 268 } |
| 269 |
| 270 return SyncError(); |
| 271 } |
| 272 |
| 273 void ExtensionSettings::StopSyncing(syncable::ModelType type) { |
| 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 275 DCHECK(sync_processor_); |
| 276 sync_processor_ = NULL; |
| 277 |
| 278 for (StorageObjMap::iterator it = storage_objs_.begin(); |
| 279 it != storage_objs_.end(); ++it) { |
| 280 it->second->StopSyncing(); |
| 281 } |
| 282 } |
OLD | NEW |