Upstream version 9.38.198.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/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"
27
28 namespace extensions {
29
30 namespace api {
31 class BluetoothSocketApiFunction;
32 class BluetoothSocketEventDispatcher;
33 }
34
35 namespace core_api {
36 class SerialEventDispatcher;
37 class TCPServerSocketEventDispatcher;
38 class TCPSocketEventDispatcher;
39 class UDPSocketEventDispatcher;
40 }
41
42 template <typename T>
43 struct NamedThreadTraits {
44   static bool IsCalledOnValidThread() {
45     return content::BrowserThread::CurrentlyOn(T::kThreadId);
46   }
47
48   static bool IsMessageLoopValid() {
49     return content::BrowserThread::IsMessageLoopValid(T::kThreadId);
50   }
51
52   static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
53     return content::BrowserThread::GetMessageLoopProxyForThread(T::kThreadId);
54   }
55 };
56
57 template <typename T>
58 struct TestThreadTraits {
59   static bool IsCalledOnValidThread() {
60     return content::BrowserThread::CurrentlyOn(thread_id_);
61   }
62
63   static bool IsMessageLoopValid() {
64     return content::BrowserThread::IsMessageLoopValid(thread_id_);
65   }
66
67   static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
68     return content::BrowserThread::GetMessageLoopProxyForThread(thread_id_);
69   }
70
71   static content::BrowserThread::ID thread_id_;
72 };
73
74 template <typename T>
75 content::BrowserThread::ID TestThreadTraits<T>::thread_id_ =
76     content::BrowserThread::IO;
77
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.
81 //
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.
87 //
88 // In the cc file the user must define a GetFactoryInstance() and manage their
89 // own instances (typically using LazyInstance or Singleton).
90 //
91 // E.g.:
92 //
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 // };
102 //
103 // In the cc file:
104 //
105 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
106 //     ApiResourceManager<Resource> > >
107 //         g_factory = LAZY_INSTANCE_INITIALIZER;
108 //
109 //
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());
127   }
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;
136   }
137
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.";
144
145     data_->InititateCleanup();
146   }
147
148   // Takes ownership.
149   int Add(T* api_resource) { return data_->Add(api_resource); }
150
151   void Remove(const std::string& extension_id, int api_resource_id) {
152     data_->Remove(extension_id, api_resource_id);
153   }
154
155   T* Get(const std::string& extension_id, int api_resource_id) {
156     return data_->Get(extension_id, api_resource_id);
157   }
158
159   base::hash_set<int>* GetResourceIds(const std::string& extension_id) {
160     return data_->GetResourceIds(extension_id);
161   }
162
163   // BrowserContextKeyedAPI implementation.
164   static BrowserContextKeyedAPIFactory<ApiResourceManager<T> >*
165       GetFactoryInstance();
166
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);
170   }
171
172   // BrowserContextKeyedAPI implementation.
173   static const char* service_name() { return T::service_name(); }
174
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);
183   }
184
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());
193   }
194
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());
201   }
202
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> >;
214
215   static const bool kServiceHasOwnInstanceInIncognito = true;
216   static const bool kServiceIsNULLWhileTesting = true;
217
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;
225
226     ApiResourceData() : next_id_(1) {}
227
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;
234
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>();
239         }
240         extension_resource_map_[extension_id].insert(id);
241
242         return id;
243       }
244       return 0;
245     }
246
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);
254       }
255     }
256
257     T* Get(const std::string& extension_id, int api_resource_id) {
258       DCHECK(ThreadingTraits::IsCalledOnValidThread());
259       return GetOwnedResource(extension_id, api_resource_id);
260     }
261
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;
274       }
275       return false;
276     }
277
278     base::hash_set<int>* GetResourceIds(const std::string& extension_id) {
279       DCHECK(ThreadingTraits::IsCalledOnValidThread());
280       return GetOwnedResourceIds(extension_id);
281     }
282
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));
292       }
293     }
294
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));
304       }
305     }
306
307     void InititateCleanup() {
308       if (ThreadingTraits::IsCalledOnValidThread()) {
309         Cleanup();
310       } else {
311         ThreadingTraits::GetSequencedTaskRunner()->PostTask(
312             FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
313       }
314     }
315
316    private:
317     friend class base::RefCountedThreadSafe<ApiResourceData>;
318
319     virtual ~ApiResourceData() {}
320
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;
327     }
328
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;
334
335       return &extension_resource_map_[extension_id];
336     }
337
338     void CleanupResourcesFromUnloadedExtension(
339         const std::string& extension_id) {
340       CleanupResourcesFromExtension(extension_id, true);
341     }
342
343     void CleanupResourcesFromSuspendedExtension(
344         const std::string& extension_id) {
345       CleanupResourcesFromExtension(extension_id, false);
346     }
347
348     void CleanupResourcesFromExtension(const std::string& extension_id,
349                                        bool remove_all) {
350       DCHECK(ThreadingTraits::IsCalledOnValidThread());
351
352       if (extension_resource_map_.find(extension_id) ==
353           extension_resource_map_.end()) {
354         return;
355       }
356
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());
369         }
370
371         if (erase) {
372           api_resource_map_.erase(*it);
373           resource_ids.erase(it++);
374         } else {
375           ++it;
376         }
377       }  // end for
378
379       // Remove extension entry if we removed all its resources.
380       if (resource_ids.size() == 0) {
381         extension_resource_map_.erase(extension_id);
382       }
383     }
384
385     void Cleanup() {
386       DCHECK(ThreadingTraits::IsCalledOnValidThread());
387
388       api_resource_map_.clear();
389       extension_resource_map_.clear();
390     }
391
392     int GenerateId() { return next_id_++; }
393
394     int next_id_;
395     ApiResourceMap api_resource_map_;
396     ExtensionToResourceMap extension_resource_map_;
397   };
398
399   content::NotificationRegistrar registrar_;
400   scoped_refptr<ApiResourceData> data_;
401
402   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
403       extension_registry_observer_;
404 };
405
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.
410 //
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.
418 //
419 // In the cc file the user must define a GetFactoryInstance() and manage their
420 // own instances (typically using LazyInstance or Singleton).
421 //
422 // E.g.:
423 //
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 // };
436 //
437 // In the cc file:
438 //
439 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
440 //     ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
441 //         g_factory = LAZY_INSTANCE_INITIALIZER;
442 //
443 //
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));
457   }
458
459   static bool IsMessageLoopValid() {
460     return content::BrowserThread::GetBlockingPool() != NULL;
461   }
462
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);
469   }
470 };
471
472 }  // namespace extensions
473
474 #endif  // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_