Implementation of leveldb-backed PrefStore.
[chromium-blink-merge.git] / extensions / browser / api / api_resource_manager.h
blobe0c9b811865d297c0785129bb252e068f57b4a42
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/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 {
27 namespace api {
28 class BluetoothSocketApiFunction;
29 class BluetoothSocketEventDispatcher;
30 class SerialEventDispatcher;
33 namespace core_api {
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).
51 // E.g.:
53 // class Resource {
54 // public:
55 // static const BrowserThread::ID kThreadId = BrowserThread::FILE;
56 // private:
57 // friend class ApiResourceManager<Resource>;
58 // static const char* service_name() {
59 // return "ResourceManager";
60 // }
61 // };
63 // In the cc file:
65 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
66 // ApiResourceManager<Resource> > >
67 // g_factory = LAZY_INSTANCE_INITIALIZER;
70 // template <>
71 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
72 // ApiResourceManager<Resource>::GetFactoryInstance() {
73 // return g_factory.Pointer();
74 // }
75 template <class T>
76 class ApiResourceManager : public BrowserContextKeyedAPI,
77 public base::NonThreadSafe,
78 public content::NotificationObserver {
79 public:
80 explicit ApiResourceManager(content::BrowserContext* context)
81 : thread_id_(T::kThreadId), data_(new ApiResourceData(thread_id_)) {
82 registrar_.Add(this,
83 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
84 content::NotificationService::AllSources());
85 registrar_.Add(this,
86 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
87 content::NotificationService::AllSources());
90 // For Testing.
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);
97 return manager;
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);
119 // Takes ownership.
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);
134 protected:
135 // content::NotificationObserver:
136 virtual void Observe(int type,
137 const content::NotificationSource& source,
138 const content::NotificationDetails& details) OVERRIDE {
139 switch (type) {
140 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
141 std::string id = content::Details<extensions::UnloadedExtensionInfo>(
142 details)->extension->id();
143 data_->InitiateExtensionUnloadedCleanup(id);
144 break;
146 case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
147 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
148 data_->InitiateExtensionSuspendedCleanup(host->extension_id());
149 break;
154 private:
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> {
175 public:
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();
186 if (id > 0) {
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);
197 return id;
199 return 0;
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);
225 } else {
226 content::BrowserThread::PostTask(
227 thread_id_,
228 FROM_HERE,
229 base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension,
230 this,
231 extension_id));
235 void InitiateExtensionSuspendedCleanup(const std::string& extension_id) {
236 if (content::BrowserThread::CurrentlyOn(thread_id_)) {
237 CleanupResourcesFromSuspendedExtension(extension_id);
238 } else {
239 content::BrowserThread::PostTask(
240 thread_id_,
241 FROM_HERE,
242 base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension,
243 this,
244 extension_id));
248 void InititateCleanup() {
249 if (content::BrowserThread::CurrentlyOn(thread_id_)) {
250 Cleanup();
251 } else {
252 content::BrowserThread::PostTask(
253 thread_id_, FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
257 private:
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())
266 return resource;
267 return NULL;
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())
274 return NULL;
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,
290 bool remove_all) {
291 DCHECK_CURRENTLY_ON(thread_id_);
293 if (extension_resource_map_.find(extension_id) ==
294 extension_resource_map_.end()) {
295 return;
298 // Remove all resources, or the non persistent ones only if |remove_all|
299 // is false.
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();) {
303 bool erase = false;
304 if (remove_all) {
305 erase = true;
306 } else {
307 linked_ptr<T> ptr = api_resource_map_[*it];
308 T* resource = ptr.get();
309 erase = (resource && !resource->IsPersistent());
312 if (erase) {
313 api_resource_map_.erase(*it);
314 resource_ids.erase(it++);
315 } else {
316 ++it;
318 } // end for
320 // Remove extension entry if we removed all its resources.
321 if (resource_ids.size() == 0) {
322 extension_resource_map_.erase(extension_id);
326 void Cleanup() {
327 DCHECK_CURRENTLY_ON(thread_id_);
329 api_resource_map_.clear();
330 extension_resource_map_.clear();
333 int GenerateId() { return next_id_++; }
335 int 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_