1// Copyright 2013 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 "extensions/browser/api/declarative/rules_cache_delegate.h" 6 7#include "content/public/browser/browser_context.h" 8#include "content/public/browser/notification_details.h" 9#include "content/public/browser/notification_source.h" 10#include "extensions/browser/api/declarative/rules_registry.h" 11#include "extensions/browser/extension_prefs.h" 12#include "extensions/browser/extension_registry.h" 13#include "extensions/browser/extension_system.h" 14#include "extensions/browser/info_map.h" 15#include "extensions/browser/state_store.h" 16#include "extensions/common/permissions/permissions_data.h" 17 18namespace { 19 20// Returns the key to use for storing declarative rules in the state store. 21std::string GetDeclarativeRuleStorageKey(const std::string& event_name, 22 bool incognito) { 23 if (incognito) 24 return "declarative_rules.incognito." + event_name; 25 else 26 return "declarative_rules." + event_name; 27} 28 29 30} // namespace 31 32namespace extensions { 33 34// RulesCacheDelegate 35 36const char RulesCacheDelegate::kRulesStoredKey[] = 37 "has_declarative_rules"; 38 39RulesCacheDelegate::RulesCacheDelegate(bool log_storage_init_delay) 40 : browser_context_(NULL), 41 log_storage_init_delay_(log_storage_init_delay), 42 notified_registry_(false), 43 weak_ptr_factory_(this) { 44} 45 46RulesCacheDelegate::~RulesCacheDelegate() {} 47 48// Returns the key to use for storing whether the rules have been stored. 49// static 50std::string RulesCacheDelegate::GetRulesStoredKey(const std::string& event_name, 51 bool incognito) { 52 std::string result(kRulesStoredKey); 53 result += incognito ? ".incognito." : "."; 54 return result + event_name; 55} 56 57// This is called from the constructor of RulesRegistry, so it is 58// important that it both 59// 1. calls no (in particular virtual) methods of the rules registry, and 60// 2. does not create scoped_refptr holding the registry. (A short-lived 61// scoped_refptr might delete the rules registry before it is constructed.) 62void RulesCacheDelegate::Init(RulesRegistry* registry) { 63 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 64 65 // WARNING: The first use of |registry_| will bind it to the calling thread 66 // so don't use this here. 67 registry_ = registry->GetWeakPtr(); 68 69 browser_context_ = registry->browser_context(); 70 storage_key_ = 71 GetDeclarativeRuleStorageKey(registry->event_name(), 72 browser_context_->IsOffTheRecord()); 73 rules_stored_key_ = GetRulesStoredKey(registry->event_name(), 74 browser_context_->IsOffTheRecord()); 75 rules_registry_thread_ = registry->owner_thread(); 76 77 ExtensionSystem& system = *ExtensionSystem::Get(browser_context_); 78 StateStore* store = system.rules_store(); 79 if (store) 80 store->RegisterKey(storage_key_); 81 82 if (browser_context_->IsOffTheRecord()) 83 log_storage_init_delay_ = false; 84 85 system.ready().Post( 86 FROM_HERE, 87 base::Bind(&RulesCacheDelegate::ReadRulesForInstalledExtensions, 88 weak_ptr_factory_.GetWeakPtr())); 89 system.ready().Post(FROM_HERE, 90 base::Bind(&RulesCacheDelegate::CheckIfReady, 91 weak_ptr_factory_.GetWeakPtr())); 92} 93 94void RulesCacheDelegate::WriteToStorage(const std::string& extension_id, 95 scoped_ptr<base::Value> value) { 96 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 97 if (!browser_context_) 98 return; 99 100 const base::ListValue* rules = NULL; 101 CHECK(value->GetAsList(&rules)); 102 bool rules_stored_previously = GetDeclarativeRulesStored(extension_id); 103 bool store_rules = !rules->empty(); 104 SetDeclarativeRulesStored(extension_id, store_rules); 105 if (!rules_stored_previously && !store_rules) 106 return; 107 108 StateStore* store = ExtensionSystem::Get(browser_context_)->rules_store(); 109 if (store) 110 store->SetExtensionValue(extension_id, storage_key_, value.Pass()); 111} 112 113void RulesCacheDelegate::CheckIfReady() { 114 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 115 if (notified_registry_ || !waiting_for_extensions_.empty()) 116 return; 117 118 content::BrowserThread::PostTask( 119 rules_registry_thread_, 120 FROM_HERE, 121 base::Bind( 122 &RulesRegistry::MarkReady, registry_, storage_init_time_)); 123 notified_registry_ = true; 124} 125 126void RulesCacheDelegate::ReadRulesForInstalledExtensions() { 127 bool is_ready = ExtensionSystem::Get(browser_context_)->ready().is_signaled(); 128 // In an OTR context, we start on top of a normal context already, so the 129 // extension service should be ready. 130 DCHECK(!browser_context_->IsOffTheRecord() || is_ready); 131 if (is_ready) { 132 const ExtensionSet& extensions = 133 ExtensionRegistry::Get(browser_context_)->enabled_extensions(); 134 const ExtensionPrefs* extension_prefs = 135 ExtensionPrefs::Get(browser_context_); 136 for (ExtensionSet::const_iterator i = extensions.begin(); 137 i != extensions.end(); 138 ++i) { 139 bool needs_apis_storing_rules = 140 (*i)->permissions_data()->HasAPIPermission( 141 APIPermission::kDeclarativeContent) || 142 (*i)->permissions_data()->HasAPIPermission( 143 APIPermission::kDeclarativeWebRequest); 144 bool respects_off_the_record = 145 !(browser_context_->IsOffTheRecord()) || 146 extension_prefs->IsIncognitoEnabled((*i)->id()); 147 if (needs_apis_storing_rules && respects_off_the_record) 148 ReadFromStorage((*i)->id()); 149 } 150 } 151} 152 153void RulesCacheDelegate::ReadFromStorage(const std::string& extension_id) { 154 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 155 if (!browser_context_) 156 return; 157 158 if (log_storage_init_delay_ && storage_init_time_.is_null()) 159 storage_init_time_ = base::Time::Now(); 160 161 if (!GetDeclarativeRulesStored(extension_id)) { 162 ExtensionSystem::Get(browser_context_)->ready().Post( 163 FROM_HERE, base::Bind(&RulesCacheDelegate::CheckIfReady, 164 weak_ptr_factory_.GetWeakPtr())); 165 return; 166 } 167 168 StateStore* store = ExtensionSystem::Get(browser_context_)->rules_store(); 169 if (!store) 170 return; 171 waiting_for_extensions_.insert(extension_id); 172 store->GetExtensionValue( 173 extension_id, 174 storage_key_, 175 base::Bind(&RulesCacheDelegate::ReadFromStorageCallback, 176 weak_ptr_factory_.GetWeakPtr(), 177 extension_id)); 178} 179 180void RulesCacheDelegate::ReadFromStorageCallback( 181 const std::string& extension_id, 182 scoped_ptr<base::Value> value) { 183 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 184 content::BrowserThread::PostTask( 185 rules_registry_thread_, 186 FROM_HERE, 187 base::Bind(&RulesRegistry::DeserializeAndAddRules, 188 registry_, 189 extension_id, 190 base::Passed(&value))); 191 192 waiting_for_extensions_.erase(extension_id); 193 194 if (waiting_for_extensions_.empty()) 195 ExtensionSystem::Get(browser_context_)->ready().Post( 196 FROM_HERE, base::Bind(&RulesCacheDelegate::CheckIfReady, 197 weak_ptr_factory_.GetWeakPtr())); 198} 199 200bool RulesCacheDelegate::GetDeclarativeRulesStored( 201 const std::string& extension_id) const { 202 CHECK(browser_context_); 203 const ExtensionScopedPrefs* extension_prefs = 204 ExtensionPrefs::Get(browser_context_); 205 206 bool rules_stored = true; 207 if (extension_prefs->ReadPrefAsBoolean( 208 extension_id, rules_stored_key_, &rules_stored)) 209 return rules_stored; 210 211 // Safe default -- if we don't know that the rules are not stored, we force 212 // a read by returning true. 213 return true; 214} 215 216void RulesCacheDelegate::SetDeclarativeRulesStored( 217 const std::string& extension_id, 218 bool rules_stored) { 219 CHECK(browser_context_); 220 DCHECK(ExtensionRegistry::Get(browser_context_) 221 ->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING)); 222 223 ExtensionScopedPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_); 224 extension_prefs->UpdateExtensionPref( 225 extension_id, 226 rules_stored_key_, 227 new base::FundamentalValue(rules_stored)); 228} 229 230} // namespace extensions 231