Enable Enterprise enrollment on desktop builds.
[chromium-blink-merge.git] / chrome / browser / extensions / api / storage / managed_value_store_cache.cc
blob814d949fd296531b2bcded6c512b928a201f6a7d
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.
5 #include "chrome/browser/extensions/api/storage/managed_value_store_cache.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/file_util.h"
11 #include "base/logging.h"
12 #include "base/memory/weak_ptr.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/extensions/api/storage/policy_value_store.h"
15 #include "chrome/browser/policy/profile_policy_connector.h"
16 #include "chrome/browser/policy/profile_policy_connector_factory.h"
17 #include "chrome/browser/policy/schema_registry_service.h"
18 #include "chrome/browser/policy/schema_registry_service_factory.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/extensions/api/storage/storage_schema_manifest_handler.h"
21 #include "components/policy/core/common/policy_namespace.h"
22 #include "components/policy/core/common/schema.h"
23 #include "components/policy/core/common/schema_map.h"
24 #include "components/policy/core/common/schema_registry.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_observer.h"
28 #include "content/public/browser/notification_registrar.h"
29 #include "content/public/browser/notification_source.h"
30 #include "extensions/browser/api/storage/settings_storage_factory.h"
31 #include "extensions/browser/extension_prefs.h"
32 #include "extensions/browser/extension_registry.h"
33 #include "extensions/browser/extension_system.h"
34 #include "extensions/browser/value_store/value_store_change.h"
35 #include "extensions/common/api/storage.h"
36 #include "extensions/common/constants.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/extension_set.h"
39 #include "extensions/common/manifest.h"
40 #include "extensions/common/manifest_constants.h"
41 #include "extensions/common/one_shot_event.h"
43 using content::BrowserContext;
44 using content::BrowserThread;
46 namespace extensions {
48 namespace storage = core_api::storage;
50 namespace {
52 const char kLoadSchemasBackgroundTaskTokenName[] =
53 "load_managed_storage_schemas_token";
55 // The Legacy Browser Support was the first user of the policy-for-extensions
56 // API, and relied on behavior that will be phased out. If this extension is
57 // present then its policies will be loaded in a special way.
58 // TODO(joaodasilva): remove this for M35. http://crbug.com/325349
59 const char kLegacyBrowserSupportExtensionId[] =
60 "heildphpnddilhkemkielfhnkaagiabh";
62 } // namespace
64 // This helper observes initialization of all the installed extensions and
65 // subsequent loads and unloads, and keeps the SchemaRegistry of the Profile
66 // in sync with the current list of extensions. This allows the PolicyService
67 // to fetch cloud policy for those extensions, and allows its providers to
68 // selectively load only extension policy that has users.
69 class ManagedValueStoreCache::ExtensionTracker
70 : public content::NotificationObserver {
71 public:
72 explicit ExtensionTracker(Profile* profile);
73 virtual ~ExtensionTracker() {}
75 // NotificationObserver implementation:
76 virtual void Observe(int type,
77 const content::NotificationSource& source,
78 const content::NotificationDetails& details) OVERRIDE;
80 private:
81 // Handler for the signal from ExtensionSystem::ready().
82 void OnExtensionsReady();
84 // Starts a schema load for all extensions that use managed storage.
85 void LoadSchemas(scoped_ptr<ExtensionSet> added);
87 bool UsesManagedStorage(const Extension* extension) const;
89 // Loads the schemas of the |extensions| and passes a ComponentMap to
90 // Register().
91 static void LoadSchemasOnBlockingPool(scoped_ptr<ExtensionSet> extensions,
92 base::WeakPtr<ExtensionTracker> self);
93 void Register(const policy::ComponentMap* components);
95 Profile* profile_;
96 content::NotificationRegistrar registrar_;
97 policy::SchemaRegistry* schema_registry_;
98 base::WeakPtrFactory<ExtensionTracker> weak_factory_;
100 DISALLOW_COPY_AND_ASSIGN(ExtensionTracker);
103 ManagedValueStoreCache::ExtensionTracker::ExtensionTracker(Profile* profile)
104 : profile_(profile),
105 schema_registry_(
106 policy::SchemaRegistryServiceFactory::GetForContext(profile)),
107 weak_factory_(this) {
108 registrar_.Add(this,
109 chrome::NOTIFICATION_EXTENSION_INSTALLED,
110 content::Source<Profile>(profile_));
111 registrar_.Add(this,
112 chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
113 content::Source<Profile>(profile_));
115 // Load schemas when the extension system is ready. It might be ready now.
116 ExtensionSystem::Get(profile_)->ready().Post(
117 FROM_HERE,
118 base::Bind(&ExtensionTracker::OnExtensionsReady,
119 weak_factory_.GetWeakPtr()));
122 void ManagedValueStoreCache::ExtensionTracker::Observe(
123 int type,
124 const content::NotificationSource& source,
125 const content::NotificationDetails& details) {
126 // Some extensions are installed on the first run before the ExtensionSystem
127 // becomes ready. Wait until all of them are ready before registering the
128 // schemas of managed extensions, so that the policy loaders are reloaded at
129 // most once.
130 if (!ExtensionSystem::Get(profile_)->ready().is_signaled())
131 return;
133 switch (type) {
134 case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
135 scoped_ptr<ExtensionSet> added(new ExtensionSet);
136 added->Insert(
137 content::Details<InstalledExtensionInfo>(details)->extension);
138 LoadSchemas(added.Pass());
139 break;
141 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
142 const Extension* removed =
143 content::Details<const Extension>(details).ptr();
144 if (removed && UsesManagedStorage(removed)) {
145 schema_registry_->UnregisterComponent(policy::PolicyNamespace(
146 policy::POLICY_DOMAIN_EXTENSIONS, removed->id()));
148 break;
150 default:
151 NOTREACHED();
152 return;
156 void ManagedValueStoreCache::ExtensionTracker::OnExtensionsReady() {
157 // Load schemas for all installed extensions.
158 LoadSchemas(
159 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet());
162 void ManagedValueStoreCache::ExtensionTracker::LoadSchemas(
163 scoped_ptr<ExtensionSet> added) {
164 // Filter out extensions that don't use managed storage.
165 ExtensionSet::const_iterator it = added->begin();
166 while (it != added->end()) {
167 std::string to_remove;
168 if (!UsesManagedStorage(*it))
169 to_remove = (*it)->id();
170 ++it;
171 if (!to_remove.empty())
172 added->Remove(to_remove);
175 // Load the schema files in a background thread.
176 BrowserThread::PostBlockingPoolSequencedTask(
177 kLoadSchemasBackgroundTaskTokenName, FROM_HERE,
178 base::Bind(&ExtensionTracker::LoadSchemasOnBlockingPool,
179 base::Passed(&added),
180 weak_factory_.GetWeakPtr()));
183 bool ManagedValueStoreCache::ExtensionTracker::UsesManagedStorage(
184 const Extension* extension) const {
185 if (extension->manifest()->HasPath(manifest_keys::kStorageManagedSchema))
186 return true;
188 // TODO(joaodasilva): remove this by M35.
189 return extension->id() == kLegacyBrowserSupportExtensionId;
192 // static
193 void ManagedValueStoreCache::ExtensionTracker::LoadSchemasOnBlockingPool(
194 scoped_ptr<ExtensionSet> extensions,
195 base::WeakPtr<ExtensionTracker> self) {
196 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
197 scoped_ptr<policy::ComponentMap> components(new policy::ComponentMap);
199 for (ExtensionSet::const_iterator it = extensions->begin();
200 it != extensions->end(); ++it) {
201 std::string schema_file;
202 if (!(*it)->manifest()->GetString(
203 manifest_keys::kStorageManagedSchema, &schema_file)) {
204 // TODO(joaodasilva): Remove this. http://crbug.com/325349
205 (*components)[(*it)->id()] = policy::Schema();
206 continue;
208 // The extension should have been validated, so assume the schema exists
209 // and is valid.
210 std::string error;
211 policy::Schema schema =
212 StorageSchemaManifestHandler::GetSchema(it->get(), &error);
213 CHECK(schema.valid()) << error;
214 (*components)[(*it)->id()] = schema;
217 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
218 base::Bind(&ExtensionTracker::Register, self,
219 base::Owned(components.release())));
222 void ManagedValueStoreCache::ExtensionTracker::Register(
223 const policy::ComponentMap* components) {
224 DCHECK_CURRENTLY_ON(BrowserThread::UI);
225 schema_registry_->RegisterComponents(policy::POLICY_DOMAIN_EXTENSIONS,
226 *components);
228 // The first SetReady() call is performed after the ExtensionSystem is ready,
229 // even if there are no managed extensions. It will trigger a loading of the
230 // initial policy for any managed extensions, and eventually the PolicyService
231 // will become ready for POLICY_DOMAIN_EXTENSIONS, and
232 // OnPolicyServiceInitialized() will be invoked.
233 // Subsequent calls to SetReady() are ignored.
234 schema_registry_->SetReady(policy::POLICY_DOMAIN_EXTENSIONS);
237 ManagedValueStoreCache::ManagedValueStoreCache(
238 BrowserContext* context,
239 const scoped_refptr<SettingsStorageFactory>& factory,
240 const scoped_refptr<SettingsObserverList>& observers)
241 : profile_(Profile::FromBrowserContext(context)),
242 policy_service_(policy::ProfilePolicyConnectorFactory::GetForProfile(
243 profile_)->policy_service()),
244 storage_factory_(factory),
245 observers_(observers),
246 base_path_(profile_->GetPath().AppendASCII(
247 extensions::kManagedSettingsDirectoryName)) {
248 DCHECK_CURRENTLY_ON(BrowserThread::UI);
250 policy_service_->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
252 extension_tracker_.reset(new ExtensionTracker(profile_));
254 if (policy_service_->IsInitializationComplete(
255 policy::POLICY_DOMAIN_EXTENSIONS)) {
256 OnPolicyServiceInitialized(policy::POLICY_DOMAIN_EXTENSIONS);
260 ManagedValueStoreCache::~ManagedValueStoreCache() {
261 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
262 // Delete the PolicyValueStores on FILE.
263 store_map_.clear();
266 void ManagedValueStoreCache::ShutdownOnUI() {
267 DCHECK_CURRENTLY_ON(BrowserThread::UI);
268 policy_service_->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
269 extension_tracker_.reset();
272 void ManagedValueStoreCache::RunWithValueStoreForExtension(
273 const StorageCallback& callback,
274 scoped_refptr<const Extension> extension) {
275 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
276 callback.Run(GetStoreFor(extension->id()));
279 void ManagedValueStoreCache::DeleteStorageSoon(
280 const std::string& extension_id) {
281 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
282 // It's possible that the store exists, but hasn't been loaded yet
283 // (because the extension is unloaded, for example). Open the database to
284 // clear it if it exists.
285 if (!HasStore(extension_id))
286 return;
287 GetStoreFor(extension_id)->DeleteStorage();
288 store_map_.erase(extension_id);
291 void ManagedValueStoreCache::OnPolicyServiceInitialized(
292 policy::PolicyDomain domain) {
293 DCHECK_CURRENTLY_ON(BrowserThread::UI);
295 if (domain != policy::POLICY_DOMAIN_EXTENSIONS)
296 return;
298 // The PolicyService now has all the initial policies ready. Send policy
299 // for all the managed extensions to their backing stores now.
300 policy::SchemaRegistry* registry =
301 policy::SchemaRegistryServiceFactory::GetForContext(profile_);
302 const policy::ComponentMap* map = registry->schema_map()->GetComponents(
303 policy::POLICY_DOMAIN_EXTENSIONS);
304 if (!map)
305 return;
307 const policy::PolicyMap empty_map;
308 for (policy::ComponentMap::const_iterator it = map->begin();
309 it != map->end(); ++it) {
310 const policy::PolicyNamespace ns(policy::POLICY_DOMAIN_EXTENSIONS,
311 it->first);
312 // If there is no policy for |ns| then this will clear the previous store,
313 // if there is one.
314 OnPolicyUpdated(ns, empty_map, policy_service_->GetPolicies(ns));
318 void ManagedValueStoreCache::OnPolicyUpdated(const policy::PolicyNamespace& ns,
319 const policy::PolicyMap& previous,
320 const policy::PolicyMap& current) {
321 DCHECK_CURRENTLY_ON(BrowserThread::UI);
323 if (!policy_service_->IsInitializationComplete(
324 policy::POLICY_DOMAIN_EXTENSIONS)) {
325 // OnPolicyUpdated is called whenever a policy changes, but it doesn't
326 // mean that all the policy providers are ready; wait until we get the
327 // final policy values before passing them to the store.
328 return;
331 BrowserThread::PostTask(
332 BrowserThread::FILE, FROM_HERE,
333 base::Bind(&ManagedValueStoreCache::UpdatePolicyOnFILE,
334 base::Unretained(this),
335 ns.component_id,
336 base::Passed(current.DeepCopy())));
339 void ManagedValueStoreCache::UpdatePolicyOnFILE(
340 const std::string& extension_id,
341 scoped_ptr<policy::PolicyMap> current_policy) {
342 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
344 if (!HasStore(extension_id) && current_policy->empty()) {
345 // Don't create the store now if there are no policies configured for this
346 // extension. If the extension uses the storage.managed API then the store
347 // will be created at RunWithValueStoreForExtension().
348 return;
351 GetStoreFor(extension_id)->SetCurrentPolicy(*current_policy);
354 PolicyValueStore* ManagedValueStoreCache::GetStoreFor(
355 const std::string& extension_id) {
356 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
358 PolicyValueStoreMap::iterator it = store_map_.find(extension_id);
359 if (it != store_map_.end())
360 return it->second.get();
362 // Create the store now, and serve the cached policy until the PolicyService
363 // sends updated values.
364 PolicyValueStore* store = new PolicyValueStore(
365 extension_id,
366 observers_,
367 make_scoped_ptr(storage_factory_->Create(base_path_, extension_id)));
368 store_map_[extension_id] = make_linked_ptr(store);
370 return store;
373 bool ManagedValueStoreCache::HasStore(const std::string& extension_id) const {
374 // TODO(joaodasilva): move this check to a ValueStore method.
375 return base::DirectoryExists(base_path_.AppendASCII(extension_id));
378 } // namespace extensions