Chromium Code Reviews| 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 scoped_ptr<base::Value>(list.release()); | |
|
battre
2012/06/15 22:56:49
nit: how about
return list.PassAs<base::Value>();
Matt Perry
2012/06/18 21:01:01
Cool, thanks!
| |
| 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 scoped_ptr<base::Value> value); | |
| 79 void WriteToStorage(const std::string& extension_id, | |
| 80 scoped_ptr<base::Value> value); | |
| 81 | |
| 82 // Check if we are done reading all data from storage, and notify the | |
| 83 // RulesRegistry if so. | |
|
battre
2012/06/15 22:56:49
add comment how this notification happens?
Matt Perry
2012/06/18 21:01:01
I'm not sure I understand what you mean. I added a
| |
| 84 void CheckIfReady(); | |
| 85 | |
| 86 // Deserialize the rules from the given Value object and add them to the | |
| 87 // RulesRegistry. | |
| 88 void ReadFromStorageOnRegistryThread(const std::string& extension_id, | |
| 89 scoped_ptr<base::Value> value); | |
| 90 | |
| 91 // Notify the RulesRegistry that we are now ready. | |
| 92 void NotifyReadyOnRegistryThread(); | |
| 93 | |
| 94 content::NotificationRegistrar registrar_; | |
| 95 Profile* profile_; | |
| 96 | |
| 97 // The key under which rules are stored. | |
| 98 const std::string storage_key_; | |
| 99 | |
| 100 // A set of extension IDs that have rules we are reading from storage. | |
| 101 std::set<std::string> waiting_for_extensions_; | |
| 102 | |
| 103 // The thread that our RulesRegistry lives on. | |
| 104 content::BrowserThread::ID rules_registry_thread_; | |
| 105 | |
| 106 // The following are only accessible on rules_registry_thread_. | |
| 107 | |
| 108 // The RulesRegistry whose delegate we are. | |
| 109 RulesRegistryWithCache* rules_registry_; | |
| 110 | |
| 111 // True when we have finished reading from storage for all extensions that | |
| 112 // are loaded on startup. | |
| 113 bool ready_; | |
| 114 }; | |
| 115 | |
| 116 RulesRegistryStorageDelegate::RulesRegistryStorageDelegate() { | |
| 117 } | |
| 118 | |
| 119 RulesRegistryStorageDelegate::~RulesRegistryStorageDelegate() { | |
| 120 // RulesRegistry owns us, which means it has been deleted. | |
| 121 inner_->rules_registry_ = NULL; | |
| 122 } | |
| 123 | |
| 124 void RulesRegistryStorageDelegate::Init(Profile* profile, | |
| 125 RulesRegistryWithCache* rules_registry, | |
| 126 const std::string& storage_key) { | |
|
battre
2012/06/15 22:56:49
nit: indent
Matt Perry
2012/06/18 21:01:01
Done.
| |
| 127 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 128 inner_ = new Inner(profile, rules_registry, storage_key); | |
| 129 } | |
| 130 | |
| 131 bool RulesRegistryStorageDelegate::IsReady() { | |
| 132 DCHECK(content::BrowserThread::CurrentlyOn(inner_->rules_registry_thread_)); | |
| 133 return inner_->ready_; | |
|
battre
2012/06/15 22:56:49
this contradicts the comment "Methods/variables sh
Matt Perry
2012/06/18 21:01:01
Are you referring to ready_? ready_'s definition f
| |
| 134 } | |
| 135 | |
| 136 void RulesRegistryStorageDelegate::OnRulesChanged( | |
| 137 RulesRegistryWithCache* rules_registry, | |
| 138 const std::string& extension_id) { | |
| 139 DCHECK(content::BrowserThread::CurrentlyOn(inner_->rules_registry_thread_)); | |
| 140 std::vector<linked_ptr<RulesRegistry::Rule> > new_rules; | |
| 141 std::string error = rules_registry->GetAllRules(extension_id, &new_rules); | |
| 142 DCHECK_EQ("", error); | |
| 143 content::BrowserThread::PostTask( | |
| 144 content::BrowserThread::UI, FROM_HERE, | |
| 145 base::Bind(&Inner::WriteToStorage, inner_.get(), extension_id, | |
| 146 base::Passed(RulesToValue(new_rules)))); | |
| 147 } | |
| 148 | |
| 149 RulesRegistryStorageDelegate::Inner::Inner(Profile* profile, | |
| 150 RulesRegistryWithCache* rules_registry, | |
| 151 const std::string& storage_key) | |
|
battre
2012/06/15 22:56:49
nit: indent
Matt Perry
2012/06/18 21:01:01
Done.
| |
| 152 : profile_(profile), | |
| 153 storage_key_(storage_key), | |
| 154 rules_registry_thread_(rules_registry->GetOwnerThread()), | |
| 155 rules_registry_(rules_registry), | |
| 156 ready_(false) { | |
| 157 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, | |
| 158 content::Source<Profile>(profile)); | |
| 159 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY, | |
| 160 content::Source<Profile>(profile)); | |
| 161 } | |
| 162 | |
| 163 RulesRegistryStorageDelegate::Inner::~Inner() {} | |
| 164 | |
| 165 void RulesRegistryStorageDelegate::Inner::Observe( | |
| 166 int type, | |
| 167 const content::NotificationSource& source, | |
| 168 const content::NotificationDetails& details) { | |
| 169 if (type == chrome::NOTIFICATION_EXTENSION_LOADED) { | |
| 170 const extensions::Extension* extension = | |
| 171 content::Details<const extensions::Extension>(details).ptr(); | |
| 172 extensions::StateStore* store = | |
| 173 ExtensionSystem::Get(profile_)->state_store(); | |
| 174 if (store && extension->HasAPIPermission( | |
| 175 ExtensionAPIPermission::kDeclarativeWebRequest)) { | |
|
Matt Perry
2012/06/15 21:54:41
Is there a better way to check if an extension is
battre
2012/06/15 22:56:49
"declarative" (and ExtensionAPIPermission::kDeclar
Matt Perry
2012/06/18 21:01:01
I'll leave as-is for now but add a TODO.
| |
| 176 waiting_for_extensions_.insert(extension->id()); | |
| 177 store->GetExtensionValue(extension->id(), storage_key_, | |
| 178 base::Bind(&Inner::ReadFromStorage, this, extension->id())); | |
|
battre
2012/06/15 22:56:49
What do you think of moving 177-178 into a functio
Matt Perry
2012/06/18 21:01:01
Done.
| |
| 179 } | |
| 180 } else if (type == chrome::NOTIFICATION_EXTENSIONS_READY) { | |
| 181 CheckIfReady(); | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 void RulesRegistryStorageDelegate::Inner::ReadFromStorage( | |
| 186 const std::string& extension_id, scoped_ptr<base::Value> value) { | |
| 187 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 188 content::BrowserThread::PostTask( | |
| 189 rules_registry_thread_, FROM_HERE, | |
| 190 base::Bind(&Inner::ReadFromStorageOnRegistryThread, this, | |
| 191 extension_id, base::Passed(value.Pass()))); | |
| 192 | |
| 193 waiting_for_extensions_.erase(extension_id); | |
| 194 CheckIfReady(); | |
| 195 } | |
| 196 | |
| 197 void RulesRegistryStorageDelegate::Inner::WriteToStorage( | |
| 198 const std::string& extension_id, scoped_ptr<base::Value> value) { | |
| 199 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 200 StateStore* store = ExtensionSystem::Get(profile_)->state_store(); | |
| 201 if (store) | |
| 202 store->SetExtensionValue(extension_id, storage_key_, value.Pass()); | |
| 203 } | |
| 204 | |
| 205 void RulesRegistryStorageDelegate::Inner::CheckIfReady() { | |
| 206 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 207 if (!waiting_for_extensions_.empty()) | |
| 208 return; | |
| 209 | |
| 210 if (ready_) | |
|
battre
2012/06/15 22:56:49
access to ready_ on UI thread
Matt Perry
2012/06/18 21:01:01
Doh! Good catch.
| |
| 211 return; // we've already notified our readiness | |
|
battre
2012/06/15 22:56:49
This behavior (call callback only once) is not ref
Matt Perry
2012/06/18 21:01:01
Added comments to CheckIfReady and OnReady.
| |
| 212 | |
| 213 content::BrowserThread::PostTask( | |
| 214 rules_registry_thread_, FROM_HERE, | |
| 215 base::Bind(&Inner::NotifyReadyOnRegistryThread, this)); | |
| 216 } | |
| 217 | |
| 218 void RulesRegistryStorageDelegate::Inner::ReadFromStorageOnRegistryThread( | |
| 219 const std::string& extension_id, scoped_ptr<base::Value> value) { | |
| 220 DCHECK(content::BrowserThread::CurrentlyOn(rules_registry_thread_)); | |
| 221 if (!rules_registry_) | |
| 222 return; // registry went away | |
| 223 | |
| 224 rules_registry_->AddRules(extension_id, RulesFromValue(value.get())); | |
| 225 } | |
| 226 | |
| 227 void RulesRegistryStorageDelegate::Inner::NotifyReadyOnRegistryThread() { | |
| 228 DCHECK(content::BrowserThread::CurrentlyOn(rules_registry_thread_)); | |
| 229 ready_ = true; | |
|
battre
2012/06/15 22:56:49
access to ready_ on rules_registry_thread_
Matt Perry
2012/06/18 21:01:01
That is the intended thread.
| |
| 230 rules_registry_->OnReady(); | |
| 231 } | |
| 232 | |
| 233 } // namespace extensions | |
| OLD | NEW |