1 // Copyright 2014 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 #ifndef EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
6 #define EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
10 #include "base/containers/hash_tables.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/linked_ptr.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/scoped_observer.h"
15 #include "base/threading/non_thread_safe.h"
16 #include "components/keyed_service/core/keyed_service.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/notification_observer.h"
19 #include "content/public/browser/notification_registrar.h"
20 #include "content/public/browser/notification_service.h"
21 #include "extensions/browser/browser_context_keyed_api_factory.h"
22 #include "extensions/browser/extension_host.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_registry_observer.h"
25 #include "extensions/browser/notification_types.h"
26 #include "extensions/common/extension.h"
28 namespace extensions
{
31 class BluetoothSocketApiFunction
;
32 class BluetoothSocketEventDispatcher
;
36 class SerialEventDispatcher
;
37 class TCPServerSocketEventDispatcher
;
38 class TCPSocketEventDispatcher
;
39 class UDPSocketEventDispatcher
;
43 struct NamedThreadTraits
{
44 static bool IsCalledOnValidThread() {
45 return content::BrowserThread::CurrentlyOn(T::kThreadId
);
48 static bool IsMessageLoopValid() {
49 return content::BrowserThread::IsMessageLoopValid(T::kThreadId
);
52 static scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
53 return content::BrowserThread::GetMessageLoopProxyForThread(T::kThreadId
);
58 struct TestThreadTraits
{
59 static bool IsCalledOnValidThread() {
60 return content::BrowserThread::CurrentlyOn(thread_id_
);
63 static bool IsMessageLoopValid() {
64 return content::BrowserThread::IsMessageLoopValid(thread_id_
);
67 static scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
68 return content::BrowserThread::GetMessageLoopProxyForThread(thread_id_
);
71 static content::BrowserThread::ID thread_id_
;
75 content::BrowserThread::ID TestThreadTraits
<T
>::thread_id_
=
76 content::BrowserThread::IO
;
78 // An ApiResourceManager manages the lifetime of a set of resources that
79 // that live on named threads (i.e. BrowserThread::IO) which ApiFunctions use.
80 // Examples of such resources are sockets or USB connections.
82 // Users of this class should define kThreadId to be the thread that
83 // ApiResourceManager to works on. The default is defined in ApiResource.
84 // The user must also define a static const char* service_name() that returns
85 // the name of the service, and in order for ApiResourceManager to use
86 // service_name() friend this class.
88 // In the cc file the user must define a GetFactoryInstance() and manage their
89 // own instances (typically using LazyInstance or Singleton).
95 // static const BrowserThread::ID kThreadId = BrowserThread::FILE;
97 // friend class ApiResourceManager<Resource>;
98 // static const char* service_name() {
99 // return "ResourceManager";
105 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
106 // ApiResourceManager<Resource> > >
107 // g_factory = LAZY_INSTANCE_INITIALIZER;
111 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
112 // ApiResourceManager<Resource>::GetFactoryInstance() {
113 // return g_factory.Pointer();
115 template <class T
, typename ThreadingTraits
= NamedThreadTraits
<T
> >
116 class ApiResourceManager
: public BrowserContextKeyedAPI
,
117 public base::NonThreadSafe
,
118 public content::NotificationObserver
,
119 public ExtensionRegistryObserver
{
121 explicit ApiResourceManager(content::BrowserContext
* context
)
122 : data_(new ApiResourceData()), extension_registry_observer_(this) {
123 extension_registry_observer_
.Add(ExtensionRegistry::Get(context
));
125 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
,
126 content::NotificationService::AllSources());
129 static ApiResourceManager
<T
, TestThreadTraits
<T
> >*
130 CreateApiResourceManagerForTest(content::BrowserContext
* context
,
131 content::BrowserThread::ID thread_id
) {
132 TestThreadTraits
<T
>::thread_id_
= thread_id
;
133 ApiResourceManager
<T
, TestThreadTraits
<T
> >* manager
=
134 new ApiResourceManager
<T
, TestThreadTraits
<T
> >(context
);
138 virtual ~ApiResourceManager() {
139 DCHECK(CalledOnValidThread());
140 DCHECK(ThreadingTraits::IsMessageLoopValid())
141 << "A unit test is using an ApiResourceManager but didn't provide "
142 "the thread message loop needed for that kind of resource. "
143 "Please ensure that the appropriate message loop is operational.";
145 data_
->InititateCleanup();
149 int Add(T
* api_resource
) { return data_
->Add(api_resource
); }
151 void Remove(const std::string
& extension_id
, int api_resource_id
) {
152 data_
->Remove(extension_id
, api_resource_id
);
155 T
* Get(const std::string
& extension_id
, int api_resource_id
) {
156 return data_
->Get(extension_id
, api_resource_id
);
159 base::hash_set
<int>* GetResourceIds(const std::string
& extension_id
) {
160 return data_
->GetResourceIds(extension_id
);
163 // BrowserContextKeyedAPI implementation.
164 static BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >*
165 GetFactoryInstance();
167 // Convenience method to get the ApiResourceManager for a profile.
168 static ApiResourceManager
<T
>* Get(content::BrowserContext
* context
) {
169 return BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >::Get(context
);
172 // BrowserContextKeyedAPI implementation.
173 static const char* service_name() { return T::service_name(); }
175 // Change the resource mapped to this |extension_id| at this
176 // |api_resource_id| to |resource|. Returns true and succeeds unless
177 // |api_resource_id| does not already identify a resource held by
179 bool Replace(const std::string
& extension_id
,
182 return data_
->Replace(extension_id
, api_resource_id
, resource
);
186 // content::NotificationObserver:
187 virtual void Observe(int type
,
188 const content::NotificationSource
& source
,
189 const content::NotificationDetails
& details
) OVERRIDE
{
190 DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED
, type
);
191 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
192 data_
->InitiateExtensionSuspendedCleanup(host
->extension_id());
195 // ExtensionRegistryObserver:
196 virtual void OnExtensionUnloaded(
197 content::BrowserContext
* browser_context
,
198 const Extension
* extension
,
199 UnloadedExtensionInfo::Reason reason
) OVERRIDE
{
200 data_
->InitiateExtensionUnloadedCleanup(extension
->id());
204 // TODO(rockot): ApiResourceData could be moved out of ApiResourceManager and
205 // we could avoid maintaining a friends list here.
206 friend class BluetoothAPI
;
207 friend class api::BluetoothSocketApiFunction
;
208 friend class api::BluetoothSocketEventDispatcher
;
209 friend class core_api::SerialEventDispatcher
;
210 friend class core_api::TCPServerSocketEventDispatcher
;
211 friend class core_api::TCPSocketEventDispatcher
;
212 friend class core_api::UDPSocketEventDispatcher
;
213 friend class BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >;
215 static const bool kServiceHasOwnInstanceInIncognito
= true;
216 static const bool kServiceIsNULLWhileTesting
= true;
218 // ApiResourceData class handles resource bookkeeping on a thread
219 // where resource lifetime is handled.
220 class ApiResourceData
: public base::RefCountedThreadSafe
<ApiResourceData
> {
222 typedef std::map
<int, linked_ptr
<T
> > ApiResourceMap
;
223 // Lookup map from extension id's to allocated resource id's.
224 typedef std::map
<std::string
, base::hash_set
<int> > ExtensionToResourceMap
;
226 ApiResourceData() : next_id_(1) {}
228 int Add(T
* api_resource
) {
229 DCHECK(ThreadingTraits::IsCalledOnValidThread());
230 int id
= GenerateId();
232 linked_ptr
<T
> resource_ptr(api_resource
);
233 api_resource_map_
[id
] = resource_ptr
;
235 const std::string
& extension_id
= api_resource
->owner_extension_id();
236 if (extension_resource_map_
.find(extension_id
) ==
237 extension_resource_map_
.end()) {
238 extension_resource_map_
[extension_id
] = base::hash_set
<int>();
240 extension_resource_map_
[extension_id
].insert(id
);
247 void Remove(const std::string
& extension_id
, int api_resource_id
) {
248 DCHECK(ThreadingTraits::IsCalledOnValidThread());
249 if (GetOwnedResource(extension_id
, api_resource_id
) != NULL
) {
250 DCHECK(extension_resource_map_
.find(extension_id
) !=
251 extension_resource_map_
.end());
252 extension_resource_map_
[extension_id
].erase(api_resource_id
);
253 api_resource_map_
.erase(api_resource_id
);
257 T
* Get(const std::string
& extension_id
, int api_resource_id
) {
258 DCHECK(ThreadingTraits::IsCalledOnValidThread());
259 return GetOwnedResource(extension_id
, api_resource_id
);
262 // Change the resource mapped to this |extension_id| at this
263 // |api_resource_id| to |resource|. Returns true and succeeds unless
264 // |api_resource_id| does not already identify a resource held by
266 bool Replace(const std::string
& extension_id
,
269 DCHECK(ThreadingTraits::IsCalledOnValidThread());
270 T
* old_resource
= api_resource_map_
[api_resource_id
].get();
271 if (old_resource
&& extension_id
== old_resource
->owner_extension_id()) {
272 api_resource_map_
[api_resource_id
] = linked_ptr
<T
>(api_resource
);
278 base::hash_set
<int>* GetResourceIds(const std::string
& extension_id
) {
279 DCHECK(ThreadingTraits::IsCalledOnValidThread());
280 return GetOwnedResourceIds(extension_id
);
283 void InitiateExtensionUnloadedCleanup(const std::string
& extension_id
) {
284 if (ThreadingTraits::IsCalledOnValidThread()) {
285 CleanupResourcesFromUnloadedExtension(extension_id
);
287 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
289 base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension
,
295 void InitiateExtensionSuspendedCleanup(const std::string
& extension_id
) {
296 if (ThreadingTraits::IsCalledOnValidThread()) {
297 CleanupResourcesFromSuspendedExtension(extension_id
);
299 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
301 base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension
,
307 void InititateCleanup() {
308 if (ThreadingTraits::IsCalledOnValidThread()) {
311 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
312 FROM_HERE
, base::Bind(&ApiResourceData::Cleanup
, this));
317 friend class base::RefCountedThreadSafe
<ApiResourceData
>;
319 virtual ~ApiResourceData() {}
321 T
* GetOwnedResource(const std::string
& extension_id
, int api_resource_id
) {
322 linked_ptr
<T
> ptr
= api_resource_map_
[api_resource_id
];
323 T
* resource
= ptr
.get();
324 if (resource
&& extension_id
== resource
->owner_extension_id())
329 base::hash_set
<int>* GetOwnedResourceIds(const std::string
& extension_id
) {
330 DCHECK(ThreadingTraits::IsCalledOnValidThread());
331 if (extension_resource_map_
.find(extension_id
) ==
332 extension_resource_map_
.end())
335 return &extension_resource_map_
[extension_id
];
338 void CleanupResourcesFromUnloadedExtension(
339 const std::string
& extension_id
) {
340 CleanupResourcesFromExtension(extension_id
, true);
343 void CleanupResourcesFromSuspendedExtension(
344 const std::string
& extension_id
) {
345 CleanupResourcesFromExtension(extension_id
, false);
348 void CleanupResourcesFromExtension(const std::string
& extension_id
,
350 DCHECK(ThreadingTraits::IsCalledOnValidThread());
352 if (extension_resource_map_
.find(extension_id
) ==
353 extension_resource_map_
.end()) {
357 // Remove all resources, or the non persistent ones only if |remove_all|
359 base::hash_set
<int>& resource_ids
= extension_resource_map_
[extension_id
];
360 for (base::hash_set
<int>::iterator it
= resource_ids
.begin();
361 it
!= resource_ids
.end();) {
366 linked_ptr
<T
> ptr
= api_resource_map_
[*it
];
367 T
* resource
= ptr
.get();
368 erase
= (resource
&& !resource
->IsPersistent());
372 api_resource_map_
.erase(*it
);
373 resource_ids
.erase(it
++);
379 // Remove extension entry if we removed all its resources.
380 if (resource_ids
.size() == 0) {
381 extension_resource_map_
.erase(extension_id
);
386 DCHECK(ThreadingTraits::IsCalledOnValidThread());
388 api_resource_map_
.clear();
389 extension_resource_map_
.clear();
392 int GenerateId() { return next_id_
++; }
395 ApiResourceMap api_resource_map_
;
396 ExtensionToResourceMap extension_resource_map_
;
399 content::NotificationRegistrar registrar_
;
400 scoped_refptr
<ApiResourceData
> data_
;
402 ScopedObserver
<ExtensionRegistry
, ExtensionRegistryObserver
>
403 extension_registry_observer_
;
406 // With WorkerPoolThreadTraits, ApiResourceManager can be used to manage the
407 // lifetime of a set of resources that live on sequenced task runner threads
408 // which ApiFunctions use. Examples of such resources are temporary file
409 // resources produced by certain API calls.
411 // Instead of kThreadId. classes used for tracking such resources should define
412 // kSequenceToken and kShutdownBehavior to identify sequence task runner for
413 // ApiResourceManager to work on and how pending tasks should behave on
415 // The user must also define a static const char* service_name() that returns
416 // the name of the service, and in order for ApiWorkerPoolResourceManager to use
417 // service_name() friend this class.
419 // In the cc file the user must define a GetFactoryInstance() and manage their
420 // own instances (typically using LazyInstance or Singleton).
424 // class PoolResource {
426 // static const char kSequenceToken[] = "temp_files";
427 // static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior =
428 // base::SequencedWorkerPool::BLOCK_SHUTDOWN;
430 // friend class ApiResourceManager<WorkerPoolResource,
431 // WorkerPoolThreadTraits>;
432 // static const char* service_name() {
433 // return "TempFilesResourceManager";
439 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
440 // ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
441 // g_factory = LAZY_INSTANCE_INITIALIZER;
445 // BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >*
446 // ApiResourceManager<WorkerPoolPoolResource,
447 // WorkerPoolThreadTraits>::GetFactoryInstance() {
448 // return g_factory.Pointer();
450 template <typename T
>
451 struct WorkerPoolThreadTraits
{
452 static bool IsCalledOnValidThread() {
453 return content::BrowserThread::GetBlockingPool()
454 ->IsRunningSequenceOnCurrentThread(
455 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
459 static bool IsMessageLoopValid() {
460 return content::BrowserThread::GetBlockingPool() != NULL
;
463 static scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
464 return content::BrowserThread::GetBlockingPool()
465 ->GetSequencedTaskRunnerWithShutdownBehavior(
466 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
468 T::kShutdownBehavior
);
472 } // namespace extensions
474 #endif // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_