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