Home | History | Annotate | Download | only in proxy
      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 #include "ppapi/proxy/ppb_image_data_proxy.h"
      6 
      7 #include <string.h>  // For memcpy
      8 
      9 #include <map>
     10 #include <vector>
     11 
     12 #include "base/logging.h"
     13 #include "base/memory/singleton.h"
     14 #include "base/memory/weak_ptr.h"
     15 #include "build/build_config.h"
     16 #include "ppapi/c/pp_completion_callback.h"
     17 #include "ppapi/c/pp_errors.h"
     18 #include "ppapi/c/pp_resource.h"
     19 #include "ppapi/proxy/enter_proxy.h"
     20 #include "ppapi/proxy/host_dispatcher.h"
     21 #include "ppapi/proxy/plugin_dispatcher.h"
     22 #include "ppapi/proxy/plugin_globals.h"
     23 #include "ppapi/proxy/plugin_resource_tracker.h"
     24 #include "ppapi/proxy/ppapi_messages.h"
     25 #include "ppapi/shared_impl/host_resource.h"
     26 #include "ppapi/shared_impl/proxy_lock.h"
     27 #include "ppapi/shared_impl/resource.h"
     28 #include "ppapi/shared_impl/scoped_pp_resource.h"
     29 #include "ppapi/thunk/enter.h"
     30 #include "ppapi/thunk/thunk.h"
     31 
     32 #if !defined(OS_NACL)
     33 #include "skia/ext/platform_canvas.h"
     34 #include "ui/surface/transport_dib.h"
     35 #endif
     36 
     37 using ppapi::thunk::PPB_ImageData_API;
     38 
     39 namespace ppapi {
     40 namespace proxy {
     41 
     42 namespace {
     43 
     44 // How ImageData re-use works
     45 // --------------------------
     46 //
     47 // When animating plugins (like video), re-creating image datas for each frame
     48 // and mapping the memory has a high overhead. So we try to re-use these when
     49 // possible.
     50 //
     51 // 1. Plugin makes an asynchronous call that transfers an ImageData to the
     52 //    implementation of some API.
     53 // 2. Plugin frees its ImageData reference. If it doesn't do this we can't
     54 //    re-use it.
     55 // 3. When the last plugin ref of an ImageData is released, we don't actually
     56 //    delete it. Instead we put it on a queue where we hold onto it in the
     57 //    plugin process for a short period of time.
     58 // 4. The API implementation that received the ImageData finishes using it.
     59 //    Without our caching system it would get deleted at this point.
     60 // 5. The proxy in the renderer will send NotifyUnusedImageData back to the
     61 //    plugin process. We check if the given resource is in the queue and mark
     62 //    it as usable.
     63 // 6. When the plugin requests a new image data, we check our queue and if there
     64 //    is a usable ImageData of the right size and format, we'll return it
     65 //    instead of making a new one. It's important that caching is only requested
     66 //    when the size is unlikely to change, so cache hits are high.
     67 //
     68 // Some notes:
     69 //
     70 //  - We only re-use image data when the plugin and host are rapidly exchanging
     71 //    them and the size is likely to remain constant. It should be clear that
     72 //    the plugin is promising that it's done with the image.
     73 //
     74 //  - Theoretically we could re-use them in other cases but the lifetime
     75 //    becomes more difficult to manage. The plugin could have used an ImageData
     76 //    in an arbitrary number of queued up PaintImageData calls which we would
     77 //    have to check.
     78 //
     79 //  - If a flush takes a long time or there are many released image datas
     80 //    accumulating in our queue such that some are deleted, we will have
     81 //    released our reference by the time the renderer notifies us of an unused
     82 //    image data. In this case we just give up.
     83 //
     84 //  - We maintain a per-instance cache. Some pages have many instances of
     85 //    Flash, for example, each of a different size. If they're all animating we
     86 //    want each to get its own image data re-use.
     87 //
     88 //  - We generate new resource IDs when re-use happens to try to avoid weird
     89 //    problems if the plugin messes up its refcounting.
     90 
     91 // Keep a cache entry for this many seconds before expiring it. We get an entry
     92 // back from the renderer after an ImageData is swapped out, so it means the
     93 // plugin has to be painting at least two frames for this time interval to
     94 // get caching.
     95 static const int kMaxAgeSeconds = 2;
     96 
     97 // ImageDataCacheEntry ---------------------------------------------------------
     98 
     99 struct ImageDataCacheEntry {
    100   ImageDataCacheEntry() : added_time(), usable(false), image() {}
    101   ImageDataCacheEntry(ImageData* i)
    102       : added_time(base::TimeTicks::Now()),
    103         usable(false),
    104         image(i) {
    105   }
    106 
    107   base::TimeTicks added_time;
    108 
    109   // Set to true when the renderer tells us that it's OK to re-use this iamge.
    110   bool usable;
    111 
    112   scoped_refptr<ImageData> image;
    113 };
    114 
    115 // ImageDataInstanceCache ------------------------------------------------------
    116 
    117 // Per-instance cache of image datas.
    118 class ImageDataInstanceCache {
    119  public:
    120   ImageDataInstanceCache() : next_insertion_point_(0) {}
    121 
    122   // These functions have the same spec as the ones in ImageDataCache.
    123   scoped_refptr<ImageData> Get(PPB_ImageData_Shared::ImageDataType type,
    124                                int width, int height,
    125                                PP_ImageDataFormat format);
    126   void Add(ImageData* image_data);
    127   void ImageDataUsable(ImageData* image_data);
    128 
    129   // Expires old entries. Returns true if there are still entries in the list,
    130   // false if this instance cache is now empty.
    131   bool ExpireEntries();
    132 
    133  private:
    134   void IncrementInsertionPoint();
    135 
    136   // We'll store this many ImageDatas per instance.
    137   const static int kCacheSize = 2;
    138 
    139   ImageDataCacheEntry images_[kCacheSize];
    140 
    141   // Index into cache where the next item will go.
    142   int next_insertion_point_;
    143 };
    144 
    145 scoped_refptr<ImageData> ImageDataInstanceCache::Get(
    146     PPB_ImageData_Shared::ImageDataType type,
    147     int width, int height,
    148     PP_ImageDataFormat format) {
    149   // Just do a brute-force search since the cache is so small.
    150   for (int i = 0; i < kCacheSize; i++) {
    151     if (!images_[i].usable)
    152       continue;
    153     if (images_[i].image->type() != type)
    154       continue;
    155     const PP_ImageDataDesc& desc = images_[i].image->desc();
    156     if (desc.format == format &&
    157         desc.size.width == width && desc.size.height == height) {
    158       scoped_refptr<ImageData> ret(images_[i].image);
    159       images_[i] = ImageDataCacheEntry();
    160 
    161       // Since we just removed an item, this entry is the best place to insert
    162       // a subsequent one.
    163       next_insertion_point_ = i;
    164       return ret;
    165     }
    166   }
    167   return scoped_refptr<ImageData>();
    168 }
    169 
    170 void ImageDataInstanceCache::Add(ImageData* image_data) {
    171   images_[next_insertion_point_] = ImageDataCacheEntry(image_data);
    172   IncrementInsertionPoint();
    173 }
    174 
    175 void ImageDataInstanceCache::ImageDataUsable(ImageData* image_data) {
    176   for (int i = 0; i < kCacheSize; i++) {
    177     if (images_[i].image.get() == image_data) {
    178       images_[i].usable = true;
    179 
    180       // This test is important. The renderer doesn't guarantee how many image
    181       // datas it has or when it notifies us when one is usable. Its possible
    182       // to get into situations where it's always telling us the old one is
    183       // usable, and then the older one immediately gets expired. Therefore,
    184       // if the next insertion would overwrite this now-usable entry, make the
    185       // next insertion overwrite some other entry to avoid the replacement.
    186       if (next_insertion_point_ == i)
    187         IncrementInsertionPoint();
    188       return;
    189     }
    190   }
    191 }
    192 
    193 bool ImageDataInstanceCache::ExpireEntries() {
    194   base::TimeTicks threshold_time =
    195       base::TimeTicks::Now() - base::TimeDelta::FromSeconds(kMaxAgeSeconds);
    196 
    197   bool has_entry = false;
    198   for (int i = 0; i < kCacheSize; i++) {
    199     if (images_[i].image.get()) {
    200       // Entry present.
    201       if (images_[i].added_time <= threshold_time) {
    202         // Found an entry to expire.
    203         images_[i] = ImageDataCacheEntry();
    204         next_insertion_point_ = i;
    205       } else {
    206         // Found an entry that we're keeping.
    207         has_entry = true;
    208       }
    209     }
    210   }
    211   return has_entry;
    212 }
    213 
    214 void ImageDataInstanceCache::IncrementInsertionPoint() {
    215   // Go to the next location, wrapping around to get LRU.
    216   next_insertion_point_++;
    217   if (next_insertion_point_ >= kCacheSize)
    218     next_insertion_point_ = 0;
    219 }
    220 
    221 // ImageDataCache --------------------------------------------------------------
    222 
    223 class ImageDataCache {
    224  public:
    225   ImageDataCache() : weak_factory_(this) {}
    226   ~ImageDataCache() {}
    227 
    228   static ImageDataCache* GetInstance();
    229 
    230   // Retrieves an image data from the cache of the specified type, size and
    231   // format if one exists. If one doesn't exist, this will return a null refptr.
    232   scoped_refptr<ImageData> Get(PP_Instance instance,
    233                                PPB_ImageData_Shared::ImageDataType type,
    234                                int width, int height,
    235                                PP_ImageDataFormat format);
    236 
    237   // Adds the given image data to the cache. There should be no plugin
    238   // references to it. This may delete an older item from the cache.
    239   void Add(ImageData* image_data);
    240 
    241   // Notification from the renderer that the given image data is usable.
    242   void ImageDataUsable(ImageData* image_data);
    243 
    244   void DidDeleteInstance(PP_Instance instance);
    245 
    246  private:
    247   friend struct LeakySingletonTraits<ImageDataCache>;
    248 
    249   // Timer callback to expire entries for the given instance.
    250   void OnTimer(PP_Instance instance);
    251 
    252   typedef std::map<PP_Instance, ImageDataInstanceCache> CacheMap;
    253   CacheMap cache_;
    254 
    255   // This class does timer calls and we don't want to run these outside of the
    256   // scope of the object. Technically, since this class is a leaked static,
    257   // this will never happen and this factory is unnecessary. However, it's
    258   // probably better not to make assumptions about the lifetime of this class.
    259   base::WeakPtrFactory<ImageDataCache> weak_factory_;
    260 
    261   DISALLOW_COPY_AND_ASSIGN(ImageDataCache);
    262 };
    263 
    264 // static
    265 ImageDataCache* ImageDataCache::GetInstance() {
    266   return Singleton<ImageDataCache,
    267                    LeakySingletonTraits<ImageDataCache> >::get();
    268 }
    269 
    270 scoped_refptr<ImageData> ImageDataCache::Get(
    271     PP_Instance instance,
    272     PPB_ImageData_Shared::ImageDataType type,
    273     int width, int height,
    274     PP_ImageDataFormat format) {
    275   CacheMap::iterator found = cache_.find(instance);
    276   if (found == cache_.end())
    277     return scoped_refptr<ImageData>();
    278   return found->second.Get(type, width, height, format);
    279 }
    280 
    281 void ImageDataCache::Add(ImageData* image_data) {
    282   cache_[image_data->pp_instance()].Add(image_data);
    283 
    284   // Schedule a timer to invalidate this entry.
    285   base::MessageLoop::current()->PostDelayedTask(
    286       FROM_HERE,
    287       RunWhileLocked(base::Bind(&ImageDataCache::OnTimer,
    288                                 weak_factory_.GetWeakPtr(),
    289                                 image_data->pp_instance())),
    290       base::TimeDelta::FromSeconds(kMaxAgeSeconds));
    291 }
    292 
    293 void ImageDataCache::ImageDataUsable(ImageData* image_data) {
    294   CacheMap::iterator found = cache_.find(image_data->pp_instance());
    295   if (found != cache_.end())
    296     found->second.ImageDataUsable(image_data);
    297 }
    298 
    299 void ImageDataCache::DidDeleteInstance(PP_Instance instance) {
    300   cache_.erase(instance);
    301 }
    302 
    303 void ImageDataCache::OnTimer(PP_Instance instance) {
    304   CacheMap::iterator found = cache_.find(instance);
    305   if (found == cache_.end())
    306     return;
    307   if (!found->second.ExpireEntries()) {
    308     // There are no more entries for this instance, remove it from the cache.
    309     cache_.erase(found);
    310   }
    311 }
    312 
    313 }  // namespace
    314 
    315 // ImageData -------------------------------------------------------------------
    316 
    317 ImageData::ImageData(const HostResource& resource,
    318                      PPB_ImageData_Shared::ImageDataType type,
    319                      const PP_ImageDataDesc& desc)
    320     : Resource(OBJECT_IS_PROXY, resource),
    321       type_(type),
    322       desc_(desc),
    323       is_candidate_for_reuse_(false) {
    324 }
    325 
    326 ImageData::~ImageData() {
    327 }
    328 
    329 PPB_ImageData_API* ImageData::AsPPB_ImageData_API() {
    330   return this;
    331 }
    332 
    333 void ImageData::LastPluginRefWasDeleted() {
    334   // The plugin no longer needs this ImageData, add it to our cache if it's
    335   // been used in a ReplaceContents. These are the ImageDatas that the renderer
    336   // will send back ImageDataUsable messages for.
    337   if (is_candidate_for_reuse_)
    338     ImageDataCache::GetInstance()->Add(this);
    339 }
    340 
    341 void ImageData::InstanceWasDeleted() {
    342   ImageDataCache::GetInstance()->DidDeleteInstance(pp_instance());
    343 }
    344 
    345 PP_Bool ImageData::Describe(PP_ImageDataDesc* desc) {
    346   memcpy(desc, &desc_, sizeof(PP_ImageDataDesc));
    347   return PP_TRUE;
    348 }
    349 
    350 int32_t ImageData::GetSharedMemory(int* /* handle */,
    351                                    uint32_t* /* byte_count */) {
    352   // Not supported in the proxy (this method is for actually implementing the
    353   // proxy in the host).
    354   return PP_ERROR_NOACCESS;
    355 }
    356 
    357 void ImageData::SetIsCandidateForReuse() {
    358   is_candidate_for_reuse_ = true;
    359 }
    360 
    361 void ImageData::RecycleToPlugin(bool zero_contents) {
    362   is_candidate_for_reuse_ = false;
    363   if (zero_contents) {
    364     void* data = Map();
    365     memset(data, 0, desc_.stride * desc_.size.height);
    366     Unmap();
    367   }
    368 }
    369 
    370 // PlatformImageData -----------------------------------------------------------
    371 
    372 #if !defined(OS_NACL)
    373 PlatformImageData::PlatformImageData(const HostResource& resource,
    374                                      const PP_ImageDataDesc& desc,
    375                                      ImageHandle handle)
    376     : ImageData(resource, PPB_ImageData_Shared::PLATFORM, desc) {
    377 #if defined(OS_WIN)
    378   transport_dib_.reset(TransportDIB::CreateWithHandle(handle));
    379 #else
    380   transport_dib_.reset(TransportDIB::Map(handle));
    381 #endif  // defined(OS_WIN)
    382 }
    383 
    384 PlatformImageData::~PlatformImageData() {
    385 }
    386 
    387 void* PlatformImageData::Map() {
    388   if (!mapped_canvas_.get()) {
    389     mapped_canvas_.reset(transport_dib_->GetPlatformCanvas(desc_.size.width,
    390                                                            desc_.size.height));
    391     if (!mapped_canvas_.get())
    392       return NULL;
    393   }
    394   const SkBitmap& bitmap =
    395       skia::GetTopDevice(*mapped_canvas_)->accessBitmap(true);
    396 
    397   bitmap.lockPixels();
    398   return bitmap.getAddr(0, 0);
    399 }
    400 
    401 void PlatformImageData::Unmap() {
    402   // TODO(brettw) have a way to unmap a TransportDIB. Currently this isn't
    403   // possible since deleting the TransportDIB also frees all the handles.
    404   // We need to add a method to TransportDIB to release the handles.
    405 }
    406 
    407 SkCanvas* PlatformImageData::GetPlatformCanvas() {
    408   return mapped_canvas_.get();
    409 }
    410 
    411 SkCanvas* PlatformImageData::GetCanvas() {
    412   return mapped_canvas_.get();
    413 }
    414 
    415 // static
    416 ImageHandle PlatformImageData::NullHandle() {
    417 #if defined(OS_WIN)
    418   return NULL;
    419 #else
    420   return ImageHandle();
    421 #endif
    422 }
    423 
    424 ImageHandle PlatformImageData::HandleFromInt(int32_t i) {
    425 #if defined(OS_WIN)
    426     return reinterpret_cast<ImageHandle>(i);
    427 #else
    428     return ImageHandle(i, false);
    429 #endif
    430 }
    431 #endif  // !defined(OS_NACL)
    432 
    433 // SimpleImageData -------------------------------------------------------------
    434 
    435 SimpleImageData::SimpleImageData(const HostResource& resource,
    436                                  const PP_ImageDataDesc& desc,
    437                                  const base::SharedMemoryHandle& handle)
    438     : ImageData(resource, PPB_ImageData_Shared::SIMPLE, desc),
    439       shm_(handle, false /* read_only */),
    440       size_(desc.size.width * desc.size.height * 4),
    441       map_count_(0) {
    442 }
    443 
    444 SimpleImageData::~SimpleImageData() {
    445 }
    446 
    447 void* SimpleImageData::Map() {
    448   if (map_count_++ == 0)
    449     shm_.Map(size_);
    450   return shm_.memory();
    451 }
    452 
    453 void SimpleImageData::Unmap() {
    454   if (--map_count_ == 0)
    455     shm_.Unmap();
    456 }
    457 
    458 SkCanvas* SimpleImageData::GetPlatformCanvas() {
    459   return NULL;  // No canvas available.
    460 }
    461 
    462 SkCanvas* SimpleImageData::GetCanvas() {
    463   return NULL;  // No canvas available.
    464 }
    465 
    466 // PPB_ImageData_Proxy ---------------------------------------------------------
    467 
    468 PPB_ImageData_Proxy::PPB_ImageData_Proxy(Dispatcher* dispatcher)
    469     : InterfaceProxy(dispatcher) {
    470 }
    471 
    472 PPB_ImageData_Proxy::~PPB_ImageData_Proxy() {
    473 }
    474 
    475 // static
    476 PP_Resource PPB_ImageData_Proxy::CreateProxyResource(
    477     PP_Instance instance,
    478     PPB_ImageData_Shared::ImageDataType type,
    479     PP_ImageDataFormat format,
    480     const PP_Size& size,
    481     PP_Bool init_to_zero) {
    482   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
    483   if (!dispatcher)
    484     return 0;
    485 
    486   // Check the cache.
    487   scoped_refptr<ImageData> cached_image_data =
    488       ImageDataCache::GetInstance()->Get(instance, type,
    489                                          size.width, size.height, format);
    490   if (cached_image_data.get()) {
    491     // We have one we can re-use rather than allocating a new one.
    492     cached_image_data->RecycleToPlugin(PP_ToBool(init_to_zero));
    493     return cached_image_data->GetReference();
    494   }
    495 
    496   HostResource result;
    497   PP_ImageDataDesc desc;
    498   switch (type) {
    499     case PPB_ImageData_Shared::SIMPLE: {
    500       ppapi::proxy::SerializedHandle image_handle_wrapper;
    501       dispatcher->Send(new PpapiHostMsg_PPBImageData_CreateSimple(
    502           kApiID, instance, format, size, init_to_zero,
    503           &result, &desc, &image_handle_wrapper));
    504       if (image_handle_wrapper.is_shmem()) {
    505         base::SharedMemoryHandle image_handle = image_handle_wrapper.shmem();
    506         if (!result.is_null())
    507           return
    508               (new SimpleImageData(result, desc, image_handle))->GetReference();
    509       }
    510       break;
    511     }
    512     case PPB_ImageData_Shared::PLATFORM: {
    513 #if !defined(OS_NACL)
    514       ImageHandle image_handle = PlatformImageData::NullHandle();
    515       dispatcher->Send(new PpapiHostMsg_PPBImageData_CreatePlatform(
    516           kApiID, instance, format, size, init_to_zero,
    517           &result, &desc, &image_handle));
    518       if (!result.is_null())
    519         return
    520             (new PlatformImageData(result, desc, image_handle))->GetReference();
    521 #else
    522       // PlatformImageData shouldn't be created in untrusted code.
    523       NOTREACHED();
    524 #endif
    525       break;
    526     }
    527   }
    528 
    529   return 0;
    530 }
    531 
    532 bool PPB_ImageData_Proxy::OnMessageReceived(const IPC::Message& msg) {
    533   bool handled = true;
    534   IPC_BEGIN_MESSAGE_MAP(PPB_ImageData_Proxy, msg)
    535 #if !defined(OS_NACL)
    536     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBImageData_CreatePlatform,
    537                         OnHostMsgCreatePlatform)
    538     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBImageData_CreateSimple,
    539                         OnHostMsgCreateSimple)
    540 #endif
    541     IPC_MESSAGE_HANDLER(PpapiMsg_PPBImageData_NotifyUnusedImageData,
    542                         OnPluginMsgNotifyUnusedImageData)
    543 
    544     IPC_MESSAGE_UNHANDLED(handled = false)
    545   IPC_END_MESSAGE_MAP()
    546   return handled;
    547 }
    548 
    549 #if !defined(OS_NACL)
    550 // static
    551 PP_Resource PPB_ImageData_Proxy::CreateImageData(
    552     PP_Instance instance,
    553     PPB_ImageData_Shared::ImageDataType type,
    554     PP_ImageDataFormat format,
    555     const PP_Size& size,
    556     bool init_to_zero,
    557     PP_ImageDataDesc* desc,
    558     IPC::PlatformFileForTransit* image_handle,
    559     uint32_t* byte_count) {
    560   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    561   if (!dispatcher)
    562     return 0;
    563 
    564   thunk::EnterResourceCreation enter(instance);
    565   if (enter.failed())
    566     return 0;
    567 
    568   PP_Bool pp_init_to_zero = init_to_zero ? PP_TRUE : PP_FALSE;
    569   PP_Resource pp_resource = 0;
    570   switch (type) {
    571     case PPB_ImageData_Shared::SIMPLE: {
    572       pp_resource = enter.functions()->CreateImageDataSimple(
    573           instance, format, &size, pp_init_to_zero);
    574       break;
    575     }
    576     case PPB_ImageData_Shared::PLATFORM: {
    577       pp_resource = enter.functions()->CreateImageData(
    578           instance, format, &size, pp_init_to_zero);
    579       break;
    580     }
    581   }
    582 
    583   if (!pp_resource)
    584     return 0;
    585 
    586   ppapi::ScopedPPResource resource(ppapi::ScopedPPResource::PassRef(),
    587                                    pp_resource);
    588 
    589   thunk::EnterResourceNoLock<PPB_ImageData_API> enter_resource(resource.get(),
    590                                                                false);
    591   if (enter_resource.object()->Describe(desc) != PP_TRUE) {
    592     DVLOG(1) << "CreateImageData failed: could not Describe";
    593     return 0;
    594   }
    595 
    596   int local_fd = 0;
    597   if (enter_resource.object()->GetSharedMemory(&local_fd,
    598                                                byte_count) != PP_OK) {
    599     DVLOG(1) << "CreateImageData failed: could not GetSharedMemory";
    600     return 0;
    601   }
    602 
    603 #if defined(OS_WIN)
    604   *image_handle = dispatcher->ShareHandleWithRemote(
    605       reinterpret_cast<HANDLE>(static_cast<intptr_t>(local_fd)), false);
    606 #elif defined(OS_POSIX)
    607   *image_handle = dispatcher->ShareHandleWithRemote(local_fd, false);
    608 #else
    609   #error Not implemented.
    610 #endif
    611 
    612   return resource.Release();
    613 }
    614 
    615 void PPB_ImageData_Proxy::OnHostMsgCreatePlatform(
    616     PP_Instance instance,
    617     int32_t format,
    618     const PP_Size& size,
    619     PP_Bool init_to_zero,
    620     HostResource* result,
    621     PP_ImageDataDesc* desc,
    622     ImageHandle* result_image_handle) {
    623   // Clear |desc| so we don't send unitialized memory to the plugin.
    624   // https://crbug.com/391023.
    625   *desc = PP_ImageDataDesc();
    626   IPC::PlatformFileForTransit image_handle;
    627   uint32_t byte_count;
    628   PP_Resource resource =
    629       CreateImageData(instance,
    630                       PPB_ImageData_Shared::PLATFORM,
    631                       static_cast<PP_ImageDataFormat>(format),
    632                       size,
    633                       true /* init_to_zero */,
    634                       desc, &image_handle, &byte_count);
    635   result->SetHostResource(instance, resource);
    636   if (resource) {
    637     *result_image_handle = image_handle;
    638   } else {
    639     *result_image_handle = PlatformImageData::NullHandle();
    640   }
    641 }
    642 
    643 void PPB_ImageData_Proxy::OnHostMsgCreateSimple(
    644     PP_Instance instance,
    645     int32_t format,
    646     const PP_Size& size,
    647     PP_Bool init_to_zero,
    648     HostResource* result,
    649     PP_ImageDataDesc* desc,
    650     ppapi::proxy::SerializedHandle* result_image_handle) {
    651   // Clear |desc| so we don't send unitialized memory to the plugin.
    652   // https://crbug.com/391023.
    653   *desc = PP_ImageDataDesc();
    654   IPC::PlatformFileForTransit image_handle;
    655   uint32_t byte_count;
    656   PP_Resource resource =
    657       CreateImageData(instance,
    658                       PPB_ImageData_Shared::SIMPLE,
    659                       static_cast<PP_ImageDataFormat>(format),
    660                       size,
    661                       true /* init_to_zero */,
    662                       desc, &image_handle, &byte_count);
    663 
    664   result->SetHostResource(instance, resource);
    665   if (resource) {
    666     result_image_handle->set_shmem(image_handle, byte_count);
    667   } else {
    668     result_image_handle->set_null_shmem();
    669   }
    670 }
    671 #endif  // !defined(OS_NACL)
    672 
    673 void PPB_ImageData_Proxy::OnPluginMsgNotifyUnusedImageData(
    674     const HostResource& old_image_data) {
    675   PluginGlobals* plugin_globals = PluginGlobals::Get();
    676   if (!plugin_globals)
    677     return;  // This may happen if the plugin is maliciously sending this
    678              // message to the renderer.
    679 
    680   EnterPluginFromHostResource<PPB_ImageData_API> enter(old_image_data);
    681   if (enter.succeeded()) {
    682     ImageData* image_data = static_cast<ImageData*>(enter.object());
    683     ImageDataCache::GetInstance()->ImageDataUsable(image_data);
    684   }
    685 
    686   // The renderer sent us a reference with the message. If the image data was
    687   // still cached in our process, the proxy still holds a reference so we can
    688   // remove the one the renderer just sent is. If the proxy no longer holds a
    689   // reference, we released everything and we should also release the one the
    690   // renderer just sent us.
    691   dispatcher()->Send(new PpapiHostMsg_PPBCore_ReleaseResource(
    692       API_ID_PPB_CORE, old_image_data));
    693 }
    694 
    695 }  // namespace proxy
    696 }  // namespace ppapi
    697