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/memory/linked_ptr.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.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 "extensions/browser/browser_context_keyed_api_factory.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_registry_observer.h"
21 #include "extensions/browser/process_manager.h"
22 #include "extensions/browser/process_manager_observer.h"
23 #include "extensions/common/extension.h"
25 namespace extensions
{
28 class BluetoothSocketApiFunction
;
29 class BluetoothSocketEventDispatcher
;
30 class SerialEventDispatcher
;
31 class TCPServerSocketEventDispatcher
;
32 class TCPSocketEventDispatcher
;
33 class UDPSocketEventDispatcher
;
37 struct NamedThreadTraits
{
38 static bool IsCalledOnValidThread() {
39 return content::BrowserThread::CurrentlyOn(T::kThreadId
);
42 static bool IsMessageLoopValid() {
43 return content::BrowserThread::IsMessageLoopValid(T::kThreadId
);
46 static scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
47 return content::BrowserThread::GetMessageLoopProxyForThread(T::kThreadId
);
52 struct TestThreadTraits
{
53 static bool IsCalledOnValidThread() {
54 return content::BrowserThread::CurrentlyOn(thread_id_
);
57 static bool IsMessageLoopValid() {
58 return content::BrowserThread::IsMessageLoopValid(thread_id_
);
61 static scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
62 return content::BrowserThread::GetMessageLoopProxyForThread(thread_id_
);
65 static content::BrowserThread::ID thread_id_
;
69 content::BrowserThread::ID TestThreadTraits
<T
>::thread_id_
=
70 content::BrowserThread::IO
;
72 // An ApiResourceManager manages the lifetime of a set of resources that
73 // that live on named threads (i.e. BrowserThread::IO) which ApiFunctions use.
74 // Examples of such resources are sockets or USB connections.
76 // Users of this class should define kThreadId to be the thread that
77 // ApiResourceManager to works on. The default is defined in ApiResource.
78 // The user must also define a static const char* service_name() that returns
79 // the name of the service, and in order for ApiResourceManager to use
80 // service_name() friend this class.
82 // In the cc file the user must define a GetFactoryInstance() and manage their
83 // own instances (typically using LazyInstance or Singleton).
89 // static const BrowserThread::ID kThreadId = BrowserThread::FILE;
91 // friend class ApiResourceManager<Resource>;
92 // static const char* service_name() {
93 // return "ResourceManager";
99 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
100 // ApiResourceManager<Resource> > >
101 // g_factory = LAZY_INSTANCE_INITIALIZER;
105 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
106 // ApiResourceManager<Resource>::GetFactoryInstance() {
107 // return g_factory.Pointer();
109 template <class T
, typename ThreadingTraits
= NamedThreadTraits
<T
>>
110 class ApiResourceManager
: public BrowserContextKeyedAPI
,
111 public base::NonThreadSafe
,
112 public ExtensionRegistryObserver
,
113 public ProcessManagerObserver
{
115 explicit ApiResourceManager(content::BrowserContext
* context
)
116 : data_(new ApiResourceData()),
117 extension_registry_observer_(this),
118 process_manager_observer_(this) {
119 extension_registry_observer_
.Add(ExtensionRegistry::Get(context
));
120 process_manager_observer_
.Add(ProcessManager::Get(context
));
123 static scoped_ptr
<ApiResourceManager
<T
, TestThreadTraits
<T
>>>
124 CreateApiResourceManagerForTest(content::BrowserContext
* context
,
125 content::BrowserThread::ID thread_id
) {
126 TestThreadTraits
<T
>::thread_id_
= thread_id
;
127 return make_scoped_ptr(
128 new ApiResourceManager
<T
, TestThreadTraits
<T
>>(context
));
131 virtual ~ApiResourceManager() {
132 DCHECK(CalledOnValidThread());
133 DCHECK(ThreadingTraits::IsMessageLoopValid())
134 << "A unit test is using an ApiResourceManager but didn't provide "
135 "the thread message loop needed for that kind of resource. "
136 "Please ensure that the appropriate message loop is operational.";
138 data_
->InititateCleanup();
142 int Add(T
* api_resource
) { return data_
->Add(api_resource
); }
144 void Remove(const std::string
& extension_id
, int api_resource_id
) {
145 data_
->Remove(extension_id
, api_resource_id
);
148 T
* Get(const std::string
& extension_id
, int api_resource_id
) {
149 return data_
->Get(extension_id
, api_resource_id
);
152 base::hash_set
<int>* GetResourceIds(const std::string
& extension_id
) {
153 return data_
->GetResourceIds(extension_id
);
156 // BrowserContextKeyedAPI implementation.
157 static BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >*
158 GetFactoryInstance();
160 // Convenience method to get the ApiResourceManager for a profile.
161 static ApiResourceManager
<T
>* Get(content::BrowserContext
* context
) {
162 return BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >::Get(context
);
165 // BrowserContextKeyedAPI implementation.
166 static const char* service_name() { return T::service_name(); }
168 // Change the resource mapped to this |extension_id| at this
169 // |api_resource_id| to |resource|. Returns true and succeeds unless
170 // |api_resource_id| does not already identify a resource held by
172 bool Replace(const std::string
& extension_id
,
175 return data_
->Replace(extension_id
, api_resource_id
, resource
);
179 // ProcessManagerObserver:
180 void OnBackgroundHostClose(const std::string
& extension_id
) override
{
181 data_
->InitiateExtensionSuspendedCleanup(extension_id
);
184 // ExtensionRegistryObserver:
185 void OnExtensionUnloaded(content::BrowserContext
* browser_context
,
186 const Extension
* extension
,
187 UnloadedExtensionInfo::Reason reason
) override
{
188 data_
->InitiateExtensionUnloadedCleanup(extension
->id());
192 // TODO(rockot): ApiResourceData could be moved out of ApiResourceManager and
193 // we could avoid maintaining a friends list here.
194 friend class BluetoothAPI
;
195 friend class core_api::BluetoothSocketApiFunction
;
196 friend class core_api::BluetoothSocketEventDispatcher
;
197 friend class core_api::SerialEventDispatcher
;
198 friend class core_api::TCPServerSocketEventDispatcher
;
199 friend class core_api::TCPSocketEventDispatcher
;
200 friend class core_api::UDPSocketEventDispatcher
;
201 friend class BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >;
203 static const bool kServiceHasOwnInstanceInIncognito
= true;
204 static const bool kServiceIsNULLWhileTesting
= true;
206 // ApiResourceData class handles resource bookkeeping on a thread
207 // where resource lifetime is handled.
208 class ApiResourceData
: public base::RefCountedThreadSafe
<ApiResourceData
> {
210 typedef std::map
<int, linked_ptr
<T
> > ApiResourceMap
;
211 // Lookup map from extension id's to allocated resource id's.
212 typedef std::map
<std::string
, base::hash_set
<int> > ExtensionToResourceMap
;
214 ApiResourceData() : next_id_(1) {}
216 int Add(T
* api_resource
) {
217 DCHECK(ThreadingTraits::IsCalledOnValidThread());
218 int id
= GenerateId();
220 linked_ptr
<T
> resource_ptr(api_resource
);
221 api_resource_map_
[id
] = resource_ptr
;
223 const std::string
& extension_id
= api_resource
->owner_extension_id();
224 ExtensionToResourceMap::iterator it
=
225 extension_resource_map_
.find(extension_id
);
226 if (it
== extension_resource_map_
.end()) {
227 it
= extension_resource_map_
.insert(
228 std::make_pair(extension_id
, base::hash_set
<int>())).first
;
230 it
->second
.insert(id
);
236 void Remove(const std::string
& extension_id
, int api_resource_id
) {
237 DCHECK(ThreadingTraits::IsCalledOnValidThread());
238 if (GetOwnedResource(extension_id
, api_resource_id
)) {
239 ExtensionToResourceMap::iterator it
=
240 extension_resource_map_
.find(extension_id
);
241 it
->second
.erase(api_resource_id
);
242 api_resource_map_
.erase(api_resource_id
);
246 T
* Get(const std::string
& extension_id
, int api_resource_id
) {
247 DCHECK(ThreadingTraits::IsCalledOnValidThread());
248 return GetOwnedResource(extension_id
, api_resource_id
);
251 // Change the resource mapped to this |extension_id| at this
252 // |api_resource_id| to |resource|. Returns true and succeeds unless
253 // |api_resource_id| does not already identify a resource held by
255 bool Replace(const std::string
& extension_id
,
258 DCHECK(ThreadingTraits::IsCalledOnValidThread());
259 T
* old_resource
= api_resource_map_
[api_resource_id
].get();
260 if (old_resource
&& extension_id
== old_resource
->owner_extension_id()) {
261 api_resource_map_
[api_resource_id
] = linked_ptr
<T
>(api_resource
);
267 base::hash_set
<int>* GetResourceIds(const std::string
& extension_id
) {
268 DCHECK(ThreadingTraits::IsCalledOnValidThread());
269 return GetOwnedResourceIds(extension_id
);
272 void InitiateExtensionUnloadedCleanup(const std::string
& extension_id
) {
273 if (ThreadingTraits::IsCalledOnValidThread()) {
274 CleanupResourcesFromUnloadedExtension(extension_id
);
276 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
278 base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension
,
284 void InitiateExtensionSuspendedCleanup(const std::string
& extension_id
) {
285 if (ThreadingTraits::IsCalledOnValidThread()) {
286 CleanupResourcesFromSuspendedExtension(extension_id
);
288 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
290 base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension
,
296 void InititateCleanup() {
297 if (ThreadingTraits::IsCalledOnValidThread()) {
300 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
301 FROM_HERE
, base::Bind(&ApiResourceData::Cleanup
, this));
306 friend class base::RefCountedThreadSafe
<ApiResourceData
>;
308 virtual ~ApiResourceData() {}
310 T
* GetOwnedResource(const std::string
& extension_id
, int api_resource_id
) {
311 linked_ptr
<T
> ptr
= api_resource_map_
[api_resource_id
];
312 T
* resource
= ptr
.get();
313 if (resource
&& extension_id
== resource
->owner_extension_id())
318 base::hash_set
<int>* GetOwnedResourceIds(const std::string
& extension_id
) {
319 DCHECK(ThreadingTraits::IsCalledOnValidThread());
320 ExtensionToResourceMap::iterator it
=
321 extension_resource_map_
.find(extension_id
);
322 if (it
== extension_resource_map_
.end())
324 return &(it
->second
);
327 void CleanupResourcesFromUnloadedExtension(
328 const std::string
& extension_id
) {
329 CleanupResourcesFromExtension(extension_id
, true);
332 void CleanupResourcesFromSuspendedExtension(
333 const std::string
& extension_id
) {
334 CleanupResourcesFromExtension(extension_id
, false);
337 void CleanupResourcesFromExtension(const std::string
& extension_id
,
339 DCHECK(ThreadingTraits::IsCalledOnValidThread());
341 ExtensionToResourceMap::iterator it
=
342 extension_resource_map_
.find(extension_id
);
343 if (it
== extension_resource_map_
.end())
346 // Remove all resources, or the non persistent ones only if |remove_all|
348 base::hash_set
<int>& resource_ids
= it
->second
;
349 for (base::hash_set
<int>::iterator it
= resource_ids
.begin();
350 it
!= resource_ids
.end();) {
355 linked_ptr
<T
> ptr
= api_resource_map_
[*it
];
356 T
* resource
= ptr
.get();
357 erase
= (resource
&& !resource
->IsPersistent());
361 api_resource_map_
.erase(*it
);
362 resource_ids
.erase(it
++);
368 // Remove extension entry if we removed all its resources.
369 if (resource_ids
.size() == 0) {
370 extension_resource_map_
.erase(extension_id
);
375 DCHECK(ThreadingTraits::IsCalledOnValidThread());
377 api_resource_map_
.clear();
378 extension_resource_map_
.clear();
381 int GenerateId() { return next_id_
++; }
384 ApiResourceMap api_resource_map_
;
385 ExtensionToResourceMap extension_resource_map_
;
388 content::NotificationRegistrar registrar_
;
389 scoped_refptr
<ApiResourceData
> data_
;
391 ScopedObserver
<ExtensionRegistry
, ExtensionRegistryObserver
>
392 extension_registry_observer_
;
393 ScopedObserver
<ProcessManager
, ProcessManagerObserver
>
394 process_manager_observer_
;
397 // With WorkerPoolThreadTraits, ApiResourceManager can be used to manage the
398 // lifetime of a set of resources that live on sequenced task runner threads
399 // which ApiFunctions use. Examples of such resources are temporary file
400 // resources produced by certain API calls.
402 // Instead of kThreadId. classes used for tracking such resources should define
403 // kSequenceToken and kShutdownBehavior to identify sequence task runner for
404 // ApiResourceManager to work on and how pending tasks should behave on
406 // The user must also define a static const char* service_name() that returns
407 // the name of the service, and in order for ApiWorkerPoolResourceManager to use
408 // service_name() friend this class.
410 // In the cc file the user must define a GetFactoryInstance() and manage their
411 // own instances (typically using LazyInstance or Singleton).
415 // class PoolResource {
417 // static const char kSequenceToken[] = "temp_files";
418 // static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior =
419 // base::SequencedWorkerPool::BLOCK_SHUTDOWN;
421 // friend class ApiResourceManager<WorkerPoolResource,
422 // WorkerPoolThreadTraits>;
423 // static const char* service_name() {
424 // return "TempFilesResourceManager";
430 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
431 // ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
432 // g_factory = LAZY_INSTANCE_INITIALIZER;
436 // BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >*
437 // ApiResourceManager<WorkerPoolPoolResource,
438 // WorkerPoolThreadTraits>::GetFactoryInstance() {
439 // return g_factory.Pointer();
441 template <typename T
>
442 struct WorkerPoolThreadTraits
{
443 static bool IsCalledOnValidThread() {
444 return content::BrowserThread::GetBlockingPool()
445 ->IsRunningSequenceOnCurrentThread(
446 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
450 static bool IsMessageLoopValid() {
451 return content::BrowserThread::GetBlockingPool() != NULL
;
454 static scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
455 return content::BrowserThread::GetBlockingPool()
456 ->GetSequencedTaskRunnerWithShutdownBehavior(
457 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
459 T::kShutdownBehavior
);
463 } // namespace extensions
465 #endif // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_