Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / storage / managed_value_store_cache.cc
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/storage/managed_value_store_cache.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/scoped_observer.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 "extensions/browser/api/storage/settings_storage_factory.h"
27 #include "extensions/browser/extension_prefs.h"
28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/browser/extension_registry_observer.h"
30 #include "extensions/browser/extension_system.h"
31 #include "extensions/browser/value_store/value_store_change.h"
32 #include "extensions/common/api/storage.h"
33 #include "extensions/common/constants.h"
34 #include "extensions/common/extension.h"
35 #include "extensions/common/extension_set.h"
36 #include "extensions/common/manifest.h"
37 #include "extensions/common/manifest_constants.h"
38 #include "extensions/common/one_shot_event.h"
39
40 using content::BrowserContext;
41 using content::BrowserThread;
42
43 namespace extensions {
44 class ExtensionRegistry;
45
46 namespace storage = core_api::storage;
47
48 namespace {
49
50 const char kLoadSchemasBackgroundTaskTokenName[] =
51     "load_managed_storage_schemas_token";
52
53 // The Legacy Browser Support was the first user of the policy-for-extensions
54 // API, and relied on behavior that will be phased out. If this extension is
55 // present then its policies will be loaded in a special way.
56 // TODO(joaodasilva): remove this for M35. http://crbug.com/325349
57 const char kLegacyBrowserSupportExtensionId[] =
58     "heildphpnddilhkemkielfhnkaagiabh";
59
60 }  // namespace
61
62 // This helper observes initialization of all the installed extensions and
63 // subsequent loads and unloads, and keeps the SchemaRegistry of the Profile
64 // in sync with the current list of extensions. This allows the PolicyService
65 // to fetch cloud policy for those extensions, and allows its providers to
66 // selectively load only extension policy that has users.
67 class ManagedValueStoreCache::ExtensionTracker
68     : public ExtensionRegistryObserver {
69  public:
70   explicit ExtensionTracker(Profile* profile);
71   virtual ~ExtensionTracker() {}
72
73  private:
74   // ExtensionRegistryObserver implementation.
75   virtual void OnExtensionWillBeInstalled(
76       content::BrowserContext* browser_context,
77       const Extension* extension,
78       bool is_update,
79       bool from_ephemeral,
80       const std::string& old_name) OVERRIDE;
81   virtual void OnExtensionUninstalled(
82       content::BrowserContext* browser_context,
83       const Extension* extension,
84       extensions::UninstallReason reason) OVERRIDE;
85
86   // Handler for the signal from ExtensionSystem::ready().
87   void OnExtensionsReady();
88
89   // Starts a schema load for all extensions that use managed storage.
90   void LoadSchemas(scoped_ptr<ExtensionSet> added);
91
92   bool UsesManagedStorage(const Extension* extension) const;
93
94   // Loads the schemas of the |extensions| and passes a ComponentMap to
95   // Register().
96   static void LoadSchemasOnBlockingPool(scoped_ptr<ExtensionSet> extensions,
97                                         base::WeakPtr<ExtensionTracker> self);
98   void Register(const policy::ComponentMap* components);
99
100   Profile* profile_;
101   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
102       extension_registry_observer_;
103   policy::SchemaRegistry* schema_registry_;
104   base::WeakPtrFactory<ExtensionTracker> weak_factory_;
105
106   DISALLOW_COPY_AND_ASSIGN(ExtensionTracker);
107 };
108
109 ManagedValueStoreCache::ExtensionTracker::ExtensionTracker(Profile* profile)
110     : profile_(profile),
111       extension_registry_observer_(this),
112       schema_registry_(policy::SchemaRegistryServiceFactory::GetForContext(
113                            profile)->registry()),
114       weak_factory_(this) {
115   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
116   // Load schemas when the extension system is ready. It might be ready now.
117   ExtensionSystem::Get(profile_)->ready().Post(
118       FROM_HERE,
119       base::Bind(&ExtensionTracker::OnExtensionsReady,
120                  weak_factory_.GetWeakPtr()));
121 }
122
123 void ManagedValueStoreCache::ExtensionTracker::OnExtensionWillBeInstalled(
124     content::BrowserContext* browser_context,
125     const Extension* extension,
126     bool is_update,
127     bool from_ephemeral,
128     const std::string& old_name) {
129   // Some extensions are installed on the first run before the ExtensionSystem
130   // becomes ready. Wait until all of them are ready before registering the
131   // schemas of managed extensions, so that the policy loaders are reloaded at
132   // most once.
133   if (!ExtensionSystem::Get(profile_)->ready().is_signaled())
134     return;
135   scoped_ptr<ExtensionSet> added(new ExtensionSet);
136   added->Insert(extension);
137   LoadSchemas(added.Pass());
138 }
139
140 void ManagedValueStoreCache::ExtensionTracker::OnExtensionUninstalled(
141     content::BrowserContext* browser_context,
142     const Extension* extension,
143     extensions::UninstallReason reason) {
144   if (!ExtensionSystem::Get(profile_)->ready().is_signaled())
145     return;
146   if (extension && UsesManagedStorage(extension)) {
147     schema_registry_->UnregisterComponent(policy::PolicyNamespace(
148         policy::POLICY_DOMAIN_EXTENSIONS, extension->id()));
149   }
150 }
151
152 void ManagedValueStoreCache::ExtensionTracker::OnExtensionsReady() {
153   // Load schemas for all installed extensions.
154   LoadSchemas(
155       ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet());
156 }
157
158 void ManagedValueStoreCache::ExtensionTracker::LoadSchemas(
159     scoped_ptr<ExtensionSet> added) {
160   // Filter out extensions that don't use managed storage.
161   ExtensionSet::const_iterator it = added->begin();
162   while (it != added->end()) {
163     std::string to_remove;
164     if (!UsesManagedStorage(it->get()))
165       to_remove = (*it)->id();
166     ++it;
167     if (!to_remove.empty())
168       added->Remove(to_remove);
169   }
170
171   // Load the schema files in a background thread.
172   BrowserThread::PostBlockingPoolSequencedTask(
173       kLoadSchemasBackgroundTaskTokenName, FROM_HERE,
174       base::Bind(&ExtensionTracker::LoadSchemasOnBlockingPool,
175                  base::Passed(&added),
176                  weak_factory_.GetWeakPtr()));
177 }
178
179 bool ManagedValueStoreCache::ExtensionTracker::UsesManagedStorage(
180     const Extension* extension) const {
181   if (extension->manifest()->HasPath(manifest_keys::kStorageManagedSchema))
182     return true;
183
184   // TODO(joaodasilva): remove this by M35.
185   return extension->id() == kLegacyBrowserSupportExtensionId;
186 }
187
188 // static
189 void ManagedValueStoreCache::ExtensionTracker::LoadSchemasOnBlockingPool(
190     scoped_ptr<ExtensionSet> extensions,
191     base::WeakPtr<ExtensionTracker> self) {
192   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
193   scoped_ptr<policy::ComponentMap> components(new policy::ComponentMap);
194
195   for (ExtensionSet::const_iterator it = extensions->begin();
196        it != extensions->end(); ++it) {
197     std::string schema_file;
198     if (!(*it)->manifest()->GetString(
199             manifest_keys::kStorageManagedSchema, &schema_file)) {
200       // TODO(joaodasilva): Remove this. http://crbug.com/325349
201       (*components)[(*it)->id()] = policy::Schema();
202       continue;
203     }
204     // The extension should have been validated, so assume the schema exists
205     // and is valid.
206     std::string error;
207     policy::Schema schema =
208         StorageSchemaManifestHandler::GetSchema(it->get(), &error);
209     // If the schema is invalid then proceed with an empty schema. The extension
210     // will be listed in chrome://policy but won't be able to load any policies.
211     if (!schema.valid())
212       schema = policy::Schema();
213     (*components)[(*it)->id()] = schema;
214   }
215
216   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
217                           base::Bind(&ExtensionTracker::Register, self,
218                                      base::Owned(components.release())));
219 }
220
221 void ManagedValueStoreCache::ExtensionTracker::Register(
222     const policy::ComponentMap* components) {
223   DCHECK_CURRENTLY_ON(BrowserThread::UI);
224   schema_registry_->RegisterComponents(policy::POLICY_DOMAIN_EXTENSIONS,
225                                        *components);
226
227   // The first SetReady() call is performed after the ExtensionSystem is ready,
228   // even if there are no managed extensions. It will trigger a loading of the
229   // initial policy for any managed extensions, and eventually the PolicyService
230   // will become ready for POLICY_DOMAIN_EXTENSIONS, and
231   // OnPolicyServiceInitialized() will be invoked.
232   // Subsequent calls to SetReady() are ignored.
233   schema_registry_->SetReady(policy::POLICY_DOMAIN_EXTENSIONS);
234 }
235
236 ManagedValueStoreCache::ManagedValueStoreCache(
237     BrowserContext* context,
238     const scoped_refptr<SettingsStorageFactory>& factory,
239     const scoped_refptr<SettingsObserverList>& observers)
240     : profile_(Profile::FromBrowserContext(context)),
241       policy_service_(policy::ProfilePolicyConnectorFactory::GetForProfile(
242                           profile_)->policy_service()),
243       storage_factory_(factory),
244       observers_(observers),
245       base_path_(profile_->GetPath().AppendASCII(
246           extensions::kManagedSettingsDirectoryName)) {
247   DCHECK_CURRENTLY_ON(BrowserThread::UI);
248
249   policy_service_->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
250
251   extension_tracker_.reset(new ExtensionTracker(profile_));
252
253   if (policy_service_->IsInitializationComplete(
254           policy::POLICY_DOMAIN_EXTENSIONS)) {
255     OnPolicyServiceInitialized(policy::POLICY_DOMAIN_EXTENSIONS);
256   }
257 }
258
259 ManagedValueStoreCache::~ManagedValueStoreCache() {
260   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
261   // Delete the PolicyValueStores on FILE.
262   store_map_.clear();
263 }
264
265 void ManagedValueStoreCache::ShutdownOnUI() {
266   DCHECK_CURRENTLY_ON(BrowserThread::UI);
267   policy_service_->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
268   extension_tracker_.reset();
269 }
270
271 void ManagedValueStoreCache::RunWithValueStoreForExtension(
272     const StorageCallback& callback,
273     scoped_refptr<const Extension> extension) {
274   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
275   callback.Run(GetStoreFor(extension->id()));
276 }
277
278 void ManagedValueStoreCache::DeleteStorageSoon(
279     const std::string& extension_id) {
280   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
281   // It's possible that the store exists, but hasn't been loaded yet
282   // (because the extension is unloaded, for example). Open the database to
283   // clear it if it exists.
284   if (!HasStore(extension_id))
285     return;
286   GetStoreFor(extension_id)->DeleteStorage();
287   store_map_.erase(extension_id);
288 }
289
290 void ManagedValueStoreCache::OnPolicyServiceInitialized(
291     policy::PolicyDomain domain) {
292   DCHECK_CURRENTLY_ON(BrowserThread::UI);
293
294   if (domain != policy::POLICY_DOMAIN_EXTENSIONS)
295     return;
296
297   // The PolicyService now has all the initial policies ready. Send policy
298   // for all the managed extensions to their backing stores now.
299   policy::SchemaRegistry* registry =
300       policy::SchemaRegistryServiceFactory::GetForContext(profile_)->registry();
301   const policy::ComponentMap* map = registry->schema_map()->GetComponents(
302       policy::POLICY_DOMAIN_EXTENSIONS);
303   if (!map)
304     return;
305
306   const policy::PolicyMap empty_map;
307   for (policy::ComponentMap::const_iterator it = map->begin();
308        it != map->end(); ++it) {
309     const policy::PolicyNamespace ns(policy::POLICY_DOMAIN_EXTENSIONS,
310                                      it->first);
311     // If there is no policy for |ns| then this will clear the previous store,
312     // if there is one.
313     OnPolicyUpdated(ns, empty_map, policy_service_->GetPolicies(ns));
314   }
315 }
316
317 void ManagedValueStoreCache::OnPolicyUpdated(const policy::PolicyNamespace& ns,
318                                              const policy::PolicyMap& previous,
319                                              const policy::PolicyMap& current) {
320   DCHECK_CURRENTLY_ON(BrowserThread::UI);
321
322   if (!policy_service_->IsInitializationComplete(
323            policy::POLICY_DOMAIN_EXTENSIONS)) {
324     // OnPolicyUpdated is called whenever a policy changes, but it doesn't
325     // mean that all the policy providers are ready; wait until we get the
326     // final policy values before passing them to the store.
327     return;
328   }
329
330   BrowserThread::PostTask(
331       BrowserThread::FILE, FROM_HERE,
332       base::Bind(&ManagedValueStoreCache::UpdatePolicyOnFILE,
333                  base::Unretained(this),
334                  ns.component_id,
335                  base::Passed(current.DeepCopy())));
336 }
337
338 void ManagedValueStoreCache::UpdatePolicyOnFILE(
339     const std::string& extension_id,
340     scoped_ptr<policy::PolicyMap> current_policy) {
341   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
342
343   if (!HasStore(extension_id) && current_policy->empty()) {
344     // Don't create the store now if there are no policies configured for this
345     // extension. If the extension uses the storage.managed API then the store
346     // will be created at RunWithValueStoreForExtension().
347     return;
348   }
349
350   GetStoreFor(extension_id)->SetCurrentPolicy(*current_policy);
351 }
352
353 PolicyValueStore* ManagedValueStoreCache::GetStoreFor(
354     const std::string& extension_id) {
355   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
356
357   PolicyValueStoreMap::iterator it = store_map_.find(extension_id);
358   if (it != store_map_.end())
359     return it->second.get();
360
361   // Create the store now, and serve the cached policy until the PolicyService
362   // sends updated values.
363   PolicyValueStore* store = new PolicyValueStore(
364       extension_id,
365       observers_,
366       make_scoped_ptr(storage_factory_->Create(base_path_, extension_id)));
367   store_map_[extension_id] = make_linked_ptr(store);
368
369   return store;
370 }
371
372 bool ManagedValueStoreCache::HasStore(const std::string& extension_id) const {
373   // TODO(joaodasilva): move this check to a ValueStore method.
374   return base::DirectoryExists(base_path_.AppendASCII(extension_id));
375 }
376
377 }  // namespace extensions