Add testing/scripts/OWNERS
[chromium-blink-merge.git] / extensions / browser / api / api_resource_manager.h
blobe90cb33c302b61ac2e715945a2901d5633d05235
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_
8 #include <map>
10 #include "base/containers/hash_tables.h"
11 #include "base/memory/linked_ptr.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/scoped_observer.h"
14 #include "base/threading/non_thread_safe.h"
15 #include "components/keyed_service/core/keyed_service.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "extensions/browser/browser_context_keyed_api_factory.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/browser/extension_registry_observer.h"
20 #include "extensions/browser/process_manager.h"
21 #include "extensions/browser/process_manager_observer.h"
22 #include "extensions/common/extension.h"
24 namespace extensions {
26 namespace core_api {
27 class BluetoothSocketApiFunction;
28 class BluetoothSocketEventDispatcher;
29 class SerialEventDispatcher;
30 class TCPServerSocketEventDispatcher;
31 class TCPSocketEventDispatcher;
32 class UDPSocketEventDispatcher;
35 template <typename T>
36 struct NamedThreadTraits {
37 static bool IsCalledOnValidThread() {
38 return content::BrowserThread::CurrentlyOn(T::kThreadId);
41 static bool IsMessageLoopValid() {
42 return content::BrowserThread::IsMessageLoopValid(T::kThreadId);
45 static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
46 return content::BrowserThread::GetMessageLoopProxyForThread(T::kThreadId);
50 template <typename T>
51 struct TestThreadTraits {
52 static bool IsCalledOnValidThread() {
53 return content::BrowserThread::CurrentlyOn(thread_id_);
56 static bool IsMessageLoopValid() {
57 return content::BrowserThread::IsMessageLoopValid(thread_id_);
60 static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
61 return content::BrowserThread::GetMessageLoopProxyForThread(thread_id_);
64 static content::BrowserThread::ID thread_id_;
67 template <typename T>
68 content::BrowserThread::ID TestThreadTraits<T>::thread_id_ =
69 content::BrowserThread::IO;
71 // An ApiResourceManager manages the lifetime of a set of resources that
72 // that live on named threads (i.e. BrowserThread::IO) which ApiFunctions use.
73 // Examples of such resources are sockets or USB connections.
75 // Users of this class should define kThreadId to be the thread that
76 // ApiResourceManager to works on. The default is defined in ApiResource.
77 // The user must also define a static const char* service_name() that returns
78 // the name of the service, and in order for ApiResourceManager to use
79 // service_name() friend this class.
81 // In the cc file the user must define a GetFactoryInstance() and manage their
82 // own instances (typically using LazyInstance or Singleton).
84 // E.g.:
86 // class Resource {
87 // public:
88 // static const BrowserThread::ID kThreadId = BrowserThread::FILE;
89 // private:
90 // friend class ApiResourceManager<Resource>;
91 // static const char* service_name() {
92 // return "ResourceManager";
93 // }
94 // };
96 // In the cc file:
98 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
99 // ApiResourceManager<Resource> > >
100 // g_factory = LAZY_INSTANCE_INITIALIZER;
103 // template <>
104 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
105 // ApiResourceManager<Resource>::GetFactoryInstance() {
106 // return g_factory.Pointer();
107 // }
108 template <class T, typename ThreadingTraits = NamedThreadTraits<T>>
109 class ApiResourceManager : public BrowserContextKeyedAPI,
110 public base::NonThreadSafe,
111 public ExtensionRegistryObserver,
112 public ProcessManagerObserver {
113 public:
114 explicit ApiResourceManager(content::BrowserContext* context)
115 : data_(new ApiResourceData()),
116 extension_registry_observer_(this),
117 process_manager_observer_(this) {
118 extension_registry_observer_.Add(ExtensionRegistry::Get(context));
119 process_manager_observer_.Add(ProcessManager::Get(context));
121 // For Testing.
122 static ApiResourceManager<T, TestThreadTraits<T> >*
123 CreateApiResourceManagerForTest(content::BrowserContext* context,
124 content::BrowserThread::ID thread_id) {
125 TestThreadTraits<T>::thread_id_ = thread_id;
126 ApiResourceManager<T, TestThreadTraits<T> >* manager =
127 new ApiResourceManager<T, TestThreadTraits<T> >(context);
128 return manager;
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();
141 // Takes ownership.
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
171 // |extension_id|.
172 bool Replace(const std::string& extension_id,
173 int api_resource_id,
174 T* resource) {
175 return data_->Replace(extension_id, api_resource_id, resource);
178 protected:
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());
191 private:
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> {
209 public:
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();
219 if (id > 0) {
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);
231 return id;
233 return 0;
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
254 // |extension_id|.
255 bool Replace(const std::string& extension_id,
256 int api_resource_id,
257 T* api_resource) {
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);
262 return true;
264 return false;
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);
275 } else {
276 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
277 FROM_HERE,
278 base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension,
279 this,
280 extension_id));
284 void InitiateExtensionSuspendedCleanup(const std::string& extension_id) {
285 if (ThreadingTraits::IsCalledOnValidThread()) {
286 CleanupResourcesFromSuspendedExtension(extension_id);
287 } else {
288 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
289 FROM_HERE,
290 base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension,
291 this,
292 extension_id));
296 void InititateCleanup() {
297 if (ThreadingTraits::IsCalledOnValidThread()) {
298 Cleanup();
299 } else {
300 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
301 FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
305 private:
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())
314 return resource;
315 return NULL;
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())
323 return NULL;
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,
338 bool remove_all) {
339 DCHECK(ThreadingTraits::IsCalledOnValidThread());
341 ExtensionToResourceMap::iterator it =
342 extension_resource_map_.find(extension_id);
343 if (it == extension_resource_map_.end())
344 return;
346 // Remove all resources, or the non persistent ones only if |remove_all|
347 // is false.
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();) {
351 bool erase = false;
352 if (remove_all) {
353 erase = true;
354 } else {
355 linked_ptr<T> ptr = api_resource_map_[*it];
356 T* resource = ptr.get();
357 erase = (resource && !resource->IsPersistent());
360 if (erase) {
361 api_resource_map_.erase(*it);
362 resource_ids.erase(it++);
363 } else {
364 ++it;
366 } // end for
368 // Remove extension entry if we removed all its resources.
369 if (resource_ids.size() == 0) {
370 extension_resource_map_.erase(extension_id);
374 void Cleanup() {
375 DCHECK(ThreadingTraits::IsCalledOnValidThread());
377 api_resource_map_.clear();
378 extension_resource_map_.clear();
381 int GenerateId() { return next_id_++; }
383 int 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
405 // shutdown.
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).
413 // E.g.:
415 // class PoolResource {
416 // public:
417 // static const char kSequenceToken[] = "temp_files";
418 // static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior =
419 // base::SequencedWorkerPool::BLOCK_SHUTDOWN;
420 // private:
421 // friend class ApiResourceManager<WorkerPoolResource,
422 // WorkerPoolThreadTraits>;
423 // static const char* service_name() {
424 // return "TempFilesResourceManager";
425 // }
426 // };
428 // In the cc file:
430 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
431 // ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
432 // g_factory = LAZY_INSTANCE_INITIALIZER;
435 // template <>
436 // BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >*
437 // ApiResourceManager<WorkerPoolPoolResource,
438 // WorkerPoolThreadTraits>::GetFactoryInstance() {
439 // return g_factory.Pointer();
440 // }
441 template <typename T>
442 struct WorkerPoolThreadTraits {
443 static bool IsCalledOnValidThread() {
444 return content::BrowserThread::GetBlockingPool()
445 ->IsRunningSequenceOnCurrentThread(
446 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
447 T::kSequenceToken));
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(
458 T::kSequenceToken),
459 T::kShutdownBehavior);
463 } // namespace extensions
465 #endif // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_