Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / api / api_resource_manager.h
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.
4
5 #ifndef EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
6 #define EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
7
8 #include <map>
9
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"
24
25 namespace extensions {
26
27 namespace api {
28 class BluetoothSocketApiFunction;
29 class BluetoothSocketEventDispatcher;
30 class SerialEventDispatcher;
31 }
32
33 namespace core_api {
34 class TCPServerSocketEventDispatcher;
35 class TCPSocketEventDispatcher;
36 class UDPSocketEventDispatcher;
37 }
38
39 // An ApiResourceManager manages the lifetime of a set of resources that
40 // ApiFunctions use. Examples are sockets or USB connections.
41 //
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.
47 //
48 // In the cc file the user must define a GetFactoryInstance() and manage their
49 // own instances (typically using LazyInstance or Singleton).
50 //
51 // E.g.:
52 //
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 // };
62 //
63 // In the cc file:
64 //
65 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
66 //     ApiResourceManager<Resource> > >
67 //         g_factory = LAZY_INSTANCE_INITIALIZER;
68 //
69 //
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());
88   }
89
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;
98   }
99
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.";
106
107     data_->InititateCleanup();
108   }
109
110   // BrowserContextKeyedAPI implementation.
111   static BrowserContextKeyedAPIFactory<ApiResourceManager<T> >*
112       GetFactoryInstance();
113
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);
117   }
118
119   // Takes ownership.
120   int Add(T* api_resource) { return data_->Add(api_resource); }
121
122   void Remove(const std::string& extension_id, int api_resource_id) {
123     data_->Remove(extension_id, api_resource_id);
124   }
125
126   T* Get(const std::string& extension_id, int api_resource_id) {
127     return data_->Get(extension_id, api_resource_id);
128   }
129
130   base::hash_set<int>* GetResourceIds(const std::string& extension_id) {
131     return data_->GetResourceIds(extension_id);
132   }
133
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;
145       }
146       case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
147         ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
148         data_->InitiateExtensionSuspendedCleanup(host->extension_id());
149         break;
150       }
151     }
152   }
153
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> >;
165
166   // BrowserContextKeyedAPI implementation.
167   static const char* service_name() { return T::service_name(); }
168
169   static const bool kServiceHasOwnInstanceInIncognito = true;
170   static const bool kServiceIsNULLWhileTesting = true;
171
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;
179
180     explicit ApiResourceData(const content::BrowserThread::ID thread_id)
181         : next_id_(1), thread_id_(thread_id) {}
182
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;
189
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>();
194         }
195         extension_resource_map_[extension_id].insert(id);
196
197         return id;
198       }
199       return 0;
200     }
201
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);
209       }
210     }
211
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);
215     }
216
217     base::hash_set<int>* GetResourceIds(const std::string& extension_id) {
218       DCHECK_CURRENTLY_ON(thread_id_);
219       return GetOwnedResourceIds(extension_id);
220     }
221
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));
232       }
233     }
234
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));
245       }
246     }
247
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));
254       }
255     }
256
257    private:
258     friend class base::RefCountedThreadSafe<ApiResourceData>;
259
260     virtual ~ApiResourceData() {}
261
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;
268     }
269
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;
275
276       return &extension_resource_map_[extension_id];
277     }
278
279     void CleanupResourcesFromUnloadedExtension(
280         const std::string& extension_id) {
281       CleanupResourcesFromExtension(extension_id, true);
282     }
283
284     void CleanupResourcesFromSuspendedExtension(
285         const std::string& extension_id) {
286       CleanupResourcesFromExtension(extension_id, false);
287     }
288
289     void CleanupResourcesFromExtension(const std::string& extension_id,
290                                        bool remove_all) {
291       DCHECK_CURRENTLY_ON(thread_id_);
292
293       if (extension_resource_map_.find(extension_id) ==
294           extension_resource_map_.end()) {
295         return;
296       }
297
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());
310         }
311
312         if (erase) {
313           api_resource_map_.erase(*it);
314           resource_ids.erase(it++);
315         } else {
316           ++it;
317         }
318       }  // end for
319
320       // Remove extension entry if we removed all its resources.
321       if (resource_ids.size() == 0) {
322         extension_resource_map_.erase(extension_id);
323       }
324     }
325
326     void Cleanup() {
327       DCHECK_CURRENTLY_ON(thread_id_);
328
329       api_resource_map_.clear();
330       extension_resource_map_.clear();
331     }
332
333     int GenerateId() { return next_id_++; }
334
335     int next_id_;
336     const content::BrowserThread::ID thread_id_;
337     ApiResourceMap api_resource_map_;
338     ExtensionToResourceMap extension_resource_map_;
339   };
340
341   content::BrowserThread::ID thread_id_;
342   content::NotificationRegistrar registrar_;
343   scoped_refptr<ApiResourceData> data_;
344 };
345
346 }  // namespace extensions
347
348 #endif  // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_