Home | History | Annotate | Download | only in api
      1 // Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_MANAGER_H_
      6 #define CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_MANAGER_H_
      7 
      8 #include <map>
      9 
     10 #include "base/lazy_instance.h"
     11 #include "base/memory/linked_ptr.h"
     12 #include "base/threading/non_thread_safe.h"
     13 #include "chrome/browser/chrome_notification_types.h"
     14 #include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
     15 #include "chrome/common/extensions/extension.h"
     16 #include "components/browser_context_keyed_service/browser_context_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 
     22 namespace extensions {
     23 
     24 // An ApiResourceManager manages the lifetime of a set of resources that
     25 // ApiFunctions use. Examples are sockets or USB connections.
     26 //
     27 // Users of this class should define kThreadId to be the thread that
     28 // ApiResourceManager to works on. The default is defined in ApiResource.
     29 // The user must also define a static const char* service_name() that returns
     30 // the name of the service, and in order for ApiResourceManager to use
     31 // service_name() friend this class.
     32 //
     33 // In the cc file the user must define a GetFactoryInstance() and manage their
     34 // own instances (typically using LazyInstance or Singleton).
     35 //
     36 // E.g.:
     37 //
     38 // class Resource {
     39 //  public:
     40 //   static const BrowserThread::ID kThreadId = BrowserThread::FILE;
     41 //  private:
     42 //   friend class ApiResourceManager<Resource>;
     43 //   static const char* service_name() {
     44 //     return "ResourceManager";
     45 //    }
     46 // };
     47 //
     48 // In the cc file:
     49 //
     50 // static base::LazyInstance<ProfileKeyedAPIFactory<
     51 //     ApiResourceManager<Resource> > >
     52 //         g_factory = LAZY_INSTANCE_INITIALIZER;
     53 //
     54 //
     55 // template <>
     56 // ProfileKeyedAPIFactory<ApiResourceManager<Resource> >*
     57 // ApiResourceManager<Resource>::GetFactoryInstance() {
     58 //   return &g_factory.Get();
     59 // }
     60 template <class T>
     61 class ApiResourceManager : public ProfileKeyedAPI,
     62                            public base::NonThreadSafe,
     63                            public content::NotificationObserver {
     64  public:
     65   explicit ApiResourceManager(Profile* profile)
     66       : thread_id_(T::kThreadId),
     67         data_(new ApiResourceData(thread_id_)) {
     68     registrar_.Add(
     69       this,
     70       chrome::NOTIFICATION_EXTENSION_UNLOADED,
     71       content::NotificationService::AllSources());
     72   }
     73 
     74   // For Testing.
     75   static ApiResourceManager<T>* CreateApiResourceManagerForTest(
     76       Profile* profile,
     77       content::BrowserThread::ID thread_id) {
     78     ApiResourceManager* manager = new ApiResourceManager<T>(profile);
     79     manager->thread_id_ = thread_id;
     80     manager->data_.reset(new ApiResourceData(thread_id));
     81     return manager;
     82   }
     83 
     84   virtual ~ApiResourceManager() {
     85     DCHECK(CalledOnValidThread());
     86     DCHECK(content::BrowserThread::IsMessageLoopValid(thread_id_)) <<
     87         "A unit test is using an ApiResourceManager but didn't provide "
     88         "the thread message loop needed for that kind of resource. "
     89         "Please ensure that the appropriate message loop is operational.";
     90 
     91     content::BrowserThread::DeleteSoon(thread_id_, FROM_HERE, data_.release());
     92   }
     93 
     94   // ProfileKeyedAPI implementation.
     95   static ProfileKeyedAPIFactory<ApiResourceManager<T> >* GetFactoryInstance();
     96 
     97   // Convenience method to get the ApiResourceManager for a profile.
     98   static ApiResourceManager<T>* Get(Profile* profile) {
     99     return ProfileKeyedAPIFactory<ApiResourceManager<T> >::GetForProfile(
    100         profile);
    101   }
    102 
    103   // Takes ownership.
    104   int Add(T* api_resource) {
    105     return data_->Add(api_resource);
    106   }
    107 
    108   void Remove(const std::string& extension_id, int api_resource_id) {
    109     data_->Remove(extension_id, api_resource_id);
    110   }
    111 
    112   T* Get(const std::string& extension_id, int api_resource_id) {
    113     return data_->Get(extension_id, api_resource_id);
    114   }
    115 
    116  protected:
    117   // content::NotificationObserver:
    118   virtual void Observe(int type,
    119                        const content::NotificationSource& source,
    120                        const content::NotificationDetails& details) OVERRIDE {
    121     switch (type) {
    122       case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
    123         std::string id =
    124             content::Details<extensions::UnloadedExtensionInfo>(details)->
    125                 extension->id();
    126         data_->InitiateCleanup(id);
    127         break;
    128       }
    129     }
    130   }
    131 
    132  private:
    133   friend class ProfileKeyedAPIFactory<ApiResourceManager<T> >;
    134   // ProfileKeyedAPI implementation.
    135   static const char* service_name() {
    136     return T::service_name();
    137   }
    138   static const bool kServiceHasOwnInstanceInIncognito = true;
    139   static const bool kServiceIsNULLWhileTesting = true;
    140 
    141   // ApiResourceData class handles resource bookkeeping on a thread
    142   // where resource lifetime is handled.
    143   class ApiResourceData {
    144    public:
    145     typedef std::map<int, linked_ptr<T> > ApiResourceMap;
    146     // Lookup map from extension id's to allocated resource id's.
    147     typedef std::map<std::string, base::hash_set<int> > ExtensionToResourceMap;
    148 
    149     explicit ApiResourceData(const content::BrowserThread::ID thread_id)
    150         : next_id_(1),
    151           thread_id_(thread_id) {
    152     }
    153 
    154     int Add(T* api_resource) {
    155       DCHECK(content::BrowserThread::CurrentlyOn(thread_id_));
    156       int id = GenerateId();
    157       if (id > 0) {
    158         linked_ptr<T> resource_ptr(api_resource);
    159         api_resource_map_[id] = resource_ptr;
    160 
    161         const std::string& extension_id = api_resource->owner_extension_id();
    162         if (extension_resource_map_.find(extension_id) ==
    163             extension_resource_map_.end()) {
    164           extension_resource_map_[extension_id] = base::hash_set<int>();
    165         }
    166         extension_resource_map_[extension_id].insert(id);
    167 
    168        return id;
    169      }
    170      return 0;
    171     }
    172 
    173     void Remove(const std::string& extension_id, int api_resource_id) {
    174       DCHECK(content::BrowserThread::CurrentlyOn(thread_id_));
    175       if (GetOwnedResource(extension_id, api_resource_id) != NULL) {
    176         DCHECK(extension_resource_map_.find(extension_id) !=
    177                extension_resource_map_.end());
    178         extension_resource_map_[extension_id].erase(api_resource_id);
    179         api_resource_map_.erase(api_resource_id);
    180       }
    181     }
    182 
    183     T* Get(const std::string& extension_id, int api_resource_id) {
    184       DCHECK(content::BrowserThread::CurrentlyOn(thread_id_));
    185       return GetOwnedResource(extension_id, api_resource_id);
    186     }
    187 
    188     void InitiateCleanup(const std::string& extension_id) {
    189       content::BrowserThread::PostTask(thread_id_, FROM_HERE,
    190           base::Bind(&ApiResourceData::CleanupResourcesFromExtension,
    191                      base::Unretained(this), extension_id));
    192     }
    193 
    194    private:
    195     T* GetOwnedResource(const std::string& extension_id,
    196                         int api_resource_id) {
    197       linked_ptr<T> ptr = api_resource_map_[api_resource_id];
    198       T* resource = ptr.get();
    199       if (resource && extension_id == resource->owner_extension_id())
    200         return resource;
    201       return NULL;
    202     }
    203 
    204     void CleanupResourcesFromExtension(const std::string& extension_id) {
    205       DCHECK(content::BrowserThread::CurrentlyOn(thread_id_));
    206       if (extension_resource_map_.find(extension_id) !=
    207           extension_resource_map_.end()) {
    208         base::hash_set<int>& resource_ids =
    209             extension_resource_map_[extension_id];
    210         for (base::hash_set<int>::iterator it = resource_ids.begin();
    211              it != resource_ids.end(); ++it) {
    212           api_resource_map_.erase(*it);
    213         }
    214         extension_resource_map_.erase(extension_id);
    215       }
    216     }
    217 
    218     int GenerateId() {
    219       return next_id_++;
    220     }
    221 
    222     int next_id_;
    223     const content::BrowserThread::ID thread_id_;
    224     ApiResourceMap api_resource_map_;
    225     ExtensionToResourceMap extension_resource_map_;
    226   };
    227 
    228   content::BrowserThread::ID thread_id_;
    229   content::NotificationRegistrar registrar_;
    230   scoped_ptr<ApiResourceData> data_;
    231 };
    232 
    233 }  // namespace extensions
    234 
    235 #endif  // CHROME_BROWSER_EXTENSIONS_API_API_RESOURCE_MANAGER_H_
    236