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

Side by Side Diff: extensions/browser/api/lock_screen_data/item_storage.cc

Issue 2934293003: The chrome.lockScreen.data API implementation (Closed)
Patch Set: remove FilePath*UTF8Unsafe methods Created 3 years, 6 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 2017 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 "extensions/browser/api/lock_screen_data/item_storage.h"
6
7 #include <set>
8 #include <utility>
9
10 #include "base/files/file_util.h"
11 #include "base/guid.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/task_scheduler/post_task.h"
14 #include "base/task_scheduler/task_traits.h"
15 #include "base/values.h"
16 #include "components/prefs/pref_registry_simple.h"
17 #include "components/prefs/pref_service.h"
18 #include "components/prefs/scoped_user_pref_update.h"
19 #include "extensions/browser/api/lock_screen_data/data_item.h"
20 #include "extensions/browser/api/lock_screen_data/operation_result.h"
21 #include "extensions/browser/event_router.h"
22 #include "extensions/browser/extension_registry.h"
23 #include "extensions/browser/extensions_browser_client.h"
24 #include "extensions/common/api/lock_screen_data.h"
25
26 namespace extensions {
27
28 namespace lock_screen_data {
29
30 namespace {
31
32 ItemStorage* g_data_item_storage = nullptr;
33
34 ItemStorage::ItemFactoryCallback* g_test_item_factory_callback = nullptr;
35
36 const char kLockScreenDataPrefKey[] = "lockScreenDataItems";
37
38 void DeleteImpl(const base::FilePath& file_path, bool recursive) {
39 base::DeleteFile(file_path, recursive);
40 }
41
42 void DoNothingOnDelete(OperationResult result) {}
43
44 std::unique_ptr<DataItem> CreateDataItem(const std::string& item_id) {
45 return g_test_item_factory_callback
46 ? g_test_item_factory_callback->Run(item_id)
47 : base::MakeUnique<DataItem>(item_id);
48 }
49
50 } // namespace
51
52 // static
53 ItemStorage* ItemStorage::Get(content::BrowserContext* context) {
54 if (g_data_item_storage && !g_data_item_storage->IsContextAllowed(context))
55 return nullptr;
56 return g_data_item_storage;
rkc 2017/06/22 18:35:47 Nothing guarantees g_data_item_storage is not null
tbarzic 2017/06/26 22:21:42 Yes it's OK - what would happen is that the API me
57 }
58
59 // static
60 void ItemStorage::RegisterLocalState(PrefRegistrySimple* registry) {
61 registry->RegisterDictionaryPref(kLockScreenDataPrefKey);
62 }
63
64 ItemStorage::ItemStorage(content::BrowserContext* context,
65 PrefService* local_state,
66 const std::string& crypto_key,
67 const base::FilePath& storage_root)
68 : context_(context),
69 user_id_(
70 ExtensionsBrowserClient::Get()->GetUserIdHashFromContext(context)),
71 crypto_key_(crypto_key),
72 local_state_(local_state),
73 storage_root_(storage_root.Append(user_id_)),
74 extension_registry_observer_(this) {
75 CHECK(!user_id_.empty());
76 extension_registry_observer_.Add(ExtensionRegistry::Get(context));
77 task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
78 {base::MayBlock(), base::TaskPriority::BACKGROUND,
79 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
80
81 ReloadDataItems();
82
83 DCHECK(!g_data_item_storage);
84 g_data_item_storage = this;
85 }
86
87 ItemStorage::~ItemStorage() {
88 DCHECK_EQ(g_data_item_storage, this);
89 g_data_item_storage = nullptr;
90 }
91
92 // static
93 void ItemStorage::SetItemFactoryForTesting(ItemFactoryCallback* callback) {
94 g_test_item_factory_callback = callback;
95 }
96
97 void ItemStorage::SetSessionLocked(bool session_locked) {
98 SessionLockedState new_state = session_locked
99 ? SessionLockedState::kLocked
100 : SessionLockedState::kNotLocked;
101 if (new_state == session_locked_state_)
102 return;
103
104 bool was_locked = session_locked_state_ == SessionLockedState::kLocked;
105 session_locked_state_ = new_state;
106
107 for (auto& extension_items : data_items_) {
108 if (extension_items.second.empty())
109 continue;
110
111 // If the session state is unlocked, dispatch Item availability events to
112 // apps with available data items.
113 if (session_locked_state_ == SessionLockedState::kNotLocked) {
114 api::lock_screen_data::DataItemsAvailableEvent event_args;
115 event_args.was_locked = was_locked;
116
117 std::unique_ptr<Event> event = base::MakeUnique<Event>(
118 events::LOCK_SCREEN_DATA_ON_DATA_ITEMS_AVAILABLE,
119 api::lock_screen_data::OnDataItemsAvailable::kEventName,
120 api::lock_screen_data::OnDataItemsAvailable::Create(event_args));
121 EventRouter::Get(context_)->DispatchEventToExtension(
122 extension_items.first /* extension ID */, std::move(event));
123 }
124 }
125 }
126
127 const DataItem* ItemStorage::CreateItem(const std::string& extension_id) {
128 if (!ExtensionRegistry::Get(context_)->GetExtensionById(
129 extension_id, ExtensionRegistry::ENABLED)) {
130 return nullptr;
131 }
132
133 const std::string item_id = base::GenerateGUID();
134 std::unique_ptr<DataItem> item = CreateDataItem(item_id);
135 DataItem* item_ptr = item.get();
136
137 {
138 // Create item entry in local state prefs, ensuring that all parent paths
139 // are properly set-up (in case this is the first entry added for a
140 // user/extension).
141 DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
142 if (!update->HasKey(user_id_)) {
143 update->SetDictionary(user_id_,
144 base::MakeUnique<base::DictionaryValue>());
145 }
146
147 base::DictionaryValue* user_dict = nullptr;
148 if (!update->GetDictionary(user_id_, &user_dict))
149 return nullptr;
150
151 if (!user_dict->HasKey(extension_id)) {
152 user_dict->SetDictionary(extension_id,
153 base::MakeUnique<base::DictionaryValue>());
154 }
155
156 base::DictionaryValue* extension_dict = nullptr;
157 if (!user_dict->GetDictionary(extension_id, &extension_dict))
158 return nullptr;
159
160 extension_dict->SetDictionary(item_id, item->ToValue());
161 }
162
163 data_items_[extension_id].emplace(item_id, std::move(item));
164 return item_ptr;
165 }
166
167 std::vector<const DataItem*> ItemStorage::GetAllForExtension(
168 const std::string& extension_id) {
169 std::vector<const DataItem*> items;
170 ExtensionDataItemMap::iterator extension_items =
171 data_items_.find(extension_id);
172 if (extension_items == data_items_.end())
173 return items;
174
175 for (const auto& item : extension_items->second) {
176 if (!item.second)
177 continue;
178 items.push_back(item.second.get());
179 }
180
181 return items;
182 }
183
184 OperationResult ItemStorage::SetItemContent(
185 const std::string& extension_id,
186 const std::string& item_id,
187 const std::vector<char>& data,
188 const ItemStorage::WriteCallback& callback) {
189 DataItem* item = FindItem(extension_id, item_id);
190 if (!item)
191 return OperationResult::kNotFound;
192
193 if (item->backing_file().empty()) {
194 item->set_backing_file(storage_root_.Append(extension_id).Append(item_id));
195
196 DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
197 update->SetDictionary(GetDataItemPrefKey(extension_id, item->id()),
198 item->ToValue());
199 }
200
201 return item->Write(data, crypto_key_, task_runner_, callback);
202 }
203
204 OperationResult ItemStorage::GetItemContent(
205 const std::string& extension_id,
206 const std::string& item_id,
207 const ItemStorage::ReadCallback& callback) {
208 DataItem* item = FindItem(extension_id, item_id);
209 if (!item)
210 return OperationResult::kNotFound;
211
212 if (item->backing_file().empty())
213 return OperationResult::kNoBackingFile;
214
215 return item->Read(crypto_key_, task_runner_, callback);
216 }
217
218 OperationResult ItemStorage::DeleteItem(const std::string& extension_id,
219 const std::string& item_id) {
220 DataItem* item = FindItem(extension_id, item_id);
221 if (!item)
222 return OperationResult::kNotFound;
223
224 {
225 DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
226 update->Remove(GetDataItemPrefKey(extension_id, item_id), nullptr);
227 }
228
229 if (!item->backing_file().empty())
230 item->Delete(task_runner_, base::Bind(&DoNothingOnDelete));
231
232 data_items_[extension_id].erase(item_id);
233 return OperationResult::kSuccess;
234 }
235
236 void ItemStorage::OnExtensionUninstalled(
237 content::BrowserContext* browser_context,
238 const Extension* extension,
239 UninstallReason reason) {
240 ClearDataForExtension(extension->id());
241 }
242
243 std::string ItemStorage::GetDataItemPrefKey(const std::string& extension_id,
244 const std::string& item_id) {
245 return user_id_ + "." + extension_id + "." + item_id;
246 }
247
248 bool ItemStorage::IsContextAllowed(content::BrowserContext* context) {
249 switch (session_locked_state_) {
250 case SessionLockedState::kUnknown:
251 return false;
252 case SessionLockedState::kLocked:
253 return ExtensionsBrowserClient::Get()->IsLockScreenContext(context);
254 case SessionLockedState::kNotLocked:
255 return context_ == context;
256 }
257 NOTREACHED() << "Unknown session locked state";
258 return false;
259 }
260
261 void ItemStorage::ReloadDataItems() {
262 data_items_.clear();
rkc 2017/06/22 18:35:47 Since this is called exactly once and only from th
tbarzic 2017/06/26 22:21:42 Done.
263
264 const base::DictionaryValue* user_data = nullptr;
265 const base::DictionaryValue* items =
266 local_state_->GetDictionary(kLockScreenDataPrefKey);
267 if (!items || !items->GetDictionary(user_id_, &user_data) || !user_data)
268 return;
269
270 // Set of extensions pref entries that should be remove from prefs (for
271 // example, entries for extensions that are not installed anymore).
272 // Note that entries are not removed directly during iteration over the
273 // dictionary to avoid invalidating data under the iterator.
274 std::set<std::string> extensions_to_remove;
275
276 for (base::DictionaryValue::Iterator extension_iter(*user_data);
277 !extension_iter.IsAtEnd(); extension_iter.Advance()) {
278 if (!ExtensionRegistry::Get(context_)->GetInstalledExtension(
279 extension_iter.key())) {
280 extensions_to_remove.insert(extension_iter.key());
281 } else {
282 if (!LoadDataItemsForExtension(extension_iter.key(),
283 extension_iter.value())) {
284 extensions_to_remove.insert(extension_iter.key());
285 }
286 }
287 }
288
289 // Remove stale/invalid entries.
290 for (const auto& extension_id : extensions_to_remove)
291 ClearDataForExtension(extension_id);
292 }
293
294 bool ItemStorage::LoadDataItemsForExtension(const std::string& extension_id,
295 const base::Value& extension_pref) {
296 const base::DictionaryValue* items = nullptr;
297 if (!extension_pref.GetAsDictionary(&items))
298 return false;
299
300 std::set<std::string> items_to_remove;
301 base::FilePath expected_file_parent = storage_root_.Append(extension_id);
302 for (base::DictionaryValue::Iterator item_iter(*items); !item_iter.IsAtEnd();
303 item_iter.Advance()) {
304 std::unique_ptr<DataItem> item = CreateDataItem(item_iter.key());
305
306 // Invalid items (including items pointing to an unexpected location) should
307 // be removed from prefs - schedule removal after the loop to avoid
308 // invalidating the iterator.
309 if (!item->InitFromValue(item_iter.value()) ||
310 (!item->backing_file().empty() &&
311 !expected_file_parent.IsParent(item->backing_file()))) {
312 items_to_remove.insert(item_iter.key());
313 continue;
314 }
315 data_items_[extension_id].emplace(item_iter.key(), std::move(item));
316 }
317
318 DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
319 for (const auto& item_id : items_to_remove) {
320 update->Remove(GetDataItemPrefKey(extension_id, item_id), nullptr);
321 }
322 return true;
323 }
324
325 void ItemStorage::ClearDataForExtension(const std::string& extension_id) {
326 task_runner_->PostTask(
327 FROM_HERE,
328 base::Bind(&DeleteImpl, storage_root_.Append(extension_id), true));
329
330 data_items_.erase(extension_id);
331
332 DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
333 update->Remove(user_id_ + "." + extension_id, nullptr);
334 }
335
336 DataItem* ItemStorage::FindItem(const std::string& extension_id,
337 const std::string& item_id) {
338 ExtensionDataItemMap::iterator extension_items =
339 data_items_.find(extension_id);
340 if (extension_items == data_items_.end())
341 return nullptr;
342
343 DataItemMap::iterator item_it = extension_items->second.find(item_id);
344 if (item_it == extension_items->second.end())
345 return nullptr;
346
347 if (!item_it->second)
348 return nullptr;
349 return item_it->second.get();
350 }
351
352 } // namespace lock_screen_data
353 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698