|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ppapi/proxy/ppb_image_data_proxy.h" | 
|  |  | 
|  | #include <string.h>  // For memcpy | 
|  |  | 
|  | #include <map> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.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 { | 
|  |  | 
|  | // How ImageData re-use works | 
|  | // -------------------------- | 
|  | // | 
|  | // When animating plugins (like video), re-creating image datas for each frame | 
|  | // and mapping the memory has a high overhead. So we try to re-use these when | 
|  | // possible. | 
|  | // | 
|  | // 1. Plugin makes an asynchronous call that transfers an ImageData to the | 
|  | //    implementation of some API. | 
|  | // 2. Plugin frees its ImageData reference. If it doesn't do this we can't | 
|  | //    re-use it. | 
|  | // 3. When the last plugin ref of an ImageData is released, we don't actually | 
|  | //    delete it. Instead we put it on a queue where we hold onto it in the | 
|  | //    plugin process for a short period of time. | 
|  | // 4. The API implementation that received the ImageData finishes using it. | 
|  | //    Without our caching system it would get deleted at this point. | 
|  | // 5. The proxy in the renderer will send NotifyUnusedImageData back to the | 
|  | //    plugin process. We check if the given resource is in the queue and mark | 
|  | //    it as usable. | 
|  | // 6. When the plugin requests a new image data, we check our queue and if there | 
|  | //    is a usable ImageData of the right size and format, we'll return it | 
|  | //    instead of making a new one. It's important that caching is only requested | 
|  | //    when the size is unlikely to change, so cache hits are high. | 
|  | // | 
|  | // Some notes: | 
|  | // | 
|  | //  - We only re-use image data when the plugin and host are rapidly exchanging | 
|  | //    them and the size is likely to remain constant. It should be clear that | 
|  | //    the plugin is promising that it's done with the image. | 
|  | // | 
|  | //  - Theoretically we could re-use them in other cases but the lifetime | 
|  | //    becomes more difficult to manage. The plugin could have used an ImageData | 
|  | //    in an arbitrary number of queued up PaintImageData calls which we would | 
|  | //    have to check. | 
|  | // | 
|  | //  - If a flush takes a long time or there are many released image datas | 
|  | //    accumulating in our queue such that some are deleted, we will have | 
|  | //    released our reference by the time the renderer notifies us of an unused | 
|  | //    image data. In this case we just give up. | 
|  | // | 
|  | //  - We maintain a per-instance cache. Some pages have many instances of | 
|  | //    Flash, for example, each of a different size. If they're all animating we | 
|  | //    want each to get its own image data re-use. | 
|  | // | 
|  | //  - We generate new resource IDs when re-use happens to try to avoid weird | 
|  | //    problems if the plugin messes up its refcounting. | 
|  |  | 
|  | // Keep a cache entry for this many seconds before expiring it. We get an entry | 
|  | // back from the renderer after an ImageData is swapped out, so it means the | 
|  | // plugin has to be painting at least two frames for this time interval to | 
|  | // get caching. | 
|  | static const int kMaxAgeSeconds = 2; | 
|  |  | 
|  | // ImageDataCacheEntry --------------------------------------------------------- | 
|  |  | 
|  | struct ImageDataCacheEntry { | 
|  | ImageDataCacheEntry() : usable(false) {} | 
|  | explicit ImageDataCacheEntry(ImageData* i) | 
|  | : added_time(base::TimeTicks::Now()), usable(false), image(i) {} | 
|  |  | 
|  | base::TimeTicks added_time; | 
|  |  | 
|  | // Set to true when the renderer tells us that it's OK to re-use this iamge. | 
|  | bool usable; | 
|  |  | 
|  | scoped_refptr<ImageData> image; | 
|  | }; | 
|  |  | 
|  | // ImageDataInstanceCache ------------------------------------------------------ | 
|  |  | 
|  | // Per-instance cache of image datas. | 
|  | class ImageDataInstanceCache { | 
|  | public: | 
|  | ImageDataInstanceCache() : next_insertion_point_(0) {} | 
|  |  | 
|  | // These functions have the same spec as the ones in ImageDataCache. | 
|  | 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); | 
|  |  | 
|  | // Expires old entries. Returns true if there are still entries in the list, | 
|  | // false if this instance cache is now empty. | 
|  | bool ExpireEntries(); | 
|  |  | 
|  | private: | 
|  | void IncrementInsertionPoint(); | 
|  |  | 
|  | // We'll store this many ImageDatas per instance. | 
|  | static const size_t kCacheSize = 2; | 
|  |  | 
|  | ImageDataCacheEntry images_[kCacheSize]; | 
|  |  | 
|  | // Index into cache where the next item will go. | 
|  | size_t next_insertion_point_; | 
|  | }; | 
|  |  | 
|  | scoped_refptr<ImageData> ImageDataInstanceCache::Get( | 
|  | PPB_ImageData_Shared::ImageDataType type, | 
|  | int width, int height, | 
|  | PP_ImageDataFormat format) { | 
|  | // Just do a brute-force search since the cache is so small. | 
|  | for (size_t 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(); | 
|  |  | 
|  | // Since we just removed an item, this entry is the best place to insert | 
|  | // a subsequent one. | 
|  | 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 (size_t i = 0; i < kCacheSize; i++) { | 
|  | if (images_[i].image.get() == image_data) { | 
|  | images_[i].usable = true; | 
|  |  | 
|  | // This test is important. The renderer doesn't guarantee how many image | 
|  | // datas it has or when it notifies us when one is usable. Its possible | 
|  | // to get into situations where it's always telling us the old one is | 
|  | // usable, and then the older one immediately gets expired. Therefore, | 
|  | // if the next insertion would overwrite this now-usable entry, make the | 
|  | // next insertion overwrite some other entry to avoid the replacement. | 
|  | 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 (size_t i = 0; i < kCacheSize; i++) { | 
|  | if (images_[i].image.get()) { | 
|  | // Entry present. | 
|  | if (images_[i].added_time <= threshold_time) { | 
|  | // Found an entry to expire. | 
|  | images_[i] = ImageDataCacheEntry(); | 
|  | next_insertion_point_ = i; | 
|  | } else { | 
|  | // Found an entry that we're keeping. | 
|  | has_entry = true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return has_entry; | 
|  | } | 
|  |  | 
|  | void ImageDataInstanceCache::IncrementInsertionPoint() { | 
|  | // Go to the next location, wrapping around to get LRU. | 
|  | next_insertion_point_++; | 
|  | if (next_insertion_point_ >= kCacheSize) | 
|  | next_insertion_point_ = 0; | 
|  | } | 
|  |  | 
|  | // ImageDataCache -------------------------------------------------------------- | 
|  |  | 
|  | class ImageDataCache { | 
|  | public: | 
|  | ImageDataCache() : weak_factory_(this) {} | 
|  | ~ImageDataCache() {} | 
|  |  | 
|  | static ImageDataCache* GetInstance(); | 
|  |  | 
|  | // Retrieves an image data from the cache of the specified type, size and | 
|  | // format if one exists. If one doesn't exist, this will return a null refptr. | 
|  | scoped_refptr<ImageData> Get(PP_Instance instance, | 
|  | PPB_ImageData_Shared::ImageDataType type, | 
|  | int width, int height, | 
|  | PP_ImageDataFormat format); | 
|  |  | 
|  | // Adds the given image data to the cache. There should be no plugin | 
|  | // references to it. This may delete an older item from the cache. | 
|  | void Add(ImageData* image_data); | 
|  |  | 
|  | // Notification from the renderer that the given image data is usable. | 
|  | void ImageDataUsable(ImageData* image_data); | 
|  |  | 
|  | void DidDeleteInstance(PP_Instance instance); | 
|  |  | 
|  | private: | 
|  | friend struct base::LeakySingletonTraits<ImageDataCache>; | 
|  |  | 
|  | // Timer callback to expire entries for the given instance. | 
|  | void OnTimer(PP_Instance instance); | 
|  |  | 
|  | typedef std::map<PP_Instance, ImageDataInstanceCache> CacheMap; | 
|  | CacheMap cache_; | 
|  |  | 
|  | // This class does timer calls and we don't want to run these outside of the | 
|  | // scope of the object. Technically, since this class is a leaked static, | 
|  | // this will never happen and this factory is unnecessary. However, it's | 
|  | // probably better not to make assumptions about the lifetime of this class. | 
|  | base::WeakPtrFactory<ImageDataCache> weak_factory_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ImageDataCache); | 
|  | }; | 
|  |  | 
|  | // static | 
|  | ImageDataCache* ImageDataCache::GetInstance() { | 
|  | return base::Singleton<ImageDataCache, | 
|  | base::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); | 
|  |  | 
|  | // Schedule a timer to invalidate this entry. | 
|  | PpapiGlobals::Get()->GetMainThreadMessageLoop()->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()) { | 
|  | // There are no more entries for this instance, remove it from the cache. | 
|  | cache_.erase(found); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // ImageData ------------------------------------------------------------------- | 
|  |  | 
|  | 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() { | 
|  | // The plugin no longer needs this ImageData, add it to our cache if it's | 
|  | // been used in a ReplaceContents. These are the ImageDatas that the renderer | 
|  | // will send back ImageDataUsable messages for. | 
|  | 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(base::SharedMemory** /* shm */, | 
|  | uint32_t* /* byte_count */) { | 
|  | // Not supported in the proxy (this method is for actually implementing the | 
|  | // proxy in the host). | 
|  | 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(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // PlatformImageData ----------------------------------------------------------- | 
|  |  | 
|  | #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  // defined(OS_WIN) | 
|  | } | 
|  |  | 
|  | PlatformImageData::~PlatformImageData() { | 
|  | } | 
|  |  | 
|  | void* PlatformImageData::Map() { | 
|  | if (!mapped_canvas_.get()) { | 
|  | if (!transport_dib_.get()) | 
|  | return NULL; | 
|  |  | 
|  | const bool is_opaque = false; | 
|  | mapped_canvas_ = transport_dib_->GetPlatformCanvas( | 
|  | desc_.size.width, desc_.size.height, is_opaque); | 
|  | if (!mapped_canvas_.get()) | 
|  | return NULL; | 
|  | } | 
|  | SkPixmap pixmap; | 
|  | skia::GetWritablePixels(mapped_canvas_.get(), &pixmap); | 
|  | return pixmap.writable_addr(); | 
|  | } | 
|  |  | 
|  | void PlatformImageData::Unmap() { | 
|  | // TODO(brettw) have a way to unmap a TransportDIB. Currently this isn't | 
|  | // possible since deleting the TransportDIB also frees all the handles. | 
|  | // We need to add a method to TransportDIB to release the handles. | 
|  | } | 
|  |  | 
|  | SkCanvas* PlatformImageData::GetCanvas() { | 
|  | return mapped_canvas_.get(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | ImageHandle PlatformImageData::NullHandle() { | 
|  | return ImageHandle(); | 
|  | } | 
|  | #endif  // !defined(OS_NACL) | 
|  |  | 
|  | // SimpleImageData ------------------------------------------------------------- | 
|  |  | 
|  | SimpleImageData::SimpleImageData(const HostResource& resource, | 
|  | const PP_ImageDataDesc& desc, | 
|  | const base::SharedMemoryHandle& handle) | 
|  | : ImageData(resource, PPB_ImageData_Shared::SIMPLE, desc), | 
|  | shm_(handle, false /* read_only */), | 
|  | 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::GetCanvas() { | 
|  | return NULL;  // No canvas available. | 
|  | } | 
|  |  | 
|  | // PPB_ImageData_Proxy --------------------------------------------------------- | 
|  |  | 
|  | PPB_ImageData_Proxy::PPB_ImageData_Proxy(Dispatcher* dispatcher) | 
|  | : InterfaceProxy(dispatcher) { | 
|  | } | 
|  |  | 
|  | PPB_ImageData_Proxy::~PPB_ImageData_Proxy() { | 
|  | } | 
|  |  | 
|  | // static | 
|  | 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; | 
|  |  | 
|  | // Check the cache. | 
|  | scoped_refptr<ImageData> cached_image_data = | 
|  | ImageDataCache::GetInstance()->Get(instance, type, | 
|  | size.width, size.height, format); | 
|  | if (cached_image_data.get()) { | 
|  | // We have one we can re-use rather than allocating a new one. | 
|  | 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 | 
|  | // PlatformImageData shouldn't be created in untrusted code. | 
|  | 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) | 
|  | // static | 
|  | 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, | 
|  | base::SharedMemoryHandle* 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; | 
|  | } | 
|  |  | 
|  | base::SharedMemory* local_shm; | 
|  | if (enter_resource.object()->GetSharedMemory(&local_shm, byte_count) != | 
|  | PP_OK) { | 
|  | DVLOG(1) << "CreateImageData failed: could not GetSharedMemory"; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | *image_handle = | 
|  | dispatcher->ShareSharedMemoryHandleWithRemote(local_shm->handle()); | 
|  | 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) { | 
|  | // Clear |desc| so we don't send uninitialized memory to the plugin. | 
|  | // https://crbug.com/391023. | 
|  | *desc = PP_ImageDataDesc(); | 
|  | base::SharedMemoryHandle image_handle; | 
|  | uint32_t byte_count; | 
|  | PP_Resource resource = | 
|  | CreateImageData(instance, | 
|  | PPB_ImageData_Shared::PLATFORM, | 
|  | static_cast<PP_ImageDataFormat>(format), | 
|  | size, | 
|  | true /* init_to_zero */, | 
|  | desc, &image_handle, &byte_count); | 
|  | result->SetHostResource(instance, resource); | 
|  | *result_image_handle = | 
|  | resource ? 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) { | 
|  | // Clear |desc| so we don't send uninitialized memory to the plugin. | 
|  | // https://crbug.com/391023. | 
|  | *desc = PP_ImageDataDesc(); | 
|  | base::SharedMemoryHandle image_handle; | 
|  | uint32_t byte_count; | 
|  | PP_Resource resource = | 
|  | CreateImageData(instance, | 
|  | PPB_ImageData_Shared::SIMPLE, | 
|  | static_cast<PP_ImageDataFormat>(format), | 
|  | size, | 
|  | true /* init_to_zero */, | 
|  | 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  // !defined(OS_NACL) | 
|  |  | 
|  | void PPB_ImageData_Proxy::OnPluginMsgNotifyUnusedImageData( | 
|  | const HostResource& old_image_data) { | 
|  | PluginGlobals* plugin_globals = PluginGlobals::Get(); | 
|  | if (!plugin_globals) { | 
|  | return;  // This may happen if the plugin is maliciously sending this | 
|  | // message to the renderer. | 
|  | } | 
|  |  | 
|  | EnterPluginFromHostResource<PPB_ImageData_API> enter(old_image_data); | 
|  | if (enter.succeeded()) { | 
|  | ImageData* image_data = static_cast<ImageData*>(enter.object()); | 
|  | ImageDataCache::GetInstance()->ImageDataUsable(image_data); | 
|  | } | 
|  |  | 
|  | // The renderer sent us a reference with the message. If the image data was | 
|  | // still cached in our process, the proxy still holds a reference so we can | 
|  | // remove the one the renderer just sent is. If the proxy no longer holds a | 
|  | // reference, we released everything and we should also release the one the | 
|  | // renderer just sent us. | 
|  | dispatcher()->Send(new PpapiHostMsg_PPBCore_ReleaseResource( | 
|  | API_ID_PPB_CORE, old_image_data)); | 
|  | } | 
|  |  | 
|  | }  // namespace proxy | 
|  | }  // namespace ppapi |