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