| // 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 |