This source file includes following definitions.
- Get
- Add
- ImageDataUsable
- ExpireEntries
- IncrementInsertionPoint
- GetInstance
- Get
- Add
- ImageDataUsable
- DidDeleteInstance
- OnTimer
- is_candidate_for_reuse_
- AsPPB_ImageData_API
- LastPluginRefWasDeleted
- InstanceWasDeleted
- Describe
- GetSharedMemory
- SetIsCandidateForReuse
- RecycleToPlugin
- Map
- Unmap
- GetPlatformCanvas
- GetCanvas
- NullHandle
- HandleFromInt
- map_count_
- Map
- Unmap
- GetPlatformCanvas
- GetCanvas
- CreateProxyResource
- OnMessageReceived
- CreateImageData
- OnHostMsgCreatePlatform
- OnHostMsgCreateSimple
- OnPluginMsgNotifyUnusedImageData
#include "ppapi/proxy/ppb_image_data_proxy.h"
#include <string.h>
#include <map>
#include <vector>
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "build/build_config.h"
#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/proxy/enter_proxy.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/plugin_globals.h"
#include "ppapi/proxy/plugin_resource_tracker.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/host_resource.h"
#include "ppapi/shared_impl/proxy_lock.h"
#include "ppapi/shared_impl/resource.h"
#include "ppapi/shared_impl/scoped_pp_resource.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/thunk.h"
#if !defined(OS_NACL)
#include "skia/ext/platform_canvas.h"
#include "ui/surface/transport_dib.h"
#endif
using ppapi::thunk::PPB_ImageData_API;
namespace ppapi {
namespace proxy {
namespace {
static const int kMaxAgeSeconds = 2;
struct ImageDataCacheEntry {
ImageDataCacheEntry() : added_time(), usable(false), image() {}
ImageDataCacheEntry(ImageData* i)
: added_time(base::TimeTicks::Now()),
usable(false),
image(i) {
}
base::TimeTicks added_time;
bool usable;
scoped_refptr<ImageData> image;
};
class ImageDataInstanceCache {
public:
ImageDataInstanceCache() : next_insertion_point_(0) {}
scoped_refptr<ImageData> Get(PPB_ImageData_Shared::ImageDataType type,
int width, int height,
PP_ImageDataFormat format);
void Add(ImageData* image_data);
void ImageDataUsable(ImageData* image_data);
bool ExpireEntries();
private:
void IncrementInsertionPoint();
const static int kCacheSize = 2;
ImageDataCacheEntry images_[kCacheSize];
int next_insertion_point_;
};
scoped_refptr<ImageData> ImageDataInstanceCache::Get(
PPB_ImageData_Shared::ImageDataType type,
int width, int height,
PP_ImageDataFormat format) {
for (int i = 0; i < kCacheSize; i++) {
if (!images_[i].usable)
continue;
if (images_[i].image->type() != type)
continue;
const PP_ImageDataDesc& desc = images_[i].image->desc();
if (desc.format == format &&
desc.size.width == width && desc.size.height == height) {
scoped_refptr<ImageData> ret(images_[i].image);
images_[i] = ImageDataCacheEntry();
next_insertion_point_ = i;
return ret;
}
}
return scoped_refptr<ImageData>();
}
void ImageDataInstanceCache::Add(ImageData* image_data) {
images_[next_insertion_point_] = ImageDataCacheEntry(image_data);
IncrementInsertionPoint();
}
void ImageDataInstanceCache::ImageDataUsable(ImageData* image_data) {
for (int i = 0; i < kCacheSize; i++) {
if (images_[i].image.get() == image_data) {
images_[i].usable = true;
if (next_insertion_point_ == i)
IncrementInsertionPoint();
return;
}
}
}
bool ImageDataInstanceCache::ExpireEntries() {
base::TimeTicks threshold_time =
base::TimeTicks::Now() - base::TimeDelta::FromSeconds(kMaxAgeSeconds);
bool has_entry = false;
for (int i = 0; i < kCacheSize; i++) {
if (images_[i].image.get()) {
if (images_[i].added_time <= threshold_time) {
images_[i] = ImageDataCacheEntry();
next_insertion_point_ = i;
} else {
has_entry = true;
}
}
}
return has_entry;
}
void ImageDataInstanceCache::IncrementInsertionPoint() {
next_insertion_point_++;
if (next_insertion_point_ >= kCacheSize)
next_insertion_point_ = 0;
}
class ImageDataCache {
public:
ImageDataCache() : weak_factory_(this) {}
~ImageDataCache() {}
static ImageDataCache* GetInstance();
scoped_refptr<ImageData> Get(PP_Instance instance,
PPB_ImageData_Shared::ImageDataType type,
int width, int height,
PP_ImageDataFormat format);
void Add(ImageData* image_data);
void ImageDataUsable(ImageData* image_data);
void DidDeleteInstance(PP_Instance instance);
private:
friend struct LeakySingletonTraits<ImageDataCache>;
void OnTimer(PP_Instance instance);
typedef std::map<PP_Instance, ImageDataInstanceCache> CacheMap;
CacheMap cache_;
base::WeakPtrFactory<ImageDataCache> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ImageDataCache);
};
ImageDataCache* ImageDataCache::GetInstance() {
return Singleton<ImageDataCache,
LeakySingletonTraits<ImageDataCache> >::get();
}
scoped_refptr<ImageData> ImageDataCache::Get(
PP_Instance instance,
PPB_ImageData_Shared::ImageDataType type,
int width, int height,
PP_ImageDataFormat format) {
CacheMap::iterator found = cache_.find(instance);
if (found == cache_.end())
return scoped_refptr<ImageData>();
return found->second.Get(type, width, height, format);
}
void ImageDataCache::Add(ImageData* image_data) {
cache_[image_data->pp_instance()].Add(image_data);
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
RunWhileLocked(base::Bind(&ImageDataCache::OnTimer,
weak_factory_.GetWeakPtr(),
image_data->pp_instance())),
base::TimeDelta::FromSeconds(kMaxAgeSeconds));
}
void ImageDataCache::ImageDataUsable(ImageData* image_data) {
CacheMap::iterator found = cache_.find(image_data->pp_instance());
if (found != cache_.end())
found->second.ImageDataUsable(image_data);
}
void ImageDataCache::DidDeleteInstance(PP_Instance instance) {
cache_.erase(instance);
}
void ImageDataCache::OnTimer(PP_Instance instance) {
CacheMap::iterator found = cache_.find(instance);
if (found == cache_.end())
return;
if (!found->second.ExpireEntries()) {
cache_.erase(found);
}
}
}
ImageData::ImageData(const HostResource& resource,
PPB_ImageData_Shared::ImageDataType type,
const PP_ImageDataDesc& desc)
: Resource(OBJECT_IS_PROXY, resource),
type_(type),
desc_(desc),
is_candidate_for_reuse_(false) {
}
ImageData::~ImageData() {
}
PPB_ImageData_API* ImageData::AsPPB_ImageData_API() {
return this;
}
void ImageData::LastPluginRefWasDeleted() {
if (is_candidate_for_reuse_)
ImageDataCache::GetInstance()->Add(this);
}
void ImageData::InstanceWasDeleted() {
ImageDataCache::GetInstance()->DidDeleteInstance(pp_instance());
}
PP_Bool ImageData::Describe(PP_ImageDataDesc* desc) {
memcpy(desc, &desc_, sizeof(PP_ImageDataDesc));
return PP_TRUE;
}
int32_t ImageData::GetSharedMemory(int* ,
uint32_t* ) {
return PP_ERROR_NOACCESS;
}
void ImageData::SetIsCandidateForReuse() {
is_candidate_for_reuse_ = true;
}
void ImageData::RecycleToPlugin(bool zero_contents) {
is_candidate_for_reuse_ = false;
if (zero_contents) {
void* data = Map();
memset(data, 0, desc_.stride * desc_.size.height);
Unmap();
}
}
#if !defined(OS_NACL)
PlatformImageData::PlatformImageData(const HostResource& resource,
const PP_ImageDataDesc& desc,
ImageHandle handle)
: ImageData(resource, PPB_ImageData_Shared::PLATFORM, desc) {
#if defined(OS_WIN)
transport_dib_.reset(TransportDIB::CreateWithHandle(handle));
#else
transport_dib_.reset(TransportDIB::Map(handle));
#endif
}
PlatformImageData::~PlatformImageData() {
}
void* PlatformImageData::Map() {
if (!mapped_canvas_.get()) {
mapped_canvas_.reset(transport_dib_->GetPlatformCanvas(desc_.size.width,
desc_.size.height));
if (!mapped_canvas_.get())
return NULL;
}
const SkBitmap& bitmap =
skia::GetTopDevice(*mapped_canvas_)->accessBitmap(true);
bitmap.lockPixels();
return bitmap.getAddr(0, 0);
}
void PlatformImageData::Unmap() {
}
SkCanvas* PlatformImageData::GetPlatformCanvas() {
return mapped_canvas_.get();
}
SkCanvas* PlatformImageData::GetCanvas() {
return mapped_canvas_.get();
}
ImageHandle PlatformImageData::NullHandle() {
#if defined(OS_WIN)
return NULL;
#elif defined(TOOLKIT_GTK)
return 0;
#else
return ImageHandle();
#endif
}
ImageHandle PlatformImageData::HandleFromInt(int32_t i) {
#if defined(OS_WIN)
return reinterpret_cast<ImageHandle>(i);
#elif defined(TOOLKIT_GTK)
return static_cast<ImageHandle>(i);
#else
return ImageHandle(i, false);
#endif
}
#endif
SimpleImageData::SimpleImageData(const HostResource& resource,
const PP_ImageDataDesc& desc,
const base::SharedMemoryHandle& handle)
: ImageData(resource, PPB_ImageData_Shared::SIMPLE, desc),
shm_(handle, false ),
size_(desc.size.width * desc.size.height * 4),
map_count_(0) {
}
SimpleImageData::~SimpleImageData() {
}
void* SimpleImageData::Map() {
if (map_count_++ == 0)
shm_.Map(size_);
return shm_.memory();
}
void SimpleImageData::Unmap() {
if (--map_count_ == 0)
shm_.Unmap();
}
SkCanvas* SimpleImageData::GetPlatformCanvas() {
return NULL;
}
SkCanvas* SimpleImageData::GetCanvas() {
return NULL;
}
PPB_ImageData_Proxy::PPB_ImageData_Proxy(Dispatcher* dispatcher)
: InterfaceProxy(dispatcher) {
}
PPB_ImageData_Proxy::~PPB_ImageData_Proxy() {
}
PP_Resource PPB_ImageData_Proxy::CreateProxyResource(
PP_Instance instance,
PPB_ImageData_Shared::ImageDataType type,
PP_ImageDataFormat format,
const PP_Size& size,
PP_Bool init_to_zero) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
if (!dispatcher)
return 0;
scoped_refptr<ImageData> cached_image_data =
ImageDataCache::GetInstance()->Get(instance, type,
size.width, size.height, format);
if (cached_image_data.get()) {
cached_image_data->RecycleToPlugin(PP_ToBool(init_to_zero));
return cached_image_data->GetReference();
}
HostResource result;
PP_ImageDataDesc desc;
switch (type) {
case PPB_ImageData_Shared::SIMPLE: {
ppapi::proxy::SerializedHandle image_handle_wrapper;
dispatcher->Send(new PpapiHostMsg_PPBImageData_CreateSimple(
kApiID, instance, format, size, init_to_zero,
&result, &desc, &image_handle_wrapper));
if (image_handle_wrapper.is_shmem()) {
base::SharedMemoryHandle image_handle = image_handle_wrapper.shmem();
if (!result.is_null())
return
(new SimpleImageData(result, desc, image_handle))->GetReference();
}
break;
}
case PPB_ImageData_Shared::PLATFORM: {
#if !defined(OS_NACL)
ImageHandle image_handle = PlatformImageData::NullHandle();
dispatcher->Send(new PpapiHostMsg_PPBImageData_CreatePlatform(
kApiID, instance, format, size, init_to_zero,
&result, &desc, &image_handle));
if (!result.is_null())
return
(new PlatformImageData(result, desc, image_handle))->GetReference();
#else
NOTREACHED();
#endif
break;
}
}
return 0;
}
bool PPB_ImageData_Proxy::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PPB_ImageData_Proxy, msg)
#if !defined(OS_NACL)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBImageData_CreatePlatform,
OnHostMsgCreatePlatform)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBImageData_CreateSimple,
OnHostMsgCreateSimple)
#endif
IPC_MESSAGE_HANDLER(PpapiMsg_PPBImageData_NotifyUnusedImageData,
OnPluginMsgNotifyUnusedImageData)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
#if !defined(OS_NACL)
PP_Resource PPB_ImageData_Proxy::CreateImageData(
PP_Instance instance,
PPB_ImageData_Shared::ImageDataType type,
PP_ImageDataFormat format,
const PP_Size& size,
bool init_to_zero,
PP_ImageDataDesc* desc,
IPC::PlatformFileForTransit* image_handle,
uint32_t* byte_count) {
HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
if (!dispatcher)
return 0;
thunk::EnterResourceCreation enter(instance);
if (enter.failed())
return 0;
PP_Bool pp_init_to_zero = init_to_zero ? PP_TRUE : PP_FALSE;
PP_Resource pp_resource = 0;
switch (type) {
case PPB_ImageData_Shared::SIMPLE: {
pp_resource = enter.functions()->CreateImageDataSimple(
instance, format, &size, pp_init_to_zero);
break;
}
case PPB_ImageData_Shared::PLATFORM: {
pp_resource = enter.functions()->CreateImageData(
instance, format, &size, pp_init_to_zero);
break;
}
}
if (!pp_resource)
return 0;
ppapi::ScopedPPResource resource(ppapi::ScopedPPResource::PassRef(),
pp_resource);
thunk::EnterResourceNoLock<PPB_ImageData_API> enter_resource(resource.get(),
false);
if (enter_resource.object()->Describe(desc) != PP_TRUE) {
DVLOG(1) << "CreateImageData failed: could not Describe";
return 0;
}
int local_fd = 0;
if (enter_resource.object()->GetSharedMemory(&local_fd,
byte_count) != PP_OK) {
DVLOG(1) << "CreateImageData failed: could not GetSharedMemory";
return 0;
}
#if defined(OS_WIN)
*image_handle = dispatcher->ShareHandleWithRemote(
reinterpret_cast<HANDLE>(static_cast<intptr_t>(local_fd)), false);
#elif defined(TOOLKIT_GTK)
if (type == PPB_ImageData_Shared::PLATFORM)
*image_handle = IPC::PlatformFileForTransit(local_fd, false);
else
*image_handle = dispatcher->ShareHandleWithRemote(local_fd, false);
#elif defined(OS_POSIX)
*image_handle = dispatcher->ShareHandleWithRemote(local_fd, false);
#else
#error Not implemented.
#endif
return resource.Release();
}
void PPB_ImageData_Proxy::OnHostMsgCreatePlatform(
PP_Instance instance,
int32_t format,
const PP_Size& size,
PP_Bool init_to_zero,
HostResource* result,
PP_ImageDataDesc* desc,
ImageHandle* result_image_handle) {
IPC::PlatformFileForTransit image_handle;
uint32_t byte_count;
PP_Resource resource =
CreateImageData(instance,
PPB_ImageData_Shared::PLATFORM,
static_cast<PP_ImageDataFormat>(format),
size,
true ,
desc, &image_handle, &byte_count);
result->SetHostResource(instance, resource);
if (resource) {
#if defined(TOOLKIT_GTK)
*result_image_handle = image_handle.fd;
#else
*result_image_handle = image_handle;
#endif
} else {
*result_image_handle = PlatformImageData::NullHandle();
}
}
void PPB_ImageData_Proxy::OnHostMsgCreateSimple(
PP_Instance instance,
int32_t format,
const PP_Size& size,
PP_Bool init_to_zero,
HostResource* result,
PP_ImageDataDesc* desc,
ppapi::proxy::SerializedHandle* result_image_handle) {
IPC::PlatformFileForTransit image_handle;
uint32_t byte_count;
PP_Resource resource =
CreateImageData(instance,
PPB_ImageData_Shared::SIMPLE,
static_cast<PP_ImageDataFormat>(format),
size,
true ,
desc, &image_handle, &byte_count);
result->SetHostResource(instance, resource);
if (resource) {
result_image_handle->set_shmem(image_handle, byte_count);
} else {
result_image_handle->set_null_shmem();
}
}
#endif
void PPB_ImageData_Proxy::OnPluginMsgNotifyUnusedImageData(
const HostResource& old_image_data) {
PluginGlobals* plugin_globals = PluginGlobals::Get();
if (!plugin_globals)
return;
EnterPluginFromHostResource<PPB_ImageData_API> enter(old_image_data);
if (enter.succeeded()) {
ImageData* image_data = static_cast<ImageData*>(enter.object());
ImageDataCache::GetInstance()->ImageDataUsable(image_data);
}
dispatcher()->Send(new PpapiHostMsg_PPBCore_ReleaseResource(
API_ID_PPB_CORE, old_image_data));
}
}
}