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.
5 #include "extensions/browser/api/declarative/rules_registry.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/time/time.h"
17 #include "base/values.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/notification_details.h"
20 #include "content/public/browser/notification_source.h"
21 #include "extensions/browser/api/declarative/rules_cache_delegate.h"
22 #include "extensions/browser/extension_error.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/extensions_browser_client.h"
26 #include "extensions/browser/state_store.h"
27 #include "extensions/common/api/declarative/declarative_manifest_data.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/extensions_client.h"
30 #include "extensions/common/manifest_constants.h"
32 namespace extensions
{
36 const char kSuccess
[] = "";
37 const char kDuplicateRuleId
[] = "Duplicate rule ID: %s";
38 const char kErrorCannotRemoveManifestRules
[] =
39 "Rules declared in the 'event_rules' manifest field cannot be removed";
41 scoped_ptr
<base::Value
> RulesToValue(
42 const std::vector
<linked_ptr
<api::events::Rule
>>& rules
) {
43 scoped_ptr
<base::ListValue
> list(new base::ListValue());
44 for (size_t i
= 0; i
< rules
.size(); ++i
)
45 list
->Append(rules
[i
]->ToValue().release());
49 std::vector
<linked_ptr
<api::events::Rule
>> RulesFromValue(
50 const base::Value
* value
) {
51 std::vector
<linked_ptr
<api::events::Rule
>> rules
;
53 const base::ListValue
* list
= NULL
;
54 if (!value
|| !value
->GetAsList(&list
))
57 rules
.reserve(list
->GetSize());
58 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
59 const base::DictionaryValue
* dict
= NULL
;
60 if (!list
->GetDictionary(i
, &dict
))
62 linked_ptr
<api::events::Rule
> rule(new api::events::Rule());
63 if (api::events::Rule::Populate(*dict
, rule
.get()))
64 rules
.push_back(rule
);
70 std::string
ToId(int identifier
) {
71 return base::StringPrintf("_%d_", identifier
);
79 RulesRegistry::RulesRegistry(content::BrowserContext
* browser_context
,
80 const std::string
& event_name
,
81 content::BrowserThread::ID owner_thread
,
82 RulesCacheDelegate
* cache_delegate
,
84 : browser_context_(browser_context
),
85 owner_thread_(owner_thread
),
86 event_name_(event_name
),
88 ready_(/*signaled=*/!cache_delegate
), // Immediately ready if no cache
89 // delegate to wait for.
90 last_generated_rule_identifier_id_(0),
91 weak_ptr_factory_(browser_context_
? this : NULL
) {
93 cache_delegate_
= cache_delegate
->GetWeakPtr();
94 cache_delegate
->Init(this);
98 std::string
RulesRegistry::AddRulesNoFill(
99 const std::string
& extension_id
,
100 const std::vector
<linked_ptr
<api::events::Rule
>>& rules
,
101 RulesDictionary
* out
) {
102 DCHECK_CURRENTLY_ON(owner_thread());
104 // Verify that all rule IDs are new.
105 for (std::vector
<linked_ptr
<api::events::Rule
>>::const_iterator i
=
107 i
!= rules
.end(); ++i
) {
108 const RuleId
& rule_id
= *((*i
)->id
);
109 // Every rule should have a priority assigned.
110 DCHECK((*i
)->priority
);
111 RulesDictionaryKey
key(extension_id
, rule_id
);
112 if (rules_
.find(key
) != rules_
.end() ||
113 manifest_rules_
.find(key
) != manifest_rules_
.end())
114 return base::StringPrintf(kDuplicateRuleId
, rule_id
.c_str());
117 std::string error
= AddRulesImpl(extension_id
, rules
);
122 // Commit all rules into |rules_| on success.
123 for (std::vector
<linked_ptr
<api::events::Rule
>>::const_iterator i
=
125 i
!= rules
.end(); ++i
) {
126 const RuleId
& rule_id
= *((*i
)->id
);
127 RulesDictionaryKey
key(extension_id
, rule_id
);
131 MaybeProcessChangedRules(extension_id
);
135 std::string
RulesRegistry::AddRules(
136 const std::string
& extension_id
,
137 const std::vector
<linked_ptr
<api::events::Rule
>>& rules
) {
138 return AddRulesInternal(extension_id
, rules
, &rules_
);
141 std::string
RulesRegistry::AddRulesInternal(
142 const std::string
& extension_id
,
143 const std::vector
<linked_ptr
<api::events::Rule
>>& rules
,
144 RulesDictionary
* out
) {
145 DCHECK_CURRENTLY_ON(owner_thread());
147 std::string error
= CheckAndFillInOptionalRules(extension_id
, rules
);
150 FillInOptionalPriorities(rules
);
152 return AddRulesNoFill(extension_id
, rules
, out
);
155 std::string
RulesRegistry::RemoveRules(
156 const std::string
& extension_id
,
157 const std::vector
<std::string
>& rule_identifiers
) {
158 DCHECK_CURRENTLY_ON(owner_thread());
160 // Check if any of the rules are non-removable.
161 for (RuleId rule_id
: rule_identifiers
) {
162 RulesDictionaryKey
lookup_key(extension_id
, rule_id
);
163 RulesDictionary::iterator itr
= manifest_rules_
.find(lookup_key
);
164 if (itr
!= manifest_rules_
.end())
165 return kErrorCannotRemoveManifestRules
;
168 std::string error
= RemoveRulesImpl(extension_id
, rule_identifiers
);
173 for (std::vector
<std::string
>::const_iterator i
= rule_identifiers
.begin();
174 i
!= rule_identifiers
.end();
176 RulesDictionaryKey
lookup_key(extension_id
, *i
);
177 rules_
.erase(lookup_key
);
180 MaybeProcessChangedRules(extension_id
);
181 RemoveUsedRuleIdentifiers(extension_id
, rule_identifiers
);
185 std::string
RulesRegistry::RemoveAllRules(const std::string
& extension_id
) {
187 RulesRegistry::RemoveAllRulesNoStoreUpdate(extension_id
, false);
188 MaybeProcessChangedRules(extension_id
); // Now update the prefs and store.
192 std::string
RulesRegistry::RemoveAllRulesNoStoreUpdate(
193 const std::string
& extension_id
,
194 bool remove_manifest_rules
) {
195 DCHECK_CURRENTLY_ON(owner_thread());
197 std::string error
= RemoveAllRulesImpl(extension_id
);
202 auto remove_rules
= [&extension_id
](RulesDictionary
& dictionary
) {
203 for (auto it
= dictionary
.begin(); it
!= dictionary
.end();) {
204 if (it
->first
.first
== extension_id
)
205 dictionary
.erase(it
++);
210 remove_rules(rules_
);
211 if (remove_manifest_rules
)
212 remove_rules(manifest_rules_
);
214 RemoveAllUsedRuleIdentifiers(extension_id
);
218 void RulesRegistry::GetRules(const std::string
& extension_id
,
219 const std::vector
<std::string
>& rule_identifiers
,
220 std::vector
<linked_ptr
<api::events::Rule
>>* out
) {
221 DCHECK_CURRENTLY_ON(owner_thread());
223 for (const auto& i
: rule_identifiers
) {
224 RulesDictionaryKey
lookup_key(extension_id
, i
);
225 RulesDictionary::iterator entry
= rules_
.find(lookup_key
);
226 if (entry
!= rules_
.end())
227 out
->push_back(entry
->second
);
228 entry
= manifest_rules_
.find(lookup_key
);
229 if (entry
!= manifest_rules_
.end())
230 out
->push_back(entry
->second
);
234 void RulesRegistry::GetRules(const std::string
& extension_id
,
235 RulesDictionary
& rules
,
236 std::vector
<linked_ptr
<api::events::Rule
>>* out
) {
237 for (const auto& i
: rules
) {
238 const RulesDictionaryKey
& key
= i
.first
;
239 if (key
.first
== extension_id
)
240 out
->push_back(i
.second
);
244 void RulesRegistry::GetAllRules(
245 const std::string
& extension_id
,
246 std::vector
<linked_ptr
<api::events::Rule
>>* out
) {
247 DCHECK_CURRENTLY_ON(owner_thread());
248 GetRules(extension_id
, manifest_rules_
, out
);
249 GetRules(extension_id
, rules_
, out
);
252 void RulesRegistry::OnExtensionUnloaded(const Extension
* extension
) {
253 DCHECK_CURRENTLY_ON(owner_thread());
254 std::string error
= RemoveAllRulesImpl(extension
->id());
256 ReportInternalError(extension
->id(), error
);
259 void RulesRegistry::OnExtensionUninstalled(const Extension
* extension
) {
260 DCHECK_CURRENTLY_ON(owner_thread());
261 std::string error
= RemoveAllRulesNoStoreUpdate(extension
->id(), true);
263 ReportInternalError(extension
->id(), error
);
266 void RulesRegistry::OnExtensionLoaded(const Extension
* extension
) {
267 DCHECK_CURRENTLY_ON(owner_thread());
268 std::vector
<linked_ptr
<api::events::Rule
>> rules
;
269 GetAllRules(extension
->id(), &rules
);
270 DeclarativeManifestData
* declarative_data
=
271 DeclarativeManifestData::Get(extension
);
272 if (declarative_data
) {
273 std::vector
<linked_ptr
<api::events::Rule
>>& manifest_rules
=
274 declarative_data
->RulesForEvent(event_name_
);
275 if (manifest_rules
.size()) {
277 AddRulesInternal(extension
->id(), manifest_rules
, &manifest_rules_
);
279 ReportInternalError(extension
->id(), error
);
282 std::string error
= AddRulesImpl(extension
->id(), rules
);
284 ReportInternalError(extension
->id(), error
);
287 size_t RulesRegistry::GetNumberOfUsedRuleIdentifiersForTesting() const {
288 size_t entry_count
= 0u;
289 for (RuleIdentifiersMap::const_iterator extension
=
290 used_rule_identifiers_
.begin();
291 extension
!= used_rule_identifiers_
.end();
293 // Each extension is counted as 1 just for being there. Otherwise we miss
294 // keys with empty values.
295 entry_count
+= 1u + extension
->second
.size();
300 void RulesRegistry::DeserializeAndAddRules(
301 const std::string
& extension_id
,
302 scoped_ptr
<base::Value
> rules
) {
303 DCHECK_CURRENTLY_ON(owner_thread());
306 AddRulesNoFill(extension_id
, RulesFromValue(rules
.get()), &rules_
);
308 ReportInternalError(extension_id
, error
);
311 void RulesRegistry::ReportInternalError(const std::string
& extension_id
,
312 const std::string
& error
) {
313 scoped_ptr
<ExtensionError
> error_instance(new InternalError(
314 extension_id
, base::ASCIIToUTF16(error
), logging::LOG_ERROR
));
315 ExtensionsBrowserClient::Get()->ReportError(browser_context_
,
316 error_instance
.Pass());
319 RulesRegistry::~RulesRegistry() {
322 void RulesRegistry::MarkReady(base::Time storage_init_time
) {
323 DCHECK_CURRENTLY_ON(owner_thread());
325 if (!storage_init_time
.is_null()) {
326 UMA_HISTOGRAM_TIMES("Extensions.DeclarativeRulesStorageInitialization",
327 base::Time::Now() - storage_init_time
);
333 void RulesRegistry::ProcessChangedRules(const std::string
& extension_id
) {
334 DCHECK_CURRENTLY_ON(owner_thread());
336 DCHECK(ContainsKey(process_changed_rules_requested_
, extension_id
));
337 process_changed_rules_requested_
[extension_id
] = NOT_SCHEDULED_FOR_PROCESSING
;
339 std::vector
<linked_ptr
<api::events::Rule
>> new_rules
;
340 GetRules(extension_id
, rules_
, &new_rules
);
341 content::BrowserThread::PostTask(
342 content::BrowserThread::UI
, FROM_HERE
,
343 base::Bind(&RulesCacheDelegate::WriteToStorage
, cache_delegate_
,
344 extension_id
, base::Passed(RulesToValue(new_rules
))));
347 void RulesRegistry::MaybeProcessChangedRules(const std::string
& extension_id
) {
348 // Read and initialize |process_changed_rules_requested_[extension_id]| if
349 // necessary. (Note that the insertion below will not overwrite
350 // |process_changed_rules_requested_[extension_id]| if that already exists.
351 std::pair
<ProcessStateMap::iterator
, bool> insertion
=
352 process_changed_rules_requested_
.insert(std::make_pair(
354 browser_context_
? NOT_SCHEDULED_FOR_PROCESSING
: NEVER_PROCESS
));
355 if (insertion
.first
->second
!= NOT_SCHEDULED_FOR_PROCESSING
)
358 process_changed_rules_requested_
[extension_id
] = SCHEDULED_FOR_PROCESSING
;
359 ready_
.Post(FROM_HERE
,
360 base::Bind(&RulesRegistry::ProcessChangedRules
,
361 weak_ptr_factory_
.GetWeakPtr(),
365 bool RulesRegistry::IsUniqueId(const std::string
& extension_id
,
366 const std::string
& rule_id
) const {
367 RuleIdentifiersMap::const_iterator identifiers
=
368 used_rule_identifiers_
.find(extension_id
);
369 if (identifiers
== used_rule_identifiers_
.end())
371 return identifiers
->second
.find(rule_id
) == identifiers
->second
.end();
374 std::string
RulesRegistry::GenerateUniqueId(const std::string
& extension_id
) {
375 while (!IsUniqueId(extension_id
, ToId(last_generated_rule_identifier_id_
)))
376 ++last_generated_rule_identifier_id_
;
377 return ToId(last_generated_rule_identifier_id_
);
380 std::string
RulesRegistry::CheckAndFillInOptionalRules(
381 const std::string
& extension_id
,
382 const std::vector
<linked_ptr
<api::events::Rule
>>& rules
) {
383 // IDs we have inserted, in case we need to rollback this operation.
384 std::vector
<std::string
> rollback_log
;
386 // First we insert all rules with existing identifier, so that generated
387 // identifiers cannot collide with identifiers passed by the caller.
388 for (std::vector
<linked_ptr
<api::events::Rule
>>::const_iterator i
=
390 i
!= rules
.end(); ++i
) {
391 api::events::Rule
* rule
= i
->get();
392 if (rule
->id
.get()) {
393 std::string id
= *(rule
->id
);
394 if (!IsUniqueId(extension_id
, id
)) {
395 RemoveUsedRuleIdentifiers(extension_id
, rollback_log
);
396 return "Id " + id
+ " was used multiple times.";
398 used_rule_identifiers_
[extension_id
].insert(id
);
401 // Now we generate IDs in case they were not specified in the rules. This
402 // cannot fail so we do not need to keep track of a rollback log.
403 for (std::vector
<linked_ptr
<api::events::Rule
>>::const_iterator i
=
405 i
!= rules
.end(); ++i
) {
406 api::events::Rule
* rule
= i
->get();
407 if (!rule
->id
.get()) {
408 rule
->id
.reset(new std::string(GenerateUniqueId(extension_id
)));
409 used_rule_identifiers_
[extension_id
].insert(*(rule
->id
));
412 return std::string();
415 void RulesRegistry::FillInOptionalPriorities(
416 const std::vector
<linked_ptr
<api::events::Rule
>>& rules
) {
417 std::vector
<linked_ptr
<api::events::Rule
>>::const_iterator i
;
418 for (i
= rules
.begin(); i
!= rules
.end(); ++i
) {
419 if (!(*i
)->priority
.get())
420 (*i
)->priority
.reset(new int(DEFAULT_PRIORITY
));
424 void RulesRegistry::RemoveUsedRuleIdentifiers(
425 const std::string
& extension_id
,
426 const std::vector
<std::string
>& identifiers
) {
427 std::vector
<std::string
>::const_iterator i
;
428 for (i
= identifiers
.begin(); i
!= identifiers
.end(); ++i
)
429 used_rule_identifiers_
[extension_id
].erase(*i
);
432 void RulesRegistry::RemoveAllUsedRuleIdentifiers(
433 const std::string
& extension_id
) {
434 used_rule_identifiers_
.erase(extension_id
);
437 } // namespace extensions