Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / extensions / browser / api / api_resource_manager.h
blob0eaf36d73bb177653e8ec69f8544fdf3fe08bdf9
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/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 {
30 namespace api {
31 class BluetoothSocketApiFunction;
32 class BluetoothSocketEventDispatcher;
35 namespace core_api {
36 class SerialEventDispatcher;
37 class TCPServerSocketEventDispatcher;
38 class TCPSocketEventDispatcher;
39 class UDPSocketEventDispatcher;
42 template <typename T>
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);
57 template <typename T>
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_;
74 template <typename T>
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).
91 // E.g.:
93 // class Resource {
94 // public:
95 // static const BrowserThread::ID kThreadId = BrowserThread::FILE;
96 // private:
97 // friend class ApiResourceManager<Resource>;
98 // static const char* service_name() {
99 // return "ResourceManager";
100 // }
101 // };
103 // In the cc file:
105 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
106 // ApiResourceManager<Resource> > >
107 // g_factory = LAZY_INSTANCE_INITIALIZER;
110 // template <>
111 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
112 // ApiResourceManager<Resource>::GetFactoryInstance() {
113 // return g_factory.Pointer();
114 // }
115 template <class T, typename ThreadingTraits = NamedThreadTraits<T> >
116 class ApiResourceManager : public BrowserContextKeyedAPI,
117 public base::NonThreadSafe,
118 public content::NotificationObserver,
119 public ExtensionRegistryObserver {
120 public:
121 explicit ApiResourceManager(content::BrowserContext* context)
122 : data_(new ApiResourceData()), extension_registry_observer_(this) {
123 extension_registry_observer_.Add(ExtensionRegistry::Get(context));
124 registrar_.Add(this,
125 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
126 content::NotificationService::AllSources());
128 // For Testing.
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);
135 return manager;
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();
148 // Takes ownership.
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
178 // |extension_id|.
179 bool Replace(const std::string& extension_id,
180 int api_resource_id,
181 T* resource) {
182 return data_->Replace(extension_id, api_resource_id, resource);
185 protected:
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());
203 private:
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> {
221 public:
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();
231 if (id > 0) {
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);
242 return id;
244 return 0;
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
265 // |extension_id|.
266 bool Replace(const std::string& extension_id,
267 int api_resource_id,
268 T* api_resource) {
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);
273 return true;
275 return false;
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);
286 } else {
287 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
288 FROM_HERE,
289 base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension,
290 this,
291 extension_id));
295 void InitiateExtensionSuspendedCleanup(const std::string& extension_id) {
296 if (ThreadingTraits::IsCalledOnValidThread()) {
297 CleanupResourcesFromSuspendedExtension(extension_id);
298 } else {
299 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
300 FROM_HERE,
301 base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension,
302 this,
303 extension_id));
307 void InititateCleanup() {
308 if (ThreadingTraits::IsCalledOnValidThread()) {
309 Cleanup();
310 } else {
311 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
312 FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
316 private:
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())
325 return resource;
326 return NULL;
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())
333 return NULL;
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,
349 bool remove_all) {
350 DCHECK(ThreadingTraits::IsCalledOnValidThread());
352 if (extension_resource_map_.find(extension_id) ==
353 extension_resource_map_.end()) {
354 return;
357 // Remove all resources, or the non persistent ones only if |remove_all|
358 // is false.
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();) {
362 bool erase = false;
363 if (remove_all) {
364 erase = true;
365 } else {
366 linked_ptr<T> ptr = api_resource_map_[*it];
367 T* resource = ptr.get();
368 erase = (resource && !resource->IsPersistent());
371 if (erase) {
372 api_resource_map_.erase(*it);
373 resource_ids.erase(it++);
374 } else {
375 ++it;
377 } // end for
379 // Remove extension entry if we removed all its resources.
380 if (resource_ids.size() == 0) {
381 extension_resource_map_.erase(extension_id);
385 void Cleanup() {
386 DCHECK(ThreadingTraits::IsCalledOnValidThread());
388 api_resource_map_.clear();
389 extension_resource_map_.clear();
392 int GenerateId() { return next_id_++; }
394 int 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
414 // shutdown.
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).
422 // E.g.:
424 // class PoolResource {
425 // public:
426 // static const char kSequenceToken[] = "temp_files";
427 // static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior =
428 // base::SequencedWorkerPool::BLOCK_SHUTDOWN;
429 // private:
430 // friend class ApiResourceManager<WorkerPoolResource,
431 // WorkerPoolThreadTraits>;
432 // static const char* service_name() {
433 // return "TempFilesResourceManager";
434 // }
435 // };
437 // In the cc file:
439 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
440 // ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
441 // g_factory = LAZY_INSTANCE_INITIALIZER;
444 // template <>
445 // BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >*
446 // ApiResourceManager<WorkerPoolPoolResource,
447 // WorkerPoolThreadTraits>::GetFactoryInstance() {
448 // return g_factory.Pointer();
449 // }
450 template <typename T>
451 struct WorkerPoolThreadTraits {
452 static bool IsCalledOnValidThread() {
453 return content::BrowserThread::GetBlockingPool()
454 ->IsRunningSequenceOnCurrentThread(
455 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
456 T::kSequenceToken));
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(
467 T::kSequenceToken),
468 T::kShutdownBehavior);
472 } // namespace extensions
474 #endif // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_