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