Add ICU message format support
[chromium-blink-merge.git] / extensions / browser / api / api_resource_manager.h
blob79ef86209dd35a012548e004e067789da79965ec
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/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 {
26 class CastChannelAsyncApiFunction;
28 namespace api {
29 class BluetoothSocketApiFunction;
30 class BluetoothSocketEventDispatcher;
31 class SerialEventDispatcher;
32 class TCPServerSocketEventDispatcher;
33 class TCPSocketEventDispatcher;
34 class UDPSocketEventDispatcher;
37 template <typename T>
38 struct NamedThreadTraits {
39 static bool IsCalledOnValidThread() {
40 return content::BrowserThread::CurrentlyOn(T::kThreadId);
43 static bool IsMessageLoopValid() {
44 return content::BrowserThread::IsMessageLoopValid(T::kThreadId);
47 static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
48 return content::BrowserThread::GetMessageLoopProxyForThread(T::kThreadId);
52 // An ApiResourceManager manages the lifetime of a set of resources that
53 // that live on named threads (i.e. BrowserThread::IO) which ApiFunctions use.
54 // Examples of such resources are sockets or USB connections.
56 // Users of this class should define kThreadId to be the thread that
57 // ApiResourceManager to works on. The default is defined in ApiResource.
58 // The user must also define a static const char* service_name() that returns
59 // the name of the service, and in order for ApiResourceManager to use
60 // service_name() friend this class.
62 // In the cc file the user must define a GetFactoryInstance() and manage their
63 // own instances (typically using LazyInstance or Singleton).
65 // E.g.:
67 // class Resource {
68 // public:
69 // static const BrowserThread::ID kThreadId = BrowserThread::FILE;
70 // private:
71 // friend class ApiResourceManager<Resource>;
72 // static const char* service_name() {
73 // return "ResourceManager";
74 // }
75 // };
77 // In the cc file:
79 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
80 // ApiResourceManager<Resource> > >
81 // g_factory = LAZY_INSTANCE_INITIALIZER;
84 // template <>
85 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
86 // ApiResourceManager<Resource>::GetFactoryInstance() {
87 // return g_factory.Pointer();
88 // }
89 template <class T, typename ThreadingTraits = NamedThreadTraits<T>>
90 class ApiResourceManager : public BrowserContextKeyedAPI,
91 public base::NonThreadSafe,
92 public ExtensionRegistryObserver,
93 public ProcessManagerObserver {
94 public:
95 explicit ApiResourceManager(content::BrowserContext* context)
96 : data_(new ApiResourceData()),
97 extension_registry_observer_(this),
98 process_manager_observer_(this) {
99 extension_registry_observer_.Add(ExtensionRegistry::Get(context));
100 process_manager_observer_.Add(ProcessManager::Get(context));
103 virtual ~ApiResourceManager() {
104 DCHECK(CalledOnValidThread());
105 DCHECK(ThreadingTraits::IsMessageLoopValid())
106 << "A unit test is using an ApiResourceManager but didn't provide "
107 "the thread message loop needed for that kind of resource. "
108 "Please ensure that the appropriate message loop is operational.";
110 data_->InititateCleanup();
113 // Takes ownership.
114 int Add(T* api_resource) { return data_->Add(api_resource); }
116 void Remove(const std::string& extension_id, int api_resource_id) {
117 data_->Remove(extension_id, api_resource_id);
120 T* Get(const std::string& extension_id, int api_resource_id) {
121 return data_->Get(extension_id, api_resource_id);
124 base::hash_set<int>* GetResourceIds(const std::string& extension_id) {
125 return data_->GetResourceIds(extension_id);
128 // BrowserContextKeyedAPI implementation.
129 static BrowserContextKeyedAPIFactory<ApiResourceManager<T> >*
130 GetFactoryInstance();
132 // Convenience method to get the ApiResourceManager for a profile.
133 static ApiResourceManager<T>* Get(content::BrowserContext* context) {
134 return BrowserContextKeyedAPIFactory<ApiResourceManager<T> >::Get(context);
137 // BrowserContextKeyedAPI implementation.
138 static const char* service_name() { return T::service_name(); }
140 // Change the resource mapped to this |extension_id| at this
141 // |api_resource_id| to |resource|. Returns true and succeeds unless
142 // |api_resource_id| does not already identify a resource held by
143 // |extension_id|.
144 bool Replace(const std::string& extension_id,
145 int api_resource_id,
146 T* resource) {
147 return data_->Replace(extension_id, api_resource_id, resource);
150 protected:
151 // ProcessManagerObserver:
152 void OnBackgroundHostClose(const std::string& extension_id) override {
153 data_->InitiateExtensionSuspendedCleanup(extension_id);
156 // ExtensionRegistryObserver:
157 void OnExtensionUnloaded(content::BrowserContext* browser_context,
158 const Extension* extension,
159 UnloadedExtensionInfo::Reason reason) override {
160 data_->InitiateExtensionUnloadedCleanup(extension->id());
163 private:
164 // TODO(rockot): ApiResourceData could be moved out of ApiResourceManager and
165 // we could avoid maintaining a friends list here.
166 friend class BluetoothAPI;
167 friend class CastChannelAsyncApiFunction;
168 friend class api::BluetoothSocketApiFunction;
169 friend class api::BluetoothSocketEventDispatcher;
170 friend class api::SerialEventDispatcher;
171 friend class api::TCPServerSocketEventDispatcher;
172 friend class api::TCPSocketEventDispatcher;
173 friend class api::UDPSocketEventDispatcher;
174 friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T> >;
176 static const bool kServiceHasOwnInstanceInIncognito = true;
177 static const bool kServiceIsNULLWhileTesting = true;
179 // ApiResourceData class handles resource bookkeeping on a thread
180 // where resource lifetime is handled.
181 class ApiResourceData : public base::RefCountedThreadSafe<ApiResourceData> {
182 public:
183 typedef std::map<int, linked_ptr<T> > ApiResourceMap;
184 // Lookup map from extension id's to allocated resource id's.
185 typedef std::map<std::string, base::hash_set<int> > ExtensionToResourceMap;
187 ApiResourceData() : next_id_(1) {}
189 int Add(T* api_resource) {
190 DCHECK(ThreadingTraits::IsCalledOnValidThread());
191 int id = GenerateId();
192 if (id > 0) {
193 linked_ptr<T> resource_ptr(api_resource);
194 api_resource_map_[id] = resource_ptr;
196 const std::string& extension_id = api_resource->owner_extension_id();
197 ExtensionToResourceMap::iterator it =
198 extension_resource_map_.find(extension_id);
199 if (it == extension_resource_map_.end()) {
200 it = extension_resource_map_.insert(
201 std::make_pair(extension_id, base::hash_set<int>())).first;
203 it->second.insert(id);
204 return id;
206 return 0;
209 void Remove(const std::string& extension_id, int api_resource_id) {
210 DCHECK(ThreadingTraits::IsCalledOnValidThread());
211 if (GetOwnedResource(extension_id, api_resource_id)) {
212 ExtensionToResourceMap::iterator it =
213 extension_resource_map_.find(extension_id);
214 it->second.erase(api_resource_id);
215 api_resource_map_.erase(api_resource_id);
219 T* Get(const std::string& extension_id, int api_resource_id) {
220 DCHECK(ThreadingTraits::IsCalledOnValidThread());
221 return GetOwnedResource(extension_id, api_resource_id);
224 // Change the resource mapped to this |extension_id| at this
225 // |api_resource_id| to |resource|. Returns true and succeeds unless
226 // |api_resource_id| does not already identify a resource held by
227 // |extension_id|.
228 bool Replace(const std::string& extension_id,
229 int api_resource_id,
230 T* api_resource) {
231 DCHECK(ThreadingTraits::IsCalledOnValidThread());
232 T* old_resource = api_resource_map_[api_resource_id].get();
233 if (old_resource && extension_id == old_resource->owner_extension_id()) {
234 api_resource_map_[api_resource_id] = linked_ptr<T>(api_resource);
235 return true;
237 return false;
240 base::hash_set<int>* GetResourceIds(const std::string& extension_id) {
241 DCHECK(ThreadingTraits::IsCalledOnValidThread());
242 return GetOwnedResourceIds(extension_id);
245 void InitiateExtensionUnloadedCleanup(const std::string& extension_id) {
246 if (ThreadingTraits::IsCalledOnValidThread()) {
247 CleanupResourcesFromUnloadedExtension(extension_id);
248 } else {
249 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
250 FROM_HERE,
251 base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension,
252 this,
253 extension_id));
257 void InitiateExtensionSuspendedCleanup(const std::string& extension_id) {
258 if (ThreadingTraits::IsCalledOnValidThread()) {
259 CleanupResourcesFromSuspendedExtension(extension_id);
260 } else {
261 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
262 FROM_HERE,
263 base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension,
264 this,
265 extension_id));
269 void InititateCleanup() {
270 if (ThreadingTraits::IsCalledOnValidThread()) {
271 Cleanup();
272 } else {
273 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
274 FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
278 private:
279 friend class base::RefCountedThreadSafe<ApiResourceData>;
281 virtual ~ApiResourceData() {}
283 T* GetOwnedResource(const std::string& extension_id, int api_resource_id) {
284 linked_ptr<T> ptr = api_resource_map_[api_resource_id];
285 T* resource = ptr.get();
286 if (resource && extension_id == resource->owner_extension_id())
287 return resource;
288 return NULL;
291 base::hash_set<int>* GetOwnedResourceIds(const std::string& extension_id) {
292 DCHECK(ThreadingTraits::IsCalledOnValidThread());
293 ExtensionToResourceMap::iterator it =
294 extension_resource_map_.find(extension_id);
295 if (it == extension_resource_map_.end())
296 return NULL;
297 return &(it->second);
300 void CleanupResourcesFromUnloadedExtension(
301 const std::string& extension_id) {
302 CleanupResourcesFromExtension(extension_id, true);
305 void CleanupResourcesFromSuspendedExtension(
306 const std::string& extension_id) {
307 CleanupResourcesFromExtension(extension_id, false);
310 void CleanupResourcesFromExtension(const std::string& extension_id,
311 bool remove_all) {
312 DCHECK(ThreadingTraits::IsCalledOnValidThread());
314 ExtensionToResourceMap::iterator it =
315 extension_resource_map_.find(extension_id);
316 if (it == extension_resource_map_.end())
317 return;
319 // Remove all resources, or the non persistent ones only if |remove_all|
320 // is false.
321 base::hash_set<int>& resource_ids = it->second;
322 for (base::hash_set<int>::iterator it = resource_ids.begin();
323 it != resource_ids.end();) {
324 bool erase = false;
325 if (remove_all) {
326 erase = true;
327 } else {
328 linked_ptr<T> ptr = api_resource_map_[*it];
329 T* resource = ptr.get();
330 erase = (resource && !resource->IsPersistent());
333 if (erase) {
334 api_resource_map_.erase(*it);
335 resource_ids.erase(it++);
336 } else {
337 ++it;
339 } // end for
341 // Remove extension entry if we removed all its resources.
342 if (resource_ids.size() == 0) {
343 extension_resource_map_.erase(extension_id);
347 void Cleanup() {
348 DCHECK(ThreadingTraits::IsCalledOnValidThread());
350 api_resource_map_.clear();
351 extension_resource_map_.clear();
354 int GenerateId() { return next_id_++; }
356 int next_id_;
357 ApiResourceMap api_resource_map_;
358 ExtensionToResourceMap extension_resource_map_;
361 content::NotificationRegistrar registrar_;
362 scoped_refptr<ApiResourceData> data_;
364 ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
365 extension_registry_observer_;
366 ScopedObserver<ProcessManager, ProcessManagerObserver>
367 process_manager_observer_;
370 // With WorkerPoolThreadTraits, ApiResourceManager can be used to manage the
371 // lifetime of a set of resources that live on sequenced task runner threads
372 // which ApiFunctions use. Examples of such resources are temporary file
373 // resources produced by certain API calls.
375 // Instead of kThreadId. classes used for tracking such resources should define
376 // kSequenceToken and kShutdownBehavior to identify sequence task runner for
377 // ApiResourceManager to work on and how pending tasks should behave on
378 // shutdown.
379 // The user must also define a static const char* service_name() that returns
380 // the name of the service, and in order for ApiWorkerPoolResourceManager to use
381 // service_name() friend this class.
383 // In the cc file the user must define a GetFactoryInstance() and manage their
384 // own instances (typically using LazyInstance or Singleton).
386 // E.g.:
388 // class PoolResource {
389 // public:
390 // static const char kSequenceToken[] = "temp_files";
391 // static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior =
392 // base::SequencedWorkerPool::BLOCK_SHUTDOWN;
393 // private:
394 // friend class ApiResourceManager<WorkerPoolResource,
395 // WorkerPoolThreadTraits>;
396 // static const char* service_name() {
397 // return "TempFilesResourceManager";
398 // }
399 // };
401 // In the cc file:
403 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
404 // ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
405 // g_factory = LAZY_INSTANCE_INITIALIZER;
408 // template <>
409 // BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >*
410 // ApiResourceManager<WorkerPoolPoolResource,
411 // WorkerPoolThreadTraits>::GetFactoryInstance() {
412 // return g_factory.Pointer();
413 // }
414 template <typename T>
415 struct WorkerPoolThreadTraits {
416 static bool IsCalledOnValidThread() {
417 return content::BrowserThread::GetBlockingPool()
418 ->IsRunningSequenceOnCurrentThread(
419 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
420 T::kSequenceToken));
423 static bool IsMessageLoopValid() {
424 return content::BrowserThread::GetBlockingPool() != NULL;
427 static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
428 return content::BrowserThread::GetBlockingPool()
429 ->GetSequencedTaskRunnerWithShutdownBehavior(
430 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
431 T::kSequenceToken),
432 T::kShutdownBehavior);
436 } // namespace extensions
438 #endif // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_