Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / api / api_resource_manager.h
index e0c9b81..0eaf36d 100644 (file)
@@ -11,8 +11,8 @@
 #include "base/lazy_instance.h"
 #include "base/memory/linked_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "base/scoped_observer.h"
 #include "base/threading/non_thread_safe.h"
-#include "chrome/browser/chrome_notification_types.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_observer.h"
@@ -20,6 +20,9 @@
 #include "content/public/browser/notification_service.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
 #include "extensions/browser/extension_host.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/browser/notification_types.h"
 #include "extensions/common/extension.h"
 
 namespace extensions {
@@ -27,17 +30,54 @@ namespace extensions {
 namespace api {
 class BluetoothSocketApiFunction;
 class BluetoothSocketEventDispatcher;
-class SerialEventDispatcher;
 }
 
 namespace core_api {
+class SerialEventDispatcher;
 class TCPServerSocketEventDispatcher;
 class TCPSocketEventDispatcher;
 class UDPSocketEventDispatcher;
 }
 
+template <typename T>
+struct NamedThreadTraits {
+  static bool IsCalledOnValidThread() {
+    return content::BrowserThread::CurrentlyOn(T::kThreadId);
+  }
+
+  static bool IsMessageLoopValid() {
+    return content::BrowserThread::IsMessageLoopValid(T::kThreadId);
+  }
+
+  static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
+    return content::BrowserThread::GetMessageLoopProxyForThread(T::kThreadId);
+  }
+};
+
+template <typename T>
+struct TestThreadTraits {
+  static bool IsCalledOnValidThread() {
+    return content::BrowserThread::CurrentlyOn(thread_id_);
+  }
+
+  static bool IsMessageLoopValid() {
+    return content::BrowserThread::IsMessageLoopValid(thread_id_);
+  }
+
+  static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
+    return content::BrowserThread::GetMessageLoopProxyForThread(thread_id_);
+  }
+
+  static content::BrowserThread::ID thread_id_;
+};
+
+template <typename T>
+content::BrowserThread::ID TestThreadTraits<T>::thread_id_ =
+    content::BrowserThread::IO;
+
 // An ApiResourceManager manages the lifetime of a set of resources that
-// ApiFunctions use. Examples are sockets or USB connections.
+// that live on named threads (i.e. BrowserThread::IO) which ApiFunctions use.
+// Examples of such resources are sockets or USB connections.
 //
 // Users of this class should define kThreadId to be the thread that
 // ApiResourceManager to works on. The default is defined in ApiResource.
@@ -72,34 +112,32 @@ class UDPSocketEventDispatcher;
 // ApiResourceManager<Resource>::GetFactoryInstance() {
 //   return g_factory.Pointer();
 // }
-template <class T>
+template <class T, typename ThreadingTraits = NamedThreadTraits<T> >
 class ApiResourceManager : public BrowserContextKeyedAPI,
                            public base::NonThreadSafe,
-                           public content::NotificationObserver {
+                           public content::NotificationObserver,
+                           public ExtensionRegistryObserver {
  public:
   explicit ApiResourceManager(content::BrowserContext* context)
-      : thread_id_(T::kThreadId), data_(new ApiResourceData(thread_id_)) {
-    registrar_.Add(this,
-                   chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
-                   content::NotificationService::AllSources());
+      : data_(new ApiResourceData()), extension_registry_observer_(this) {
+    extension_registry_observer_.Add(ExtensionRegistry::Get(context));
     registrar_.Add(this,
-                   chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
+                   extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
                    content::NotificationService::AllSources());
   }
-
   // For Testing.
-  static ApiResourceManager<T>* CreateApiResourceManagerForTest(
-      content::BrowserContext* context,
-      content::BrowserThread::ID thread_id) {
-    ApiResourceManager* manager = new ApiResourceManager<T>(context);
-    manager->thread_id_ = thread_id;
-    manager->data_ = new ApiResourceData(thread_id);
+  static ApiResourceManager<T, TestThreadTraits<T> >*
+  CreateApiResourceManagerForTest(content::BrowserContext* context,
+                                  content::BrowserThread::ID thread_id) {
+    TestThreadTraits<T>::thread_id_ = thread_id;
+    ApiResourceManager<T, TestThreadTraits<T> >* manager =
+        new ApiResourceManager<T, TestThreadTraits<T> >(context);
     return manager;
   }
 
   virtual ~ApiResourceManager() {
     DCHECK(CalledOnValidThread());
-    DCHECK(content::BrowserThread::IsMessageLoopValid(thread_id_))
+    DCHECK(ThreadingTraits::IsMessageLoopValid())
         << "A unit test is using an ApiResourceManager but didn't provide "
            "the thread message loop needed for that kind of resource. "
            "Please ensure that the appropriate message loop is operational.";
@@ -107,15 +145,6 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     data_->InititateCleanup();
   }
 
-  // BrowserContextKeyedAPI implementation.
-  static BrowserContextKeyedAPIFactory<ApiResourceManager<T> >*
-      GetFactoryInstance();
-
-  // Convenience method to get the ApiResourceManager for a profile.
-  static ApiResourceManager<T>* Get(content::BrowserContext* context) {
-    return BrowserContextKeyedAPIFactory<ApiResourceManager<T> >::Get(context);
-  }
-
   // Takes ownership.
   int Add(T* api_resource) { return data_->Add(api_resource); }
 
@@ -131,24 +160,44 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     return data_->GetResourceIds(extension_id);
   }
 
+  // BrowserContextKeyedAPI implementation.
+  static BrowserContextKeyedAPIFactory<ApiResourceManager<T> >*
+      GetFactoryInstance();
+
+  // Convenience method to get the ApiResourceManager for a profile.
+  static ApiResourceManager<T>* Get(content::BrowserContext* context) {
+    return BrowserContextKeyedAPIFactory<ApiResourceManager<T> >::Get(context);
+  }
+
+  // BrowserContextKeyedAPI implementation.
+  static const char* service_name() { return T::service_name(); }
+
+  // Change the resource mapped to this |extension_id| at this
+  // |api_resource_id| to |resource|. Returns true and succeeds unless
+  // |api_resource_id| does not already identify a resource held by
+  // |extension_id|.
+  bool Replace(const std::string& extension_id,
+               int api_resource_id,
+               T* resource) {
+    return data_->Replace(extension_id, api_resource_id, resource);
+  }
+
  protected:
   // content::NotificationObserver:
   virtual void Observe(int type,
                        const content::NotificationSource& source,
                        const content::NotificationDetails& details) OVERRIDE {
-    switch (type) {
-      case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
-        std::string id = content::Details<extensions::UnloadedExtensionInfo>(
-                             details)->extension->id();
-        data_->InitiateExtensionUnloadedCleanup(id);
-        break;
-      }
-      case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
-        ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
-        data_->InitiateExtensionSuspendedCleanup(host->extension_id());
-        break;
-      }
-    }
+    DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, type);
+    ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
+    data_->InitiateExtensionSuspendedCleanup(host->extension_id());
+  }
+
+  // ExtensionRegistryObserver:
+  virtual void OnExtensionUnloaded(
+      content::BrowserContext* browser_context,
+      const Extension* extension,
+      UnloadedExtensionInfo::Reason reason) OVERRIDE {
+    data_->InitiateExtensionUnloadedCleanup(extension->id());
   }
 
  private:
@@ -157,15 +206,12 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
   friend class BluetoothAPI;
   friend class api::BluetoothSocketApiFunction;
   friend class api::BluetoothSocketEventDispatcher;
-  friend class api::SerialEventDispatcher;
+  friend class core_api::SerialEventDispatcher;
   friend class core_api::TCPServerSocketEventDispatcher;
   friend class core_api::TCPSocketEventDispatcher;
   friend class core_api::UDPSocketEventDispatcher;
   friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T> >;
 
-  // BrowserContextKeyedAPI implementation.
-  static const char* service_name() { return T::service_name(); }
-
   static const bool kServiceHasOwnInstanceInIncognito = true;
   static const bool kServiceIsNULLWhileTesting = true;
 
@@ -177,11 +223,10 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     // Lookup map from extension id's to allocated resource id's.
     typedef std::map<std::string, base::hash_set<int> > ExtensionToResourceMap;
 
-    explicit ApiResourceData(const content::BrowserThread::ID thread_id)
-        : next_id_(1), thread_id_(thread_id) {}
+    ApiResourceData() : next_id_(1) {}
 
     int Add(T* api_resource) {
-      DCHECK_CURRENTLY_ON(thread_id_);
+      DCHECK(ThreadingTraits::IsCalledOnValidThread());
       int id = GenerateId();
       if (id > 0) {
         linked_ptr<T> resource_ptr(api_resource);
@@ -200,7 +245,7 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     }
 
     void Remove(const std::string& extension_id, int api_resource_id) {
-      DCHECK_CURRENTLY_ON(thread_id_);
+      DCHECK(ThreadingTraits::IsCalledOnValidThread());
       if (GetOwnedResource(extension_id, api_resource_id) != NULL) {
         DCHECK(extension_resource_map_.find(extension_id) !=
                extension_resource_map_.end());
@@ -210,21 +255,36 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     }
 
     T* Get(const std::string& extension_id, int api_resource_id) {
-      DCHECK_CURRENTLY_ON(thread_id_);
+      DCHECK(ThreadingTraits::IsCalledOnValidThread());
       return GetOwnedResource(extension_id, api_resource_id);
     }
 
+    // Change the resource mapped to this |extension_id| at this
+    // |api_resource_id| to |resource|. Returns true and succeeds unless
+    // |api_resource_id| does not already identify a resource held by
+    // |extension_id|.
+    bool Replace(const std::string& extension_id,
+                 int api_resource_id,
+                 T* api_resource) {
+      DCHECK(ThreadingTraits::IsCalledOnValidThread());
+      T* old_resource = api_resource_map_[api_resource_id].get();
+      if (old_resource && extension_id == old_resource->owner_extension_id()) {
+        api_resource_map_[api_resource_id] = linked_ptr<T>(api_resource);
+        return true;
+      }
+      return false;
+    }
+
     base::hash_set<int>* GetResourceIds(const std::string& extension_id) {
-      DCHECK_CURRENTLY_ON(thread_id_);
+      DCHECK(ThreadingTraits::IsCalledOnValidThread());
       return GetOwnedResourceIds(extension_id);
     }
 
     void InitiateExtensionUnloadedCleanup(const std::string& extension_id) {
-      if (content::BrowserThread::CurrentlyOn(thread_id_)) {
+      if (ThreadingTraits::IsCalledOnValidThread()) {
         CleanupResourcesFromUnloadedExtension(extension_id);
       } else {
-        content::BrowserThread::PostTask(
-            thread_id_,
+        ThreadingTraits::GetSequencedTaskRunner()->PostTask(
             FROM_HERE,
             base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension,
                        this,
@@ -233,11 +293,10 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     }
 
     void InitiateExtensionSuspendedCleanup(const std::string& extension_id) {
-      if (content::BrowserThread::CurrentlyOn(thread_id_)) {
+      if (ThreadingTraits::IsCalledOnValidThread()) {
         CleanupResourcesFromSuspendedExtension(extension_id);
       } else {
-        content::BrowserThread::PostTask(
-            thread_id_,
+        ThreadingTraits::GetSequencedTaskRunner()->PostTask(
             FROM_HERE,
             base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension,
                        this,
@@ -246,11 +305,11 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     }
 
     void InititateCleanup() {
-      if (content::BrowserThread::CurrentlyOn(thread_id_)) {
+      if (ThreadingTraits::IsCalledOnValidThread()) {
         Cleanup();
       } else {
-        content::BrowserThread::PostTask(
-            thread_id_, FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
+        ThreadingTraits::GetSequencedTaskRunner()->PostTask(
+            FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this));
       }
     }
 
@@ -268,7 +327,7 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     }
 
     base::hash_set<int>* GetOwnedResourceIds(const std::string& extension_id) {
-      DCHECK_CURRENTLY_ON(thread_id_);
+      DCHECK(ThreadingTraits::IsCalledOnValidThread());
       if (extension_resource_map_.find(extension_id) ==
           extension_resource_map_.end())
         return NULL;
@@ -288,7 +347,7 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
 
     void CleanupResourcesFromExtension(const std::string& extension_id,
                                        bool remove_all) {
-      DCHECK_CURRENTLY_ON(thread_id_);
+      DCHECK(ThreadingTraits::IsCalledOnValidThread());
 
       if (extension_resource_map_.find(extension_id) ==
           extension_resource_map_.end()) {
@@ -324,7 +383,7 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     }
 
     void Cleanup() {
-      DCHECK_CURRENTLY_ON(thread_id_);
+      DCHECK(ThreadingTraits::IsCalledOnValidThread());
 
       api_resource_map_.clear();
       extension_resource_map_.clear();
@@ -333,14 +392,81 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
     int GenerateId() { return next_id_++; }
 
     int next_id_;
-    const content::BrowserThread::ID thread_id_;
     ApiResourceMap api_resource_map_;
     ExtensionToResourceMap extension_resource_map_;
   };
 
-  content::BrowserThread::ID thread_id_;
   content::NotificationRegistrar registrar_;
   scoped_refptr<ApiResourceData> data_;
+
+  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
+      extension_registry_observer_;
+};
+
+// With WorkerPoolThreadTraits, ApiResourceManager can be used to manage the
+// lifetime of a set of resources that live on sequenced task runner threads
+// which ApiFunctions use. Examples of such resources are temporary file
+// resources produced by certain API calls.
+//
+// Instead of kThreadId. classes used for tracking such resources should define
+// kSequenceToken and kShutdownBehavior to identify sequence task runner for
+// ApiResourceManager to work on and how pending tasks should behave on
+// shutdown.
+// The user must also define a static const char* service_name() that returns
+// the name of the service, and in order for ApiWorkerPoolResourceManager to use
+// service_name() friend this class.
+//
+// In the cc file the user must define a GetFactoryInstance() and manage their
+// own instances (typically using LazyInstance or Singleton).
+//
+// E.g.:
+//
+// class PoolResource {
+//  public:
+//   static const char kSequenceToken[] = "temp_files";
+//   static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior =
+//       base::SequencedWorkerPool::BLOCK_SHUTDOWN;
+//  private:
+//   friend class ApiResourceManager<WorkerPoolResource,
+//                                   WorkerPoolThreadTraits>;
+//   static const char* service_name() {
+//     return "TempFilesResourceManager";
+//    }
+// };
+//
+// In the cc file:
+//
+// static base::LazyInstance<BrowserContextKeyedAPIFactory<
+//     ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
+//         g_factory = LAZY_INSTANCE_INITIALIZER;
+//
+//
+// template <>
+// BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >*
+// ApiResourceManager<WorkerPoolPoolResource,
+//                    WorkerPoolThreadTraits>::GetFactoryInstance() {
+//   return g_factory.Pointer();
+// }
+template <typename T>
+struct WorkerPoolThreadTraits {
+  static bool IsCalledOnValidThread() {
+    return content::BrowserThread::GetBlockingPool()
+        ->IsRunningSequenceOnCurrentThread(
+            content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
+                T::kSequenceToken));
+  }
+
+  static bool IsMessageLoopValid() {
+    return content::BrowserThread::GetBlockingPool() != NULL;
+  }
+
+  static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
+    return content::BrowserThread::GetBlockingPool()
+        ->GetSequencedTaskRunnerWithShutdownBehavior(
+            content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
+                T::kSequenceToken),
+            T::kShutdownBehavior);
+  }
 };
 
 }  // namespace extensions