| OLD | NEW |
| (Empty) | |
| 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_storage_deleg
ate.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "chrome/browser/extensions/extension_system.h" |
| 9 #include "chrome/browser/extensions/state_store.h" |
| 10 #include "chrome/common/chrome_notification_types.h" |
| 11 #include "chrome/common/extensions/extension.h" |
| 12 #include "content/public/browser/browser_thread.h" |
| 13 #include "content/public/browser/notification_details.h" |
| 14 #include "content/public/browser/notification_source.h" |
| 15 |
| 16 namespace extensions { |
| 17 |
| 18 namespace { |
| 19 |
| 20 scoped_ptr<base::Value> RulesToValue( |
| 21 const std::vector<linked_ptr<RulesRegistry::Rule> >& rules) { |
| 22 scoped_ptr<base::ListValue> list(new base::ListValue()); |
| 23 for (size_t i = 0; i < rules.size(); ++i) |
| 24 list->Append(rules[i]->ToValue().release()); |
| 25 return list.PassAs<base::Value>(); |
| 26 } |
| 27 |
| 28 std::vector<linked_ptr<RulesRegistry::Rule> > RulesFromValue( |
| 29 base::Value* value) { |
| 30 std::vector<linked_ptr<RulesRegistry::Rule> > rules; |
| 31 |
| 32 base::ListValue* list = NULL; |
| 33 if (!value || !value->GetAsList(&list)) |
| 34 return rules; |
| 35 |
| 36 for (size_t i = 0; i < list->GetSize(); ++i) { |
| 37 base::DictionaryValue* dict = NULL; |
| 38 if (!list->GetDictionary(i, &dict)) |
| 39 continue; |
| 40 linked_ptr<RulesRegistry::Rule> rule(new RulesRegistry::Rule()); |
| 41 if (RulesRegistry::Rule::Populate(*dict, rule.get())) |
| 42 rules.push_back(rule); |
| 43 } |
| 44 |
| 45 return rules; |
| 46 } |
| 47 |
| 48 } // namespace |
| 49 |
| 50 // This class coordinates information between the UI and RulesRegistry threads. |
| 51 // It may outlive the RulesRegistry, which owns the delegate. Methods/variables |
| 52 // should be used on the UI thread unless otherwise noted. |
| 53 class RulesRegistryStorageDelegate::Inner |
| 54 : public content::NotificationObserver, |
| 55 public base::RefCountedThreadSafe< |
| 56 Inner, content::BrowserThread::DeleteOnUIThread> { |
| 57 public: |
| 58 Inner(Profile* profile, |
| 59 RulesRegistryWithCache* rules_registry, |
| 60 const std::string& storage_key); |
| 61 |
| 62 private: |
| 63 friend class RulesRegistryStorageDelegate; |
| 64 friend struct content::BrowserThread::DeleteOnThread< |
| 65 content::BrowserThread::UI>; |
| 66 friend class base::DeleteHelper<Inner>; |
| 67 |
| 68 ~Inner(); |
| 69 |
| 70 // NotificationObserver |
| 71 virtual void Observe( |
| 72 int type, |
| 73 const content::NotificationSource& source, |
| 74 const content::NotificationDetails& details) OVERRIDE; |
| 75 |
| 76 // Read/write a list of rules serialized to Values. |
| 77 void ReadFromStorage(const std::string& extension_id); |
| 78 void ReadFromStorageCallback(const std::string& extension_id, |
| 79 scoped_ptr<base::Value> value); |
| 80 void WriteToStorage(const std::string& extension_id, |
| 81 scoped_ptr<base::Value> value); |
| 82 |
| 83 // Check if we are done reading all data from storage on startup, and notify |
| 84 // the RulesRegistry on its thread if so. The notification is delivered |
| 85 // exactly once. |
| 86 void CheckIfReady(); |
| 87 |
| 88 // Deserialize the rules from the given Value object and add them to the |
| 89 // RulesRegistry. |
| 90 void ReadFromStorageOnRegistryThread(const std::string& extension_id, |
| 91 scoped_ptr<base::Value> value); |
| 92 |
| 93 // Notify the RulesRegistry that we are now ready. |
| 94 void NotifyReadyOnRegistryThread(); |
| 95 |
| 96 content::NotificationRegistrar registrar_; |
| 97 Profile* profile_; |
| 98 |
| 99 // The key under which rules are stored. |
| 100 const std::string storage_key_; |
| 101 |
| 102 // A set of extension IDs that have rules we are reading from storage. |
| 103 std::set<std::string> waiting_for_extensions_; |
| 104 |
| 105 // The thread that our RulesRegistry lives on. |
| 106 content::BrowserThread::ID rules_registry_thread_; |
| 107 |
| 108 // The following are only accessible on rules_registry_thread_. |
| 109 |
| 110 // The RulesRegistry whose delegate we are. |
| 111 RulesRegistryWithCache* rules_registry_; |
| 112 |
| 113 // True when we have finished reading from storage for all extensions that |
| 114 // are loaded on startup. |
| 115 bool ready_; |
| 116 }; |
| 117 |
| 118 RulesRegistryStorageDelegate::RulesRegistryStorageDelegate() { |
| 119 } |
| 120 |
| 121 RulesRegistryStorageDelegate::~RulesRegistryStorageDelegate() { |
| 122 // RulesRegistry owns us, which means it has been deleted. |
| 123 inner_->rules_registry_ = NULL; |
| 124 } |
| 125 |
| 126 void RulesRegistryStorageDelegate::Init(Profile* profile, |
| 127 RulesRegistryWithCache* rules_registry, |
| 128 const std::string& storage_key) { |
| 129 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 130 extensions::StateStore* store = ExtensionSystem::Get(profile)->state_store(); |
| 131 if (store) |
| 132 store->RegisterKey(storage_key); |
| 133 inner_ = new Inner(profile, rules_registry, storage_key); |
| 134 } |
| 135 |
| 136 bool RulesRegistryStorageDelegate::IsReady() { |
| 137 DCHECK(content::BrowserThread::CurrentlyOn(inner_->rules_registry_thread_)); |
| 138 return inner_->ready_; |
| 139 } |
| 140 |
| 141 void RulesRegistryStorageDelegate::OnRulesChanged( |
| 142 RulesRegistryWithCache* rules_registry, |
| 143 const std::string& extension_id) { |
| 144 DCHECK(content::BrowserThread::CurrentlyOn(inner_->rules_registry_thread_)); |
| 145 std::vector<linked_ptr<RulesRegistry::Rule> > new_rules; |
| 146 std::string error = rules_registry->GetAllRules(extension_id, &new_rules); |
| 147 DCHECK_EQ("", error); |
| 148 content::BrowserThread::PostTask( |
| 149 content::BrowserThread::UI, FROM_HERE, |
| 150 base::Bind(&Inner::WriteToStorage, inner_.get(), extension_id, |
| 151 base::Passed(RulesToValue(new_rules)))); |
| 152 } |
| 153 |
| 154 RulesRegistryStorageDelegate::Inner::Inner( |
| 155 Profile* profile, |
| 156 RulesRegistryWithCache* rules_registry, |
| 157 const std::string& storage_key) |
| 158 : profile_(profile), |
| 159 storage_key_(storage_key), |
| 160 rules_registry_thread_(rules_registry->GetOwnerThread()), |
| 161 rules_registry_(rules_registry), |
| 162 ready_(false) { |
| 163 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, |
| 164 content::Source<Profile>(profile)); |
| 165 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY, |
| 166 content::Source<Profile>(profile)); |
| 167 } |
| 168 |
| 169 RulesRegistryStorageDelegate::Inner::~Inner() {} |
| 170 |
| 171 void RulesRegistryStorageDelegate::Inner::Observe( |
| 172 int type, |
| 173 const content::NotificationSource& source, |
| 174 const content::NotificationDetails& details) { |
| 175 if (type == chrome::NOTIFICATION_EXTENSION_LOADED) { |
| 176 const extensions::Extension* extension = |
| 177 content::Details<const extensions::Extension>(details).ptr(); |
| 178 // TODO(mpcomplete): This API check should generalize to any use of |
| 179 // declarative rules, not just webRequest. |
| 180 if (extension->HasAPIPermission( |
| 181 ExtensionAPIPermission::kDeclarativeWebRequest)) { |
| 182 ReadFromStorage(extension->id()); |
| 183 } |
| 184 } else if (type == chrome::NOTIFICATION_EXTENSIONS_READY) { |
| 185 CheckIfReady(); |
| 186 } |
| 187 } |
| 188 |
| 189 void RulesRegistryStorageDelegate::Inner::ReadFromStorage( |
| 190 const std::string& extension_id) { |
| 191 extensions::StateStore* store = ExtensionSystem::Get(profile_)->state_store(); |
| 192 if (store) { |
| 193 waiting_for_extensions_.insert(extension_id); |
| 194 store->GetExtensionValue(extension_id, storage_key_, |
| 195 base::Bind(&Inner::ReadFromStorageCallback, this, extension_id)); |
| 196 } |
| 197 } |
| 198 |
| 199 void RulesRegistryStorageDelegate::Inner::ReadFromStorageCallback( |
| 200 const std::string& extension_id, scoped_ptr<base::Value> value) { |
| 201 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 202 content::BrowserThread::PostTask( |
| 203 rules_registry_thread_, FROM_HERE, |
| 204 base::Bind(&Inner::ReadFromStorageOnRegistryThread, this, |
| 205 extension_id, base::Passed(value.Pass()))); |
| 206 |
| 207 waiting_for_extensions_.erase(extension_id); |
| 208 CheckIfReady(); |
| 209 } |
| 210 |
| 211 void RulesRegistryStorageDelegate::Inner::WriteToStorage( |
| 212 const std::string& extension_id, scoped_ptr<base::Value> value) { |
| 213 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 214 StateStore* store = ExtensionSystem::Get(profile_)->state_store(); |
| 215 if (store) |
| 216 store->SetExtensionValue(extension_id, storage_key_, value.Pass()); |
| 217 } |
| 218 |
| 219 void RulesRegistryStorageDelegate::Inner::CheckIfReady() { |
| 220 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 221 if (!waiting_for_extensions_.empty()) |
| 222 return; |
| 223 |
| 224 content::BrowserThread::PostTask( |
| 225 rules_registry_thread_, FROM_HERE, |
| 226 base::Bind(&Inner::NotifyReadyOnRegistryThread, this)); |
| 227 } |
| 228 |
| 229 void RulesRegistryStorageDelegate::Inner::ReadFromStorageOnRegistryThread( |
| 230 const std::string& extension_id, scoped_ptr<base::Value> value) { |
| 231 DCHECK(content::BrowserThread::CurrentlyOn(rules_registry_thread_)); |
| 232 if (!rules_registry_) |
| 233 return; // registry went away |
| 234 |
| 235 rules_registry_->AddRules(extension_id, RulesFromValue(value.get())); |
| 236 } |
| 237 |
| 238 void RulesRegistryStorageDelegate::Inner::NotifyReadyOnRegistryThread() { |
| 239 DCHECK(content::BrowserThread::CurrentlyOn(rules_registry_thread_)); |
| 240 if (ready_) |
| 241 return; // we've already notified our readiness |
| 242 |
| 243 ready_ = true; |
| 244 rules_registry_->OnReady(); |
| 245 } |
| 246 |
| 247 } // namespace extensions |
| OLD | NEW |