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.h" 6 7 #include <utility> 8 9 #include "base/bind.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/time/time.h" 16 #include "base/values.h" 17 #include "chrome/browser/chrome_notification_types.h" 18 #include "chrome/browser/extensions/api/declarative/rules_cache_delegate.h" 19 #include "chrome/browser/extensions/extension_prefs.h" 20 #include "chrome/browser/extensions/extension_service.h" 21 #include "chrome/browser/extensions/extension_system.h" 22 #include "chrome/browser/extensions/extension_util.h" 23 #include "chrome/browser/extensions/state_store.h" 24 #include "chrome/browser/profiles/profile.h" 25 #include "content/public/browser/browser_thread.h" 26 #include "content/public/browser/notification_details.h" 27 #include "content/public/browser/notification_source.h" 28 #include "extensions/common/extension.h" 29 30 namespace { 31 32 const char kSuccess[] = ""; 33 const char kDuplicateRuleId[] = "Duplicate rule ID: %s"; 34 35 scoped_ptr<base::Value> RulesToValue( 36 const std::vector<linked_ptr<extensions::RulesRegistry::Rule> >& rules) { 37 scoped_ptr<base::ListValue> list(new base::ListValue()); 38 for (size_t i = 0; i < rules.size(); ++i) 39 list->Append(rules[i]->ToValue().release()); 40 return list.PassAs<base::Value>(); 41 } 42 43 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > RulesFromValue( 44 const base::Value* value) { 45 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > rules; 46 47 const base::ListValue* list = NULL; 48 if (!value || !value->GetAsList(&list)) 49 return rules; 50 51 rules.reserve(list->GetSize()); 52 for (size_t i = 0; i < list->GetSize(); ++i) { 53 const base::DictionaryValue* dict = NULL; 54 if (!list->GetDictionary(i, &dict)) 55 continue; 56 linked_ptr<extensions::RulesRegistry::Rule> rule( 57 new extensions::RulesRegistry::Rule()); 58 if (extensions::RulesRegistry::Rule::Populate(*dict, rule.get())) 59 rules.push_back(rule); 60 } 61 62 return rules; 63 } 64 65 std::string ToId(int identifier) { 66 return base::StringPrintf("_%d_", identifier); 67 } 68 69 } // namespace 70 71 72 namespace extensions { 73 74 // RulesRegistry 75 76 RulesRegistry::RulesRegistry( 77 Profile* profile, 78 const std::string& event_name, 79 content::BrowserThread::ID owner_thread, 80 RulesCacheDelegate* cache_delegate, 81 const WebViewKey& webview_key) 82 : profile_(profile), 83 owner_thread_(owner_thread), 84 event_name_(event_name), 85 webview_key_(webview_key), 86 weak_ptr_factory_(profile ? this : NULL), 87 last_generated_rule_identifier_id_(0) { 88 if (cache_delegate) { 89 cache_delegate_ = cache_delegate->GetWeakPtr(); 90 cache_delegate->Init(this); 91 } else { 92 content::BrowserThread::PostTask( 93 owner_thread, 94 FROM_HERE, 95 base::Bind(&RulesRegistry::MarkReady, this, base::Time::Now())); 96 } 97 } 98 99 std::string RulesRegistry::AddRulesNoFill( 100 const std::string& extension_id, 101 const std::vector<linked_ptr<Rule> >& rules) { 102 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 103 104 // Verify that all rule IDs are new. 105 for (std::vector<linked_ptr<Rule> >::const_iterator i = 106 rules.begin(); i != rules.end(); ++i) { 107 const RuleId& rule_id = *((*i)->id); 108 // Every rule should have a priority assigned. 109 DCHECK((*i)->priority); 110 RulesDictionaryKey key(extension_id, rule_id); 111 if (rules_.find(key) != rules_.end()) 112 return base::StringPrintf(kDuplicateRuleId, rule_id.c_str()); 113 } 114 115 std::string error = AddRulesImpl(extension_id, rules); 116 117 if (!error.empty()) 118 return error; 119 120 // Commit all rules into |rules_| on success. 121 for (std::vector<linked_ptr<Rule> >::const_iterator i = 122 rules.begin(); i != rules.end(); ++i) { 123 const RuleId& rule_id = *((*i)->id); 124 RulesDictionaryKey key(extension_id, rule_id); 125 rules_[key] = *i; 126 } 127 128 MaybeProcessChangedRules(extension_id); 129 return kSuccess; 130 } 131 132 std::string RulesRegistry::AddRules( 133 const std::string& extension_id, 134 const std::vector<linked_ptr<Rule> >& rules) { 135 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 136 137 std::string error = CheckAndFillInOptionalRules(extension_id, rules); 138 if (!error.empty()) 139 return error; 140 FillInOptionalPriorities(rules); 141 142 return AddRulesNoFill(extension_id, rules); 143 } 144 145 std::string RulesRegistry::RemoveRules( 146 const std::string& extension_id, 147 const std::vector<std::string>& rule_identifiers) { 148 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 149 150 std::string error = RemoveRulesImpl(extension_id, rule_identifiers); 151 152 if (!error.empty()) 153 return error; 154 155 for (std::vector<std::string>::const_iterator i = rule_identifiers.begin(); 156 i != rule_identifiers.end(); 157 ++i) { 158 RulesDictionaryKey lookup_key(extension_id, *i); 159 rules_.erase(lookup_key); 160 } 161 162 MaybeProcessChangedRules(extension_id); 163 RemoveUsedRuleIdentifiers(extension_id, rule_identifiers); 164 return kSuccess; 165 } 166 167 std::string RulesRegistry::RemoveAllRules(const std::string& extension_id) { 168 std::string result = RulesRegistry::RemoveAllRulesNoStoreUpdate(extension_id); 169 MaybeProcessChangedRules(extension_id); // Now update the prefs and store. 170 return result; 171 } 172 173 std::string RulesRegistry::RemoveAllRulesNoStoreUpdate( 174 const std::string& extension_id) { 175 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 176 177 std::string error = RemoveAllRulesImpl(extension_id); 178 179 if (!error.empty()) 180 return error; 181 182 for (RulesDictionary::const_iterator i = rules_.begin(); 183 i != rules_.end();) { 184 const RulesDictionaryKey& key = i->first; 185 ++i; 186 if (key.first == extension_id) 187 rules_.erase(key); 188 } 189 190 RemoveAllUsedRuleIdentifiers(extension_id); 191 return kSuccess; 192 } 193 194 void RulesRegistry::GetRules(const std::string& extension_id, 195 const std::vector<std::string>& rule_identifiers, 196 std::vector<linked_ptr<Rule> >* out) { 197 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 198 199 for (std::vector<std::string>::const_iterator i = rule_identifiers.begin(); 200 i != rule_identifiers.end(); ++i) { 201 RulesDictionaryKey lookup_key(extension_id, *i); 202 RulesDictionary::iterator entry = rules_.find(lookup_key); 203 if (entry != rules_.end()) 204 out->push_back(entry->second); 205 } 206 } 207 208 void RulesRegistry::GetAllRules(const std::string& extension_id, 209 std::vector<linked_ptr<Rule> >* out) { 210 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 211 212 for (RulesDictionary::const_iterator i = rules_.begin(); 213 i != rules_.end(); ++i) { 214 const RulesDictionaryKey& key = i->first; 215 if (key.first == extension_id) 216 out->push_back(i->second); 217 } 218 } 219 220 void RulesRegistry::OnExtensionUnloaded(const std::string& extension_id) { 221 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 222 std::string error = RemoveAllRulesImpl(extension_id); 223 if (!error.empty()) 224 LOG(ERROR) << error; 225 } 226 227 void RulesRegistry::OnExtensionUninstalled(const std::string& extension_id) { 228 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 229 std::string error = RemoveAllRulesNoStoreUpdate(extension_id); 230 if (!error.empty()) 231 LOG(ERROR) << error; 232 } 233 234 void RulesRegistry::OnExtensionLoaded(const std::string& extension_id) { 235 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 236 std::vector<linked_ptr<Rule> > rules; 237 GetAllRules(extension_id, &rules); 238 std::string error = AddRulesImpl(extension_id, rules); 239 if (!error.empty()) 240 LOG(ERROR) << error; 241 } 242 243 size_t RulesRegistry::GetNumberOfUsedRuleIdentifiersForTesting() const { 244 size_t entry_count = 0u; 245 for (RuleIdentifiersMap::const_iterator extension = 246 used_rule_identifiers_.begin(); 247 extension != used_rule_identifiers_.end(); 248 ++extension) { 249 // Each extension is counted as 1 just for being there. Otherwise we miss 250 // keys with empty values. 251 entry_count += 1u + extension->second.size(); 252 } 253 return entry_count; 254 } 255 256 void RulesRegistry::DeserializeAndAddRules( 257 const std::string& extension_id, 258 scoped_ptr<base::Value> rules) { 259 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 260 261 AddRulesNoFill(extension_id, RulesFromValue(rules.get())); 262 } 263 264 RulesRegistry::~RulesRegistry() { 265 } 266 267 void RulesRegistry::MarkReady(base::Time storage_init_time) { 268 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 269 270 if (!storage_init_time.is_null()) { 271 UMA_HISTOGRAM_TIMES("Extensions.DeclarativeRulesStorageInitialization", 272 base::Time::Now() - storage_init_time); 273 } 274 275 ready_.Signal(); 276 } 277 278 void RulesRegistry::ProcessChangedRules(const std::string& extension_id) { 279 DCHECK(content::BrowserThread::CurrentlyOn(owner_thread())); 280 281 DCHECK(ContainsKey(process_changed_rules_requested_, extension_id)); 282 process_changed_rules_requested_[extension_id] = NOT_SCHEDULED_FOR_PROCESSING; 283 284 std::vector<linked_ptr<Rule> > new_rules; 285 GetAllRules(extension_id, &new_rules); 286 content::BrowserThread::PostTask( 287 content::BrowserThread::UI, 288 FROM_HERE, 289 base::Bind(&RulesCacheDelegate::WriteToStorage, 290 cache_delegate_, 291 extension_id, 292 base::Passed(RulesToValue(new_rules)))); 293 } 294 295 void RulesRegistry::MaybeProcessChangedRules(const std::string& extension_id) { 296 // Read and initialize |process_changed_rules_requested_[extension_id]| if 297 // necessary. (Note that the insertion below will not overwrite 298 // |process_changed_rules_requested_[extension_id]| if that already exists. 299 std::pair<ProcessStateMap::iterator, bool> insertion = 300 process_changed_rules_requested_.insert(std::make_pair( 301 extension_id, 302 profile_ ? NOT_SCHEDULED_FOR_PROCESSING : NEVER_PROCESS)); 303 if (insertion.first->second != NOT_SCHEDULED_FOR_PROCESSING) 304 return; 305 306 process_changed_rules_requested_[extension_id] = SCHEDULED_FOR_PROCESSING; 307 ready_.Post(FROM_HERE, 308 base::Bind(&RulesRegistry::ProcessChangedRules, 309 weak_ptr_factory_.GetWeakPtr(), 310 extension_id)); 311 } 312 313 bool RulesRegistry::IsUniqueId(const std::string& extension_id, 314 const std::string& rule_id) const { 315 RuleIdentifiersMap::const_iterator identifiers = 316 used_rule_identifiers_.find(extension_id); 317 if (identifiers == used_rule_identifiers_.end()) 318 return true; 319 return identifiers->second.find(rule_id) == identifiers->second.end(); 320 } 321 322 std::string RulesRegistry::GenerateUniqueId(const std::string& extension_id) { 323 while (!IsUniqueId(extension_id, ToId(last_generated_rule_identifier_id_))) 324 ++last_generated_rule_identifier_id_; 325 return ToId(last_generated_rule_identifier_id_); 326 } 327 328 std::string RulesRegistry::CheckAndFillInOptionalRules( 329 const std::string& extension_id, 330 const std::vector<linked_ptr<Rule> >& rules) { 331 // IDs we have inserted, in case we need to rollback this operation. 332 std::vector<std::string> rollback_log; 333 334 // First we insert all rules with existing identifier, so that generated 335 // identifiers cannot collide with identifiers passed by the caller. 336 for (std::vector<linked_ptr<Rule> >::const_iterator i = rules.begin(); 337 i != rules.end(); 338 ++i) { 339 Rule* rule = i->get(); 340 if (rule->id.get()) { 341 std::string id = *(rule->id); 342 if (!IsUniqueId(extension_id, id)) { 343 RemoveUsedRuleIdentifiers(extension_id, rollback_log); 344 return "Id " + id + " was used multiple times."; 345 } 346 used_rule_identifiers_[extension_id].insert(id); 347 } 348 } 349 // Now we generate IDs in case they were not specified in the rules. This 350 // cannot fail so we do not need to keep track of a rollback log. 351 for (std::vector<linked_ptr<Rule> >::const_iterator i = rules.begin(); 352 i != rules.end(); 353 ++i) { 354 Rule* rule = i->get(); 355 if (!rule->id.get()) { 356 rule->id.reset(new std::string(GenerateUniqueId(extension_id))); 357 used_rule_identifiers_[extension_id].insert(*(rule->id)); 358 } 359 } 360 return std::string(); 361 } 362 363 void RulesRegistry::FillInOptionalPriorities( 364 const std::vector<linked_ptr<Rule> >& rules) { 365 std::vector<linked_ptr<Rule> >::const_iterator i; 366 for (i = rules.begin(); i != rules.end(); ++i) { 367 if (!(*i)->priority.get()) 368 (*i)->priority.reset(new int(DEFAULT_PRIORITY)); 369 } 370 } 371 372 void RulesRegistry::RemoveUsedRuleIdentifiers( 373 const std::string& extension_id, 374 const std::vector<std::string>& identifiers) { 375 std::vector<std::string>::const_iterator i; 376 for (i = identifiers.begin(); i != identifiers.end(); ++i) 377 used_rule_identifiers_[extension_id].erase(*i); 378 } 379 380 void RulesRegistry::RemoveAllUsedRuleIdentifiers( 381 const std::string& extension_id) { 382 used_rule_identifiers_.erase(extension_id); 383 } 384 385 } // namespace extensions 386