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"
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
;
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";
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
{
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
;
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
91 static void LoadSchemasOnBlockingPool(scoped_ptr
<ExtensionSet
> extensions
,
92 base::WeakPtr
<ExtensionTracker
> self
);
93 void Register(const policy::ComponentMap
* components
);
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
)
106 policy::SchemaRegistryServiceFactory::GetForContext(profile
)),
107 weak_factory_(this) {
109 chrome::NOTIFICATION_EXTENSION_INSTALLED
,
110 content::Source
<Profile
>(profile_
));
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(
118 base::Bind(&ExtensionTracker::OnExtensionsReady
,
119 weak_factory_
.GetWeakPtr()));
122 void ManagedValueStoreCache::ExtensionTracker::Observe(
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
130 if (!ExtensionSystem::Get(profile_
)->ready().is_signaled())
134 case chrome::NOTIFICATION_EXTENSION_INSTALLED
: {
135 scoped_ptr
<ExtensionSet
> added(new ExtensionSet
);
137 content::Details
<InstalledExtensionInfo
>(details
)->extension
);
138 LoadSchemas(added
.Pass());
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()));
156 void ManagedValueStoreCache::ExtensionTracker::OnExtensionsReady() {
157 // Load schemas for all installed extensions.
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();
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
))
188 // TODO(joaodasilva): remove this by M35.
189 return extension
->id() == kLegacyBrowserSupportExtensionId
;
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();
208 // The extension should have been validated, so assume the schema exists
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
,
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.
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
))
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
)
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
);
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
,
312 // If there is no policy for |ns| then this will clear the previous store,
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.
331 BrowserThread::PostTask(
332 BrowserThread::FILE, FROM_HERE
,
333 base::Bind(&ManagedValueStoreCache::UpdatePolicyOnFILE
,
334 base::Unretained(this),
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().
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(
367 make_scoped_ptr(storage_factory_
->Create(base_path_
, extension_id
)));
368 store_map_
[extension_id
] = make_linked_ptr(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