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/threading/non_thread_safe.h"
15 #include "chrome/browser/chrome_notification_types.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/common/extension.h"
25 namespace extensions
{
28 class BluetoothSocketApiFunction
;
29 class BluetoothSocketEventDispatcher
;
30 class SerialEventDispatcher
;
34 class TCPServerSocketEventDispatcher
;
35 class TCPSocketEventDispatcher
;
36 class UDPSocketEventDispatcher
;
39 // An ApiResourceManager manages the lifetime of a set of resources that
40 // ApiFunctions use. Examples are sockets or USB connections.
42 // Users of this class should define kThreadId to be the thread that
43 // ApiResourceManager to works on. The default is defined in ApiResource.
44 // The user must also define a static const char* service_name() that returns
45 // the name of the service, and in order for ApiResourceManager to use
46 // service_name() friend this class.
48 // In the cc file the user must define a GetFactoryInstance() and manage their
49 // own instances (typically using LazyInstance or Singleton).
55 // static const BrowserThread::ID kThreadId = BrowserThread::FILE;
57 // friend class ApiResourceManager<Resource>;
58 // static const char* service_name() {
59 // return "ResourceManager";
65 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
66 // ApiResourceManager<Resource> > >
67 // g_factory = LAZY_INSTANCE_INITIALIZER;
71 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
72 // ApiResourceManager<Resource>::GetFactoryInstance() {
73 // return g_factory.Pointer();
76 class ApiResourceManager
: public BrowserContextKeyedAPI
,
77 public base::NonThreadSafe
,
78 public content::NotificationObserver
{
80 explicit ApiResourceManager(content::BrowserContext
* context
)
81 : thread_id_(T::kThreadId
), data_(new ApiResourceData(thread_id_
)) {
83 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
,
84 content::NotificationService::AllSources());
86 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED
,
87 content::NotificationService::AllSources());
91 static ApiResourceManager
<T
>* CreateApiResourceManagerForTest(
92 content::BrowserContext
* context
,
93 content::BrowserThread::ID thread_id
) {
94 ApiResourceManager
* manager
= new ApiResourceManager
<T
>(context
);
95 manager
->thread_id_
= thread_id
;
96 manager
->data_
= new ApiResourceData(thread_id
);
100 virtual ~ApiResourceManager() {
101 DCHECK(CalledOnValidThread());
102 DCHECK(content::BrowserThread::IsMessageLoopValid(thread_id_
))
103 << "A unit test is using an ApiResourceManager but didn't provide "
104 "the thread message loop needed for that kind of resource. "
105 "Please ensure that the appropriate message loop is operational.";
107 data_
->InititateCleanup();
110 // BrowserContextKeyedAPI implementation.
111 static BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >*
112 GetFactoryInstance();
114 // Convenience method to get the ApiResourceManager for a profile.
115 static ApiResourceManager
<T
>* Get(content::BrowserContext
* context
) {
116 return BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >::Get(context
);
120 int Add(T
* api_resource
) { return data_
->Add(api_resource
); }
122 void Remove(const std::string
& extension_id
, int api_resource_id
) {
123 data_
->Remove(extension_id
, api_resource_id
);
126 T
* Get(const std::string
& extension_id
, int api_resource_id
) {
127 return data_
->Get(extension_id
, api_resource_id
);
130 base::hash_set
<int>* GetResourceIds(const std::string
& extension_id
) {
131 return data_
->GetResourceIds(extension_id
);
135 // content::NotificationObserver:
136 virtual void Observe(int type
,
137 const content::NotificationSource
& source
,
138 const content::NotificationDetails
& details
) OVERRIDE
{
140 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
: {
141 std::string id
= content::Details
<extensions::UnloadedExtensionInfo
>(
142 details
)->extension
->id();
143 data_
->InitiateExtensionUnloadedCleanup(id
);
146 case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED
: {
147 ExtensionHost
* host
= content::Details
<ExtensionHost
>(details
).ptr();
148 data_
->InitiateExtensionSuspendedCleanup(host
->extension_id());
155 // TODO(rockot): ApiResourceData could be moved out of ApiResourceManager and
156 // we could avoid maintaining a friends list here.
157 friend class BluetoothAPI
;
158 friend class api::BluetoothSocketApiFunction
;
159 friend class api::BluetoothSocketEventDispatcher
;
160 friend class api::SerialEventDispatcher
;
161 friend class core_api::TCPServerSocketEventDispatcher
;
162 friend class core_api::TCPSocketEventDispatcher
;
163 friend class core_api::UDPSocketEventDispatcher
;
164 friend class BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >;
166 // BrowserContextKeyedAPI implementation.
167 static const char* service_name() { return T::service_name(); }
169 static const bool kServiceHasOwnInstanceInIncognito
= true;
170 static const bool kServiceIsNULLWhileTesting
= true;
172 // ApiResourceData class handles resource bookkeeping on a thread
173 // where resource lifetime is handled.
174 class ApiResourceData
: public base::RefCountedThreadSafe
<ApiResourceData
> {
176 typedef std::map
<int, linked_ptr
<T
> > ApiResourceMap
;
177 // Lookup map from extension id's to allocated resource id's.
178 typedef std::map
<std::string
, base::hash_set
<int> > ExtensionToResourceMap
;
180 explicit ApiResourceData(const content::BrowserThread::ID thread_id
)
181 : next_id_(1), thread_id_(thread_id
) {}
183 int Add(T
* api_resource
) {
184 DCHECK_CURRENTLY_ON(thread_id_
);
185 int id
= GenerateId();
187 linked_ptr
<T
> resource_ptr(api_resource
);
188 api_resource_map_
[id
] = resource_ptr
;
190 const std::string
& extension_id
= api_resource
->owner_extension_id();
191 if (extension_resource_map_
.find(extension_id
) ==
192 extension_resource_map_
.end()) {
193 extension_resource_map_
[extension_id
] = base::hash_set
<int>();
195 extension_resource_map_
[extension_id
].insert(id
);
202 void Remove(const std::string
& extension_id
, int api_resource_id
) {
203 DCHECK_CURRENTLY_ON(thread_id_
);
204 if (GetOwnedResource(extension_id
, api_resource_id
) != NULL
) {
205 DCHECK(extension_resource_map_
.find(extension_id
) !=
206 extension_resource_map_
.end());
207 extension_resource_map_
[extension_id
].erase(api_resource_id
);
208 api_resource_map_
.erase(api_resource_id
);
212 T
* Get(const std::string
& extension_id
, int api_resource_id
) {
213 DCHECK_CURRENTLY_ON(thread_id_
);
214 return GetOwnedResource(extension_id
, api_resource_id
);
217 base::hash_set
<int>* GetResourceIds(const std::string
& extension_id
) {
218 DCHECK_CURRENTLY_ON(thread_id_
);
219 return GetOwnedResourceIds(extension_id
);
222 void InitiateExtensionUnloadedCleanup(const std::string
& extension_id
) {
223 if (content::BrowserThread::CurrentlyOn(thread_id_
)) {
224 CleanupResourcesFromUnloadedExtension(extension_id
);
226 content::BrowserThread::PostTask(
229 base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension
,
235 void InitiateExtensionSuspendedCleanup(const std::string
& extension_id
) {
236 if (content::BrowserThread::CurrentlyOn(thread_id_
)) {
237 CleanupResourcesFromSuspendedExtension(extension_id
);
239 content::BrowserThread::PostTask(
242 base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension
,
248 void InititateCleanup() {
249 if (content::BrowserThread::CurrentlyOn(thread_id_
)) {
252 content::BrowserThread::PostTask(
253 thread_id_
, FROM_HERE
, base::Bind(&ApiResourceData::Cleanup
, this));
258 friend class base::RefCountedThreadSafe
<ApiResourceData
>;
260 virtual ~ApiResourceData() {}
262 T
* GetOwnedResource(const std::string
& extension_id
, int api_resource_id
) {
263 linked_ptr
<T
> ptr
= api_resource_map_
[api_resource_id
];
264 T
* resource
= ptr
.get();
265 if (resource
&& extension_id
== resource
->owner_extension_id())
270 base::hash_set
<int>* GetOwnedResourceIds(const std::string
& extension_id
) {
271 DCHECK_CURRENTLY_ON(thread_id_
);
272 if (extension_resource_map_
.find(extension_id
) ==
273 extension_resource_map_
.end())
276 return &extension_resource_map_
[extension_id
];
279 void CleanupResourcesFromUnloadedExtension(
280 const std::string
& extension_id
) {
281 CleanupResourcesFromExtension(extension_id
, true);
284 void CleanupResourcesFromSuspendedExtension(
285 const std::string
& extension_id
) {
286 CleanupResourcesFromExtension(extension_id
, false);
289 void CleanupResourcesFromExtension(const std::string
& extension_id
,
291 DCHECK_CURRENTLY_ON(thread_id_
);
293 if (extension_resource_map_
.find(extension_id
) ==
294 extension_resource_map_
.end()) {
298 // Remove all resources, or the non persistent ones only if |remove_all|
300 base::hash_set
<int>& resource_ids
= extension_resource_map_
[extension_id
];
301 for (base::hash_set
<int>::iterator it
= resource_ids
.begin();
302 it
!= resource_ids
.end();) {
307 linked_ptr
<T
> ptr
= api_resource_map_
[*it
];
308 T
* resource
= ptr
.get();
309 erase
= (resource
&& !resource
->IsPersistent());
313 api_resource_map_
.erase(*it
);
314 resource_ids
.erase(it
++);
320 // Remove extension entry if we removed all its resources.
321 if (resource_ids
.size() == 0) {
322 extension_resource_map_
.erase(extension_id
);
327 DCHECK_CURRENTLY_ON(thread_id_
);
329 api_resource_map_
.clear();
330 extension_resource_map_
.clear();
333 int GenerateId() { return next_id_
++; }
336 const content::BrowserThread::ID thread_id_
;
337 ApiResourceMap api_resource_map_
;
338 ExtensionToResourceMap extension_resource_map_
;
341 content::BrowserThread::ID thread_id_
;
342 content::NotificationRegistrar registrar_
;
343 scoped_refptr
<ApiResourceData
> data_
;
346 } // namespace extensions
348 #endif // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_