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
<core_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
<core_api::events::Rule
>> RulesFromValue(
50 const base::Value
* value
) {
51 std::vector
<linked_ptr
<core_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
<core_api::events::Rule
> rule(new core_api::events::Rule());
63 if (core_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
<core_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
<core_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
<core_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
<core_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
<core_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(
219 const std::string
& extension_id
,
220 const std::vector
<std::string
>& rule_identifiers
,
221 std::vector
<linked_ptr
<core_api::events::Rule
>>* out
) {
222 DCHECK_CURRENTLY_ON(owner_thread());
224 for (const auto& i
: rule_identifiers
) {
225 RulesDictionaryKey
lookup_key(extension_id
, i
);
226 RulesDictionary::iterator entry
= rules_
.find(lookup_key
);
227 if (entry
!= rules_
.end())
228 out
->push_back(entry
->second
);
229 entry
= manifest_rules_
.find(lookup_key
);
230 if (entry
!= manifest_rules_
.end())
231 out
->push_back(entry
->second
);
235 void RulesRegistry::GetRules(
236 const std::string
& extension_id
,
237 RulesDictionary
& rules
,
238 std::vector
<linked_ptr
<core_api::events::Rule
>>* out
) {
239 for (const auto& i
: rules
) {
240 const RulesDictionaryKey
& key
= i
.first
;
241 if (key
.first
== extension_id
)
242 out
->push_back(i
.second
);
246 void RulesRegistry::GetAllRules(
247 const std::string
& extension_id
,
248 std::vector
<linked_ptr
<core_api::events::Rule
>>* out
) {
249 DCHECK_CURRENTLY_ON(owner_thread());
250 GetRules(extension_id
, manifest_rules_
, out
);
251 GetRules(extension_id
, rules_
, out
);
254 void RulesRegistry::OnExtensionUnloaded(const Extension
* extension
) {
255 DCHECK_CURRENTLY_ON(owner_thread());
256 std::string error
= RemoveAllRulesImpl(extension
->id());
258 ReportInternalError(extension
->id(), error
);
261 void RulesRegistry::OnExtensionUninstalled(const Extension
* extension
) {
262 DCHECK_CURRENTLY_ON(owner_thread());
263 std::string error
= RemoveAllRulesNoStoreUpdate(extension
->id(), true);
265 ReportInternalError(extension
->id(), error
);
268 void RulesRegistry::OnExtensionLoaded(const Extension
* extension
) {
269 DCHECK_CURRENTLY_ON(owner_thread());
270 std::vector
<linked_ptr
<core_api::events::Rule
>> rules
;
271 GetAllRules(extension
->id(), &rules
);
272 DeclarativeManifestData
* declarative_data
=
273 DeclarativeManifestData::Get(extension
);
274 if (declarative_data
) {
275 std::vector
<linked_ptr
<core_api::events::Rule
>>& manifest_rules
=
276 declarative_data
->RulesForEvent(event_name_
);
277 if (manifest_rules
.size()) {
279 AddRulesInternal(extension
->id(), manifest_rules
, &manifest_rules_
);
281 ReportInternalError(extension
->id(), error
);
284 std::string error
= AddRulesImpl(extension
->id(), rules
);
286 ReportInternalError(extension
->id(), error
);
289 size_t RulesRegistry::GetNumberOfUsedRuleIdentifiersForTesting() const {
290 size_t entry_count
= 0u;
291 for (RuleIdentifiersMap::const_iterator extension
=
292 used_rule_identifiers_
.begin();
293 extension
!= used_rule_identifiers_
.end();
295 // Each extension is counted as 1 just for being there. Otherwise we miss
296 // keys with empty values.
297 entry_count
+= 1u + extension
->second
.size();
302 void RulesRegistry::DeserializeAndAddRules(
303 const std::string
& extension_id
,
304 scoped_ptr
<base::Value
> rules
) {
305 DCHECK_CURRENTLY_ON(owner_thread());
308 AddRulesNoFill(extension_id
, RulesFromValue(rules
.get()), &rules_
);
310 ReportInternalError(extension_id
, error
);
313 void RulesRegistry::ReportInternalError(const std::string
& extension_id
,
314 const std::string
& error
) {
315 scoped_ptr
<ExtensionError
> error_instance(new InternalError(
316 extension_id
, base::ASCIIToUTF16(error
), logging::LOG_ERROR
));
317 ExtensionsBrowserClient::Get()->ReportError(browser_context_
,
318 error_instance
.Pass());
321 RulesRegistry::~RulesRegistry() {
324 void RulesRegistry::MarkReady(base::Time storage_init_time
) {
325 DCHECK_CURRENTLY_ON(owner_thread());
327 if (!storage_init_time
.is_null()) {
328 UMA_HISTOGRAM_TIMES("Extensions.DeclarativeRulesStorageInitialization",
329 base::Time::Now() - storage_init_time
);
335 void RulesRegistry::ProcessChangedRules(const std::string
& extension_id
) {
336 DCHECK_CURRENTLY_ON(owner_thread());
338 DCHECK(ContainsKey(process_changed_rules_requested_
, extension_id
));
339 process_changed_rules_requested_
[extension_id
] = NOT_SCHEDULED_FOR_PROCESSING
;
341 std::vector
<linked_ptr
<core_api::events::Rule
>> new_rules
;
342 GetRules(extension_id
, rules_
, &new_rules
);
343 content::BrowserThread::PostTask(
344 content::BrowserThread::UI
, FROM_HERE
,
345 base::Bind(&RulesCacheDelegate::WriteToStorage
, cache_delegate_
,
346 extension_id
, base::Passed(RulesToValue(new_rules
))));
349 void RulesRegistry::MaybeProcessChangedRules(const std::string
& extension_id
) {
350 // Read and initialize |process_changed_rules_requested_[extension_id]| if
351 // necessary. (Note that the insertion below will not overwrite
352 // |process_changed_rules_requested_[extension_id]| if that already exists.
353 std::pair
<ProcessStateMap::iterator
, bool> insertion
=
354 process_changed_rules_requested_
.insert(std::make_pair(
356 browser_context_
? NOT_SCHEDULED_FOR_PROCESSING
: NEVER_PROCESS
));
357 if (insertion
.first
->second
!= NOT_SCHEDULED_FOR_PROCESSING
)
360 process_changed_rules_requested_
[extension_id
] = SCHEDULED_FOR_PROCESSING
;
361 ready_
.Post(FROM_HERE
,
362 base::Bind(&RulesRegistry::ProcessChangedRules
,
363 weak_ptr_factory_
.GetWeakPtr(),
367 bool RulesRegistry::IsUniqueId(const std::string
& extension_id
,
368 const std::string
& rule_id
) const {
369 RuleIdentifiersMap::const_iterator identifiers
=
370 used_rule_identifiers_
.find(extension_id
);
371 if (identifiers
== used_rule_identifiers_
.end())
373 return identifiers
->second
.find(rule_id
) == identifiers
->second
.end();
376 std::string
RulesRegistry::GenerateUniqueId(const std::string
& extension_id
) {
377 while (!IsUniqueId(extension_id
, ToId(last_generated_rule_identifier_id_
)))
378 ++last_generated_rule_identifier_id_
;
379 return ToId(last_generated_rule_identifier_id_
);
382 std::string
RulesRegistry::CheckAndFillInOptionalRules(
383 const std::string
& extension_id
,
384 const std::vector
<linked_ptr
<core_api::events::Rule
>>& rules
) {
385 // IDs we have inserted, in case we need to rollback this operation.
386 std::vector
<std::string
> rollback_log
;
388 // First we insert all rules with existing identifier, so that generated
389 // identifiers cannot collide with identifiers passed by the caller.
390 for (std::vector
<linked_ptr
<core_api::events::Rule
>>::const_iterator i
=
392 i
!= rules
.end(); ++i
) {
393 core_api::events::Rule
* rule
= i
->get();
394 if (rule
->id
.get()) {
395 std::string id
= *(rule
->id
);
396 if (!IsUniqueId(extension_id
, id
)) {
397 RemoveUsedRuleIdentifiers(extension_id
, rollback_log
);
398 return "Id " + id
+ " was used multiple times.";
400 used_rule_identifiers_
[extension_id
].insert(id
);
403 // Now we generate IDs in case they were not specified in the rules. This
404 // cannot fail so we do not need to keep track of a rollback log.
405 for (std::vector
<linked_ptr
<core_api::events::Rule
>>::const_iterator i
=
407 i
!= rules
.end(); ++i
) {
408 core_api::events::Rule
* rule
= i
->get();
409 if (!rule
->id
.get()) {
410 rule
->id
.reset(new std::string(GenerateUniqueId(extension_id
)));
411 used_rule_identifiers_
[extension_id
].insert(*(rule
->id
));
414 return std::string();
417 void RulesRegistry::FillInOptionalPriorities(
418 const std::vector
<linked_ptr
<core_api::events::Rule
>>& rules
) {
419 std::vector
<linked_ptr
<core_api::events::Rule
>>::const_iterator i
;
420 for (i
= rules
.begin(); i
!= rules
.end(); ++i
) {
421 if (!(*i
)->priority
.get())
422 (*i
)->priority
.reset(new int(DEFAULT_PRIORITY
));
426 void RulesRegistry::RemoveUsedRuleIdentifiers(
427 const std::string
& extension_id
,
428 const std::vector
<std::string
>& identifiers
) {
429 std::vector
<std::string
>::const_iterator i
;
430 for (i
= identifiers
.begin(); i
!= identifiers
.end(); ++i
)
431 used_rule_identifiers_
[extension_id
].erase(*i
);
434 void RulesRegistry::RemoveAllUsedRuleIdentifiers(
435 const std::string
& extension_id
) {
436 used_rule_identifiers_
.erase(extension_id
);
439 } // namespace extensions