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   // This class does timer calls and we don't want to run these outside of the
    253   // scope of the object. Technically, since this class is a leaked static,
    254   // this will never happen and this factory is unnecessary. However, it's
    255   // probably better not to make assumptions about the lifetime of this class.
    256   base::WeakPtrFactory<ImageDataCache> weak_factory_;
    257 
    258   typedef std::map<PP_Instance, ImageDataInstanceCache> CacheMap;
    259   CacheMap cache_;
    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 #elif defined(TOOLKIT_GTK)
    420   return 0;
    421 #else
    422   return ImageHandle();
    423 #endif
    424 }
    425 
    426 ImageHandle PlatformImageData::HandleFromInt(int32_t i) {
    427 #if defined(OS_WIN)
    428     return reinterpret_cast<ImageHandle>(i);
    429 #elif defined(TOOLKIT_GTK)
    430     return static_cast<ImageHandle>(i);
    431 #else
    432     return ImageHandle(i, false);
    433 #endif
    434 }
    435 #endif  // !defined(OS_NACL)
    436 
    437 // SimpleImageData -------------------------------------------------------------
    438 
    439 SimpleImageData::SimpleImageData(const HostResource& resource,
    440                                  const PP_ImageDataDesc& desc,
    441                                  const base::SharedMemoryHandle& handle)
    442     : ImageData(resource, PPB_ImageData_Shared::SIMPLE, desc),
    443       shm_(handle, false /* read_only */),
    444       size_(desc.size.width * desc.size.height * 4),
    445       map_count_(0) {
    446 }
    447 
    448 SimpleImageData::~SimpleImageData() {
    449 }
    450 
    451 void* SimpleImageData::Map() {
    452   if (map_count_++ == 0)
    453     shm_.Map(size_);
    454   return shm_.memory();
    455 }
    456 
    457 void SimpleImageData::Unmap() {
    458   if (--map_count_ == 0)
    459     shm_.Unmap();
    460 }
    461 
    462 SkCanvas* SimpleImageData::GetPlatformCanvas() {
    463   return NULL;  // No canvas available.
    464 }
    465 
    466 SkCanvas* SimpleImageData::GetCanvas() {
    467   return NULL;  // No canvas available.
    468 }
    469 
    470 // PPB_ImageData_Proxy ---------------------------------------------------------
    471 
    472 PPB_ImageData_Proxy::PPB_ImageData_Proxy(Dispatcher* dispatcher)
    473     : InterfaceProxy(dispatcher) {
    474 }
    475 
    476 PPB_ImageData_Proxy::~PPB_ImageData_Proxy() {
    477 }
    478 
    479 // static
    480 PP_Resource PPB_ImageData_Proxy::CreateProxyResource(
    481     PP_Instance instance,
    482     PPB_ImageData_Shared::ImageDataType type,
    483     PP_ImageDataFormat format,
    484     const PP_Size& size,
    485     PP_Bool init_to_zero) {
    486   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
    487   if (!dispatcher)
    488     return 0;
    489 
    490   // Check the cache.
    491   scoped_refptr<ImageData> cached_image_data =
    492       ImageDataCache::GetInstance()->Get(instance, type,
    493                                          size.width, size.height, format);
    494   if (cached_image_data.get()) {
    495     // We have one we can re-use rather than allocating a new one.
    496     cached_image_data->RecycleToPlugin(PP_ToBool(init_to_zero));
    497     return cached_image_data->GetReference();
    498   }
    499 
    500   HostResource result;
    501   PP_ImageDataDesc desc;
    502   switch (type) {
    503     case PPB_ImageData_Shared::SIMPLE: {
    504       ppapi::proxy::SerializedHandle image_handle_wrapper;
    505       dispatcher->Send(new PpapiHostMsg_PPBImageData_CreateSimple(
    506           kApiID, instance, format, size, init_to_zero,
    507           &result, &desc, &image_handle_wrapper));
    508       if (image_handle_wrapper.is_shmem()) {
    509         base::SharedMemoryHandle image_handle = image_handle_wrapper.shmem();
    510         if (!result.is_null())
    511           return
    512               (new SimpleImageData(result, desc, image_handle))->GetReference();
    513       }
    514       break;
    515     }
    516     case PPB_ImageData_Shared::PLATFORM: {
    517 #if !defined(OS_NACL)
    518       ImageHandle image_handle = PlatformImageData::NullHandle();
    519       dispatcher->Send(new PpapiHostMsg_PPBImageData_CreatePlatform(
    520           kApiID, instance, format, size, init_to_zero,
    521           &result, &desc, &image_handle));
    522       if (!result.is_null())
    523         return
    524             (new PlatformImageData(result, desc, image_handle))->GetReference();
    525 #else
    526       // PlatformImageData shouldn't be created in untrusted code.
    527       NOTREACHED();
    528 #endif
    529       break;
    530     }
    531   }
    532 
    533   return 0;
    534 }
    535 
    536 bool PPB_ImageData_Proxy::OnMessageReceived(const IPC::Message& msg) {
    537   bool handled = true;
    538   IPC_BEGIN_MESSAGE_MAP(PPB_ImageData_Proxy, msg)
    539 #if !defined(OS_NACL)
    540     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBImageData_CreatePlatform,
    541                         OnHostMsgCreatePlatform)
    542     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBImageData_CreateSimple,
    543                         OnHostMsgCreateSimple)
    544 #endif
    545     IPC_MESSAGE_HANDLER(PpapiMsg_PPBImageData_NotifyUnusedImageData,
    546                         OnPluginMsgNotifyUnusedImageData)
    547 
    548     IPC_MESSAGE_UNHANDLED(handled = false)
    549   IPC_END_MESSAGE_MAP()
    550   return handled;
    551 }
    552 
    553 #if !defined(OS_NACL)
    554 // static
    555 PP_Resource PPB_ImageData_Proxy::CreateImageData(
    556     PP_Instance instance,
    557     PPB_ImageData_Shared::ImageDataType type,
    558     PP_ImageDataFormat format,
    559     const PP_Size& size,
    560     bool init_to_zero,
    561     PP_ImageDataDesc* desc,
    562     IPC::PlatformFileForTransit* image_handle,
    563     uint32_t* byte_count) {
    564   HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
    565   if (!dispatcher)
    566     return 0;
    567 
    568   thunk::EnterResourceCreation enter(instance);
    569   if (enter.failed())
    570     return 0;
    571 
    572   PP_Bool pp_init_to_zero = init_to_zero ? PP_TRUE : PP_FALSE;
    573   PP_Resource pp_resource = 0;
    574   switch (type) {
    575     case PPB_ImageData_Shared::SIMPLE: {
    576       pp_resource = enter.functions()->CreateImageDataSimple(
    577           instance, format, &size, pp_init_to_zero);
    578       break;
    579     }
    580     case PPB_ImageData_Shared::PLATFORM: {
    581       pp_resource = enter.functions()->CreateImageData(
    582           instance, format, &size, pp_init_to_zero);
    583       break;
    584     }
    585   }
    586 
    587   if (!pp_resource)
    588     return 0;
    589 
    590   ppapi::ScopedPPResource resource(ppapi::ScopedPPResource::PassRef(),
    591                                    pp_resource);
    592 
    593   thunk::EnterResourceNoLock<PPB_ImageData_API> enter_resource(resource.get(),
    594                                                                false);
    595   if (enter_resource.object()->Describe(desc) != PP_TRUE) {
    596     DVLOG(1) << "CreateImageData failed: could not Describe";
    597     return 0;
    598   }
    599 
    600   int local_fd = 0;
    601   if (enter_resource.object()->GetSharedMemory(&local_fd,
    602                                                byte_count) != PP_OK) {
    603     DVLOG(1) << "CreateImageData failed: could not GetSharedMemory";
    604     return 0;
    605   }
    606 
    607 #if defined(OS_WIN)
    608   *image_handle = dispatcher->ShareHandleWithRemote(
    609       reinterpret_cast<HANDLE>(static_cast<intptr_t>(local_fd)), false);
    610 #elif defined(TOOLKIT_GTK)
    611   // On X Windows, a PlatformImageData is backed by a SysV shared memory key,
    612   // so embed that in a fake PlatformFileForTransit and don't share it across
    613   // processes.
    614   if (type == PPB_ImageData_Shared::PLATFORM)
    615     *image_handle = IPC::PlatformFileForTransit(local_fd, false);
    616   else
    617     *image_handle = dispatcher->ShareHandleWithRemote(local_fd, false);
    618 #elif defined(OS_POSIX)
    619   *image_handle = dispatcher->ShareHandleWithRemote(local_fd, false);
    620 #else
    621   #error Not implemented.
    622 #endif
    623 
    624   return resource.Release();
    625 }
    626 
    627 void PPB_ImageData_Proxy::OnHostMsgCreatePlatform(
    628     PP_Instance instance,
    629     int32_t format,
    630     const PP_Size& size,
    631     PP_Bool init_to_zero,
    632     HostResource* result,
    633     PP_ImageDataDesc* desc,
    634     ImageHandle* result_image_handle) {
    635   IPC::PlatformFileForTransit image_handle;
    636   uint32_t byte_count;
    637   PP_Resource resource =
    638       CreateImageData(instance,
    639                       PPB_ImageData_Shared::PLATFORM,
    640                       static_cast<PP_ImageDataFormat>(format),
    641                       size,
    642                       true /* init_to_zero */,
    643                       desc, &image_handle, &byte_count);
    644   result->SetHostResource(instance, resource);
    645   if (resource) {
    646 #if defined(TOOLKIT_GTK)
    647     // On X Windows ImageHandle is a SysV shared memory key.
    648     *result_image_handle = image_handle.fd;
    649 #else
    650     *result_image_handle = image_handle;
    651 #endif
    652   } else {
    653     *result_image_handle = PlatformImageData::NullHandle();
    654   }
    655 }
    656 
    657 void PPB_ImageData_Proxy::OnHostMsgCreateSimple(
    658     PP_Instance instance,
    659     int32_t format,
    660     const PP_Size& size,
    661     PP_Bool init_to_zero,
    662     HostResource* result,
    663     PP_ImageDataDesc* desc,
    664     ppapi::proxy::SerializedHandle* result_image_handle) {
    665   IPC::PlatformFileForTransit image_handle;
    666   uint32_t byte_count;
    667   PP_Resource resource =
    668       CreateImageData(instance,
    669                       PPB_ImageData_Shared::SIMPLE,
    670                       static_cast<PP_ImageDataFormat>(format),
    671                       size,
    672                       true /* init_to_zero */,
    673                       desc, &image_handle, &byte_count);
    674 
    675   result->SetHostResource(instance, resource);
    676   if (resource) {
    677     result_image_handle->set_shmem(image_handle, byte_count);
    678   } else {
    679     result_image_handle->set_null_shmem();
    680   }
    681 }
    682 #endif  // !defined(OS_NACL)
    683 
    684 void PPB_ImageData_Proxy::OnPluginMsgNotifyUnusedImageData(
    685     const HostResource& old_image_data) {
    686   PluginGlobals* plugin_globals = PluginGlobals::Get();
    687   if (!plugin_globals)
    688     return;  // This may happen if the plugin is maliciously sending this
    689              // message to the renderer.
    690 
    691   EnterPluginFromHostResource<PPB_ImageData_API> enter(old_image_data);
    692   if (enter.succeeded()) {
    693     ImageData* image_data = static_cast<ImageData*>(enter.object());
    694     ImageDataCache::GetInstance()->ImageDataUsable(image_data);
    695   }
    696 
    697   // The renderer sent us a reference with the message. If the image data was
    698   // still cached in our process, the proxy still holds a reference so we can
    699   // remove the one the renderer just sent is. If the proxy no longer holds a
    700   // reference, we released everything and we should also release the one the
    701   // renderer just sent us.
    702   dispatcher()->Send(new PpapiHostMsg_PPBCore_ReleaseResource(
    703       API_ID_PPB_CORE, old_image_data));
    704 }
    705 
    706 }  // namespace proxy
    707 }  // namespace ppapi
    708