Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ppapi / proxy / ppb_image_data_proxy.cc
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   IPC::PlatformFileForTransit image_handle;
624   uint32_t byte_count;
625   PP_Resource resource =
626       CreateImageData(instance,
627                       PPB_ImageData_Shared::PLATFORM,
628                       static_cast<PP_ImageDataFormat>(format),
629                       size,
630                       true /* init_to_zero */,
631                       desc, &image_handle, &byte_count);
632   result->SetHostResource(instance, resource);
633   if (resource) {
634     *result_image_handle = image_handle;
635   } else {
636     *result_image_handle = PlatformImageData::NullHandle();
637   }
638 }
639
640 void PPB_ImageData_Proxy::OnHostMsgCreateSimple(
641     PP_Instance instance,
642     int32_t format,
643     const PP_Size& size,
644     PP_Bool init_to_zero,
645     HostResource* result,
646     PP_ImageDataDesc* desc,
647     ppapi::proxy::SerializedHandle* result_image_handle) {
648   IPC::PlatformFileForTransit image_handle;
649   uint32_t byte_count;
650   PP_Resource resource =
651       CreateImageData(instance,
652                       PPB_ImageData_Shared::SIMPLE,
653                       static_cast<PP_ImageDataFormat>(format),
654                       size,
655                       true /* init_to_zero */,
656                       desc, &image_handle, &byte_count);
657
658   result->SetHostResource(instance, resource);
659   if (resource) {
660     result_image_handle->set_shmem(image_handle, byte_count);
661   } else {
662     result_image_handle->set_null_shmem();
663   }
664 }
665 #endif  // !defined(OS_NACL)
666
667 void PPB_ImageData_Proxy::OnPluginMsgNotifyUnusedImageData(
668     const HostResource& old_image_data) {
669   PluginGlobals* plugin_globals = PluginGlobals::Get();
670   if (!plugin_globals)
671     return;  // This may happen if the plugin is maliciously sending this
672              // message to the renderer.
673
674   EnterPluginFromHostResource<PPB_ImageData_API> enter(old_image_data);
675   if (enter.succeeded()) {
676     ImageData* image_data = static_cast<ImageData*>(enter.object());
677     ImageDataCache::GetInstance()->ImageDataUsable(image_data);
678   }
679
680   // The renderer sent us a reference with the message. If the image data was
681   // still cached in our process, the proxy still holds a reference so we can
682   // remove the one the renderer just sent is. If the proxy no longer holds a
683   // reference, we released everything and we should also release the one the
684   // renderer just sent us.
685   dispatcher()->Send(new PpapiHostMsg_PPBCore_ReleaseResource(
686       API_ID_PPB_CORE, old_image_data));
687 }
688
689 }  // namespace proxy
690 }  // namespace ppapi