#ifndef EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
#define EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/notreached.h"
#include "base/scoped_observation.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_factory.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/process_manager_factory.h"
#include "extensions/browser/process_manager_observer.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
namespace extensions {
namespace api {
class BluetoothSocketApiFunction;
class BluetoothSocketEventDispatcher;
class SerialConnectFunction;
class SerialPortManager;
class TCPServerSocketEventDispatcher;
class TCPSocketEventDispatcher;
class UDPSocketEventDispatcher;
}
template <typename T>
struct NamedThreadTraits { … };
template <class T, typename ThreadingTraits = NamedThreadTraits<T>>
class ApiResourceManager : public BrowserContextKeyedAPI,
public ExtensionRegistryObserver,
public ProcessManagerObserver {
public:
explicit ApiResourceManager(content::BrowserContext* context)
: … { … }
virtual ~ApiResourceManager() { … }
int Add(T* api_resource) { … }
void Remove(const ExtensionId& extension_id, int api_resource_id) { … }
T* Get(const ExtensionId& extension_id, int api_resource_id) { … }
std::unordered_set<int>* GetResourceIds(const ExtensionId& extension_id) { … }
static BrowserContextKeyedAPIFactory<ApiResourceManager<T>>*
GetFactoryInstance();
static ApiResourceManager<T>* Get(content::BrowserContext* context) { … }
static const char* service_name() { … }
bool Replace(const ExtensionId& extension_id,
int api_resource_id,
T* resource) { … }
protected:
void OnBackgroundHostClose(const ExtensionId& extension_id) override { … }
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionReason reason) override { … }
private:
friend class BluetoothAPI;
friend class api::BluetoothSocketApiFunction;
friend class api::BluetoothSocketEventDispatcher;
friend class api::SerialConnectFunction;
friend class api::SerialPortManager;
friend class api::TCPServerSocketEventDispatcher;
friend class api::TCPSocketEventDispatcher;
friend class api::UDPSocketEventDispatcher;
friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T>>;
static const bool kServiceHasOwnInstanceInIncognito = true;
static const bool kServiceIsNULLWhileTesting = true;
class ApiResourceData : public base::RefCountedThreadSafe<ApiResourceData> {
public:
typedef std::map<int, std::unique_ptr<T>> ApiResourceMap;
typedef std::map<std::string, std::unordered_set<int>>
ExtensionToResourceMap;
ApiResourceData() : next_id_(1) { DETACH_FROM_SEQUENCE(sequence_checker_); }
int Add(T* api_resource) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
int id = GenerateId();
if (id > 0) {
api_resource_map_[id] = base::WrapUnique<T>(api_resource);
const ExtensionId& extension_id = api_resource->owner_extension_id();
ExtensionToResourceMap::iterator it =
extension_resource_map_.find(extension_id);
if (it == extension_resource_map_.end()) {
it = extension_resource_map_
.insert(
std::make_pair(extension_id, std::unordered_set<int>()))
.first;
}
it->second.insert(id);
return id;
}
return 0;
}
void Remove(const ExtensionId& extension_id, int api_resource_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (GetOwnedResource(extension_id, api_resource_id)) {
ExtensionToResourceMap::iterator it =
extension_resource_map_.find(extension_id);
it->second.erase(api_resource_id);
api_resource_map_.erase(api_resource_id);
}
}
T* Get(const ExtensionId& extension_id, int api_resource_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return GetOwnedResource(extension_id, api_resource_id);
}
bool Replace(const ExtensionId& extension_id,
int api_resource_id,
T* api_resource) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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] = base::WrapUnique<T>(api_resource);
return true;
}
return false;
}
std::unordered_set<int>* GetResourceIds(const ExtensionId& extension_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return GetOwnedResourceIds(extension_id);
}
void InitiateExtensionUnloadedCleanup(const ExtensionId& extension_id) {
ThreadingTraits::GetSequencedTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
&ApiResourceData::CleanupResourcesFromUnloadedExtension, this,
extension_id));
}
void InitiateExtensionSuspendedCleanup(const ExtensionId& extension_id) {
ThreadingTraits::GetSequencedTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
&ApiResourceData::CleanupResourcesFromSuspendedExtension, this,
extension_id));
}
void InititateCleanup() {
ThreadingTraits::GetSequencedTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ApiResourceData::Cleanup, this));
}
private:
friend class base::RefCountedThreadSafe<ApiResourceData>;
virtual ~ApiResourceData() {}
T* GetOwnedResource(const ExtensionId& extension_id, int api_resource_id) {
const std::unique_ptr<T>& ptr = api_resource_map_[api_resource_id];
T* resource = ptr.get();
if (resource && extension_id == resource->owner_extension_id())
return resource;
return NULL;
}
std::unordered_set<int>* GetOwnedResourceIds(
const ExtensionId& extension_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ExtensionToResourceMap::iterator it =
extension_resource_map_.find(extension_id);
if (it == extension_resource_map_.end())
return NULL;
return &(it->second);
}
void CleanupResourcesFromUnloadedExtension(
const ExtensionId& extension_id) {
CleanupResourcesFromExtension(extension_id, true);
}
void CleanupResourcesFromSuspendedExtension(
const ExtensionId& extension_id) {
CleanupResourcesFromExtension(extension_id, false);
}
void CleanupResourcesFromExtension(const ExtensionId& extension_id,
bool remove_all) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ExtensionToResourceMap::iterator extension_it =
extension_resource_map_.find(extension_id);
if (extension_it == extension_resource_map_.end())
return;
std::unordered_set<int>& resource_ids = extension_it->second;
for (std::unordered_set<int>::iterator it = resource_ids.begin();
it != resource_ids.end();) {
bool erase = false;
if (remove_all) {
erase = true;
} else {
std::unique_ptr<T>& ptr = api_resource_map_[*it];
T* resource = ptr.get();
erase = (resource && !resource->IsPersistent());
}
if (erase) {
api_resource_map_.erase(*it);
resource_ids.erase(it++);
} else {
++it;
}
}
if (resource_ids.size() == 0) {
extension_resource_map_.erase(extension_id);
}
}
void Cleanup() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ApiResourceMap local_api_resource_map;
api_resource_map_.swap(local_api_resource_map);
local_api_resource_map.clear();
ExtensionToResourceMap local_extension_resource_map;
extension_resource_map_.swap(local_extension_resource_map);
local_extension_resource_map.clear();
}
int GenerateId() { return next_id_++; }
int next_id_;
ApiResourceMap api_resource_map_;
ExtensionToResourceMap extension_resource_map_;
SEQUENCE_CHECKER(sequence_checker_);
};
scoped_refptr<ApiResourceData> data_;
base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
extension_registry_observation_{this};
base::ScopedObservation<ProcessManager, ProcessManagerObserver>
process_manager_observation_{this};
SEQUENCE_CHECKER(sequence_checker_);
};
BrowserContextFactoryDependencies<ApiResourceManager<T>>;
}
#endif