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

Side by Side Diff: chrome/browser/extensions/api/declarative/rules_registry_with_cache.cc

Issue 49693003: Refactor RulesRegistryWithCache to RulesRegistry (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed nits Created 7 years, 1 month 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 (c) 2012 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 "chrome/browser/extensions/api/declarative/rules_registry_with_cache.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/time/time.h"
13 #include "base/values.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/extensions/extension_info_map.h"
16 #include "chrome/browser/extensions/extension_prefs.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_system.h"
19 #include "chrome/browser/extensions/extension_util.h"
20 #include "chrome/browser/extensions/state_store.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/extensions/extension.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_source.h"
26
27 namespace {
28
29 const char kSuccess[] = "";
30 const char kDuplicateRuleId[] = "Duplicate rule ID: %s";
31
32 scoped_ptr<base::Value> RulesToValue(
33 const std::vector<linked_ptr<extensions::RulesRegistry::Rule> >& rules) {
34 scoped_ptr<base::ListValue> list(new base::ListValue());
35 for (size_t i = 0; i < rules.size(); ++i)
36 list->Append(rules[i]->ToValue().release());
37 return list.PassAs<base::Value>();
38 }
39
40 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > RulesFromValue(
41 const base::Value* value) {
42 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > rules;
43
44 const base::ListValue* list = NULL;
45 if (!value || !value->GetAsList(&list))
46 return rules;
47
48 rules.reserve(list->GetSize());
49 for (size_t i = 0; i < list->GetSize(); ++i) {
50 const base::DictionaryValue* dict = NULL;
51 if (!list->GetDictionary(i, &dict))
52 continue;
53 linked_ptr<extensions::RulesRegistry::Rule> rule(
54 new extensions::RulesRegistry::Rule());
55 if (extensions::RulesRegistry::Rule::Populate(*dict, rule.get()))
56 rules.push_back(rule);
57 }
58
59 return rules;
60 }
61
62 // Returns the key to use for storing declarative rules in the state store.
63 std::string GetDeclarativeRuleStorageKey(const std::string& event_name,
64 bool incognito) {
65 if (incognito)
66 return "declarative_rules.incognito." + event_name;
67 else
68 return "declarative_rules." + event_name;
69 }
70
71 } // namespace
72
73
74 namespace extensions {
75
76 // RulesRegistryWithCache
77
78 RulesRegistryWithCache::RulesRegistryWithCache(
79 Profile* profile,
80 const std::string& event_name,
81 content::BrowserThread::ID owner_thread,
82 bool log_storage_init_delay,
83 scoped_ptr<RuleStorageOnUI>* ui_part)
84 : RulesRegistry(owner_thread, event_name),
85 weak_ptr_factory_(profile ? this : NULL),
86 storage_on_ui_(
87 (profile ? (new RuleStorageOnUI(profile,
88 event_name,
89 owner_thread,
90 weak_ptr_factory_.GetWeakPtr(),
91 log_storage_init_delay))->GetWeakPtr()
92 : base::WeakPtr<RuleStorageOnUI>())),
93 process_changed_rules_requested_(profile ? NOT_SCHEDULED_FOR_PROCESSING
94 : NEVER_PROCESS) {
95 if (!profile) {
96 CHECK(!ui_part);
97 return;
98 }
99
100 ui_part->reset(storage_on_ui_.get());
101
102 storage_on_ui_->Init();
103 }
104
105 std::string RulesRegistryWithCache::AddRules(
106 const std::string& extension_id,
107 const std::vector<linked_ptr<Rule> >& rules) {
108 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
109
110 // Verify that all rule IDs are new.
111 for (std::vector<linked_ptr<Rule> >::const_iterator i =
112 rules.begin(); i != rules.end(); ++i) {
113 const RuleId& rule_id = *((*i)->id);
114 RulesDictionaryKey key(extension_id, rule_id);
115 if (rules_.find(key) != rules_.end())
116 return base::StringPrintf(kDuplicateRuleId, rule_id.c_str());
117 }
118
119 std::string error = AddRulesImpl(extension_id, rules);
120
121 if (!error.empty())
122 return error;
123
124 // Commit all rules into |rules_| on success.
125 for (std::vector<linked_ptr<Rule> >::const_iterator i =
126 rules.begin(); i != rules.end(); ++i) {
127 const RuleId& rule_id = *((*i)->id);
128 RulesDictionaryKey key(extension_id, rule_id);
129 rules_[key] = *i;
130 }
131
132 MaybeProcessChangedRules(extension_id);
133 return kSuccess;
134 }
135
136 std::string RulesRegistryWithCache::RemoveRules(
137 const std::string& extension_id,
138 const std::vector<std::string>& rule_identifiers) {
139 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
140
141 std::string error = RemoveRulesImpl(extension_id, rule_identifiers);
142
143 if (!error.empty())
144 return error;
145
146 // Commit removal of rules from |rules_| on success.
147 for (std::vector<std::string>::const_iterator i =
148 rule_identifiers.begin(); i != rule_identifiers.end(); ++i) {
149 RulesDictionaryKey lookup_key(extension_id, *i);
150 rules_.erase(lookup_key);
151 }
152
153 MaybeProcessChangedRules(extension_id);
154 return kSuccess;
155 }
156
157 std::string RulesRegistryWithCache::RemoveAllRules(
158 const std::string& extension_id) {
159 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
160
161 std::string error = RemoveAllRulesImpl(extension_id);
162
163 if (!error.empty())
164 return error;
165
166 // Commit removal of rules from |rules_| on success.
167 for (RulesDictionary::const_iterator i = rules_.begin();
168 i != rules_.end();) {
169 const RulesDictionaryKey& key = i->first;
170 ++i;
171 if (key.first == extension_id)
172 rules_.erase(key);
173 }
174
175 MaybeProcessChangedRules(extension_id);
176 return kSuccess;
177 }
178
179 std::string RulesRegistryWithCache::GetRules(
180 const std::string& extension_id,
181 const std::vector<std::string>& rule_identifiers,
182 std::vector<linked_ptr<RulesRegistry::Rule> >* out) {
183 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
184
185 for (std::vector<std::string>::const_iterator i = rule_identifiers.begin();
186 i != rule_identifiers.end(); ++i) {
187 RulesDictionaryKey lookup_key(extension_id, *i);
188 RulesDictionary::iterator entry = rules_.find(lookup_key);
189 if (entry != rules_.end())
190 out->push_back(entry->second);
191 }
192 return kSuccess;
193 }
194
195 std::string RulesRegistryWithCache::GetAllRules(
196 const std::string& extension_id,
197 std::vector<linked_ptr<RulesRegistry::Rule> >* out) {
198 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
199
200 for (RulesDictionary::const_iterator i = rules_.begin();
201 i != rules_.end(); ++i) {
202 const RulesDictionaryKey& key = i->first;
203 if (key.first == extension_id)
204 out->push_back(i->second);
205 }
206 return kSuccess;
207 }
208
209 void RulesRegistryWithCache::OnExtensionUnloaded(
210 const std::string& extension_id) {
211 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
212 std::string error = RemoveAllRules(extension_id);
213 if (!error.empty())
214 LOG(ERROR) << error;
215 }
216
217 RulesRegistryWithCache::~RulesRegistryWithCache() {
218 }
219
220 void RulesRegistryWithCache::MarkReady(base::Time storage_init_time) {
221 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
222
223 if (!storage_init_time.is_null()) {
224 UMA_HISTOGRAM_TIMES("Extensions.DeclarativeRulesStorageInitialization",
225 base::Time::Now() - storage_init_time);
226 }
227
228 ready_.Signal();
229 }
230
231 void RulesRegistryWithCache::DeserializeAndAddRules(
232 const std::string& extension_id,
233 scoped_ptr<base::Value> rules) {
234 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
235
236 AddRules(extension_id, RulesFromValue(rules.get()));
237 }
238
239 void RulesRegistryWithCache::ProcessChangedRules(
240 const std::string& extension_id) {
241 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread()));
242
243 process_changed_rules_requested_ = NOT_SCHEDULED_FOR_PROCESSING;
244
245 std::vector<linked_ptr<RulesRegistry::Rule> > new_rules;
246 std::string error = GetAllRules(extension_id, &new_rules);
247 DCHECK_EQ(std::string(), error);
248 content::BrowserThread::PostTask(
249 content::BrowserThread::UI,
250 FROM_HERE,
251 base::Bind(&RuleStorageOnUI::WriteToStorage,
252 storage_on_ui_,
253 extension_id,
254 base::Passed(RulesToValue(new_rules))));
255 }
256
257 void RulesRegistryWithCache::MaybeProcessChangedRules(
258 const std::string& extension_id) {
259 if (process_changed_rules_requested_ != NOT_SCHEDULED_FOR_PROCESSING)
260 return;
261
262 process_changed_rules_requested_ = SCHEDULED_FOR_PROCESSING;
263 ready_.Post(FROM_HERE,
264 base::Bind(&RulesRegistryWithCache::ProcessChangedRules,
265 weak_ptr_factory_.GetWeakPtr(),
266 extension_id));
267 }
268
269 // RulesRegistryWithCache::RuleStorageOnUI
270
271 const char RulesRegistryWithCache::RuleStorageOnUI::kRulesStoredKey[] =
272 "has_declarative_rules";
273
274 RulesRegistryWithCache::RuleStorageOnUI::RuleStorageOnUI(
275 Profile* profile,
276 const std::string& event_name,
277 content::BrowserThread::ID rules_registry_thread,
278 base::WeakPtr<RulesRegistryWithCache> registry,
279 bool log_storage_init_delay)
280 : profile_(profile),
281 storage_key_(GetDeclarativeRuleStorageKey(event_name,
282 profile->IsOffTheRecord())),
283 rules_stored_key_(GetRulesStoredKey(event_name,
284 profile->IsOffTheRecord())),
285 log_storage_init_delay_(log_storage_init_delay),
286 registry_(registry),
287 rules_registry_thread_(rules_registry_thread),
288 notified_registry_(false),
289 weak_ptr_factory_(this) {}
290
291 RulesRegistryWithCache::RuleStorageOnUI::~RuleStorageOnUI() {}
292
293 // Returns the key to use for storing whether the rules have been stored.
294 // static
295 std::string RulesRegistryWithCache::RuleStorageOnUI::GetRulesStoredKey(
296 const std::string& event_name,
297 bool incognito) {
298 std::string result(kRulesStoredKey);
299 result += incognito ? ".incognito." : ".";
300 return result + event_name;
301 }
302
303 // This is called from the constructor of RulesRegistryWithCache, so it is
304 // important that it both
305 // 1. calls no (in particular virtual) methods of the rules registry, and
306 // 2. does not create scoped_refptr holding the registry. (A short-lived
307 // scoped_refptr might delete the rules registry before it is constructed.)
308 void RulesRegistryWithCache::RuleStorageOnUI::Init() {
309 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
310
311 ExtensionSystem& system = *ExtensionSystem::Get(profile_);
312 extensions::StateStore* store = system.rules_store();
313 if (store)
314 store->RegisterKey(storage_key_);
315
316 registrar_.Add(this,
317 chrome::NOTIFICATION_EXTENSION_LOADED,
318 content::Source<Profile>(profile_->GetOriginalProfile()));
319
320 if (profile_->IsOffTheRecord())
321 log_storage_init_delay_ = false;
322
323 system.ready().Post(
324 FROM_HERE,
325 base::Bind(&RuleStorageOnUI::ReadRulesForInstalledExtensions,
326 GetWeakPtr()));
327 system.ready().Post(FROM_HERE,
328 base::Bind(&RuleStorageOnUI::CheckIfReady, GetWeakPtr()));
329 }
330
331 void RulesRegistryWithCache::RuleStorageOnUI::WriteToStorage(
332 const std::string& extension_id,
333 scoped_ptr<base::Value> value) {
334 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
335 if (!profile_)
336 return;
337
338 const base::ListValue* rules = NULL;
339 CHECK(value->GetAsList(&rules));
340 bool rules_stored_previously = GetDeclarativeRulesStored(extension_id);
341 bool store_rules = !rules->empty();
342 SetDeclarativeRulesStored(extension_id, store_rules);
343 if (!rules_stored_previously && !store_rules)
344 return;
345
346 StateStore* store = ExtensionSystem::Get(profile_)->rules_store();
347 if (store)
348 store->SetExtensionValue(extension_id, storage_key_, value.Pass());
349 }
350
351 void RulesRegistryWithCache::RuleStorageOnUI::Observe(
352 int type,
353 const content::NotificationSource& source,
354 const content::NotificationDetails& details) {
355 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
356 DCHECK(type == chrome::NOTIFICATION_EXTENSION_LOADED);
357
358 const extensions::Extension* extension =
359 content::Details<const extensions::Extension>(details).ptr();
360 // TODO(mpcomplete): This API check should generalize to any use of
361 // declarative rules, not just webRequest.
362 if (extension->HasAPIPermission(APIPermission::kDeclarativeContent) ||
363 extension->HasAPIPermission(APIPermission::kDeclarativeWebRequest)) {
364 ExtensionInfoMap* extension_info_map =
365 ExtensionSystem::Get(profile_)->info_map();
366 if (profile_->IsOffTheRecord() &&
367 !extension_info_map->IsIncognitoEnabled(extension->id())) {
368 // Ignore this extension.
369 } else {
370 ReadFromStorage(extension->id());
371 }
372 }
373 }
374
375 void RulesRegistryWithCache::RuleStorageOnUI::CheckIfReady() {
376 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
377 if (notified_registry_ || !waiting_for_extensions_.empty())
378 return;
379
380 content::BrowserThread::PostTask(
381 rules_registry_thread_,
382 FROM_HERE,
383 base::Bind(
384 &RulesRegistryWithCache::MarkReady, registry_, storage_init_time_));
385 notified_registry_ = true;
386 }
387
388 void
389 RulesRegistryWithCache::RuleStorageOnUI::ReadRulesForInstalledExtensions() {
390 ExtensionSystem& system = *ExtensionSystem::Get(profile_);
391 ExtensionService* extension_service = system.extension_service();
392 DCHECK(extension_service);
393 // In an OTR profile, we start on top of a normal profile already, so the
394 // extension service should be ready.
395 DCHECK(!profile_->IsOffTheRecord() || extension_service->is_ready());
396 if (extension_service->is_ready()) {
397 const ExtensionSet* extensions = extension_service->extensions();
398 for (ExtensionSet::const_iterator i = extensions->begin();
399 i != extensions->end();
400 ++i) {
401 bool needs_apis_storing_rules =
402 (*i)->HasAPIPermission(APIPermission::kDeclarativeContent) ||
403 (*i)->HasAPIPermission(APIPermission::kDeclarativeWebRequest);
404 bool respects_off_the_record =
405 !(profile_->IsOffTheRecord()) ||
406 extension_util::IsIncognitoEnabled((*i)->id(), extension_service);
407 if (needs_apis_storing_rules && respects_off_the_record)
408 ReadFromStorage((*i)->id());
409 }
410 }
411 }
412
413 void RulesRegistryWithCache::RuleStorageOnUI::ReadFromStorage(
414 const std::string& extension_id) {
415 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
416 if (!profile_)
417 return;
418
419 if (log_storage_init_delay_ && storage_init_time_.is_null())
420 storage_init_time_ = base::Time::Now();
421
422 if (!GetDeclarativeRulesStored(extension_id)) {
423 ExtensionSystem::Get(profile_)->ready().Post(
424 FROM_HERE, base::Bind(&RuleStorageOnUI::CheckIfReady, GetWeakPtr()));
425 return;
426 }
427
428 extensions::StateStore* store = ExtensionSystem::Get(profile_)->rules_store();
429 if (!store)
430 return;
431 waiting_for_extensions_.insert(extension_id);
432 store->GetExtensionValue(extension_id,
433 storage_key_,
434 base::Bind(&RuleStorageOnUI::ReadFromStorageCallback,
435 weak_ptr_factory_.GetWeakPtr(),
436 extension_id));
437 }
438
439 void RulesRegistryWithCache::RuleStorageOnUI::ReadFromStorageCallback(
440 const std::string& extension_id,
441 scoped_ptr<base::Value> value) {
442 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
443 content::BrowserThread::PostTask(
444 rules_registry_thread_,
445 FROM_HERE,
446 base::Bind(&RulesRegistryWithCache::DeserializeAndAddRules,
447 registry_,
448 extension_id,
449 base::Passed(&value)));
450
451 waiting_for_extensions_.erase(extension_id);
452
453 if (waiting_for_extensions_.empty())
454 ExtensionSystem::Get(profile_)->ready().Post(
455 FROM_HERE, base::Bind(&RuleStorageOnUI::CheckIfReady, GetWeakPtr()));
456 }
457
458 bool RulesRegistryWithCache::RuleStorageOnUI::GetDeclarativeRulesStored(
459 const std::string& extension_id) const {
460 CHECK(profile_);
461 const ExtensionScopedPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
462
463 bool rules_stored = true;
464 if (extension_prefs->ReadPrefAsBoolean(
465 extension_id, rules_stored_key_, &rules_stored))
466 return rules_stored;
467
468 // Safe default -- if we don't know that the rules are not stored, we force
469 // a read by returning true.
470 return true;
471 }
472
473 void RulesRegistryWithCache::RuleStorageOnUI::SetDeclarativeRulesStored(
474 const std::string& extension_id,
475 bool rules_stored) {
476 CHECK(profile_);
477 ExtensionScopedPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
478 extension_prefs->UpdateExtensionPref(
479 extension_id,
480 rules_stored_key_,
481 new base::FundamentalValue(rules_stored));
482 }
483
484 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698