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