1 // Copyright 2014 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 <GLES2/gl2.h> 6 7 #include "base/memory/shared_memory.h" 8 #include "base/message_loop/message_loop.h" 9 #include "ppapi/c/pp_errors.h" 10 #include "ppapi/c/ppb_video_decoder.h" 11 #include "ppapi/proxy/locking_resource_releaser.h" 12 #include "ppapi/proxy/plugin_message_filter.h" 13 #include "ppapi/proxy/ppapi_message_utils.h" 14 #include "ppapi/proxy/ppapi_messages.h" 15 #include "ppapi/proxy/ppapi_proxy_test.h" 16 #include "ppapi/proxy/ppb_graphics_3d_proxy.h" 17 #include "ppapi/proxy/video_decoder_constants.h" 18 #include "ppapi/proxy/video_decoder_resource.h" 19 #include "ppapi/shared_impl/proxy_lock.h" 20 #include "ppapi/thunk/thunk.h" 21 22 using ppapi::proxy::ResourceMessageTestSink; 23 24 namespace ppapi { 25 namespace proxy { 26 27 namespace { 28 29 const PP_Bool kAllowSoftwareFallback = PP_TRUE; 30 const PP_Resource kGraphics3D = 7; 31 const uint32_t kShmSize = 256; 32 const size_t kDecodeBufferSize = 16; 33 const uint32_t kDecodeId = 5; 34 const uint32_t kTextureId1 = 1; 35 const uint32_t kTextureId2 = 2; 36 const uint32_t kNumRequestedTextures = 2; 37 38 class MockCompletionCallback { 39 public: 40 MockCompletionCallback() : called_(false) {} 41 42 bool called() { return called_; } 43 int32_t result() { return result_; } 44 45 void Reset() { called_ = false; } 46 47 static void Callback(void* user_data, int32_t result) { 48 MockCompletionCallback* that = 49 reinterpret_cast<MockCompletionCallback*>(user_data); 50 that->called_ = true; 51 that->result_ = result; 52 } 53 54 private: 55 bool called_; 56 int32_t result_; 57 }; 58 59 class VideoDecoderResourceTest : public PluginProxyTest { 60 public: 61 VideoDecoderResourceTest() 62 : decoder_iface_(thunk::GetPPB_VideoDecoder_0_1_Thunk()) {} 63 64 const PPB_VideoDecoder_0_1* decoder_iface() const { return decoder_iface_; } 65 66 void SendReply(const ResourceMessageCallParams& params, 67 int32_t result, 68 const IPC::Message& nested_message) { 69 ResourceMessageReplyParams reply_params(params.pp_resource(), 70 params.sequence()); 71 reply_params.set_result(result); 72 PluginMessageFilter::DispatchResourceReplyForTest(reply_params, 73 nested_message); 74 } 75 76 void SendReplyWithHandle(const ResourceMessageCallParams& params, 77 int32_t result, 78 const IPC::Message& nested_message, 79 const SerializedHandle& handle) { 80 ResourceMessageReplyParams reply_params(params.pp_resource(), 81 params.sequence()); 82 reply_params.set_result(result); 83 reply_params.AppendHandle(handle); 84 PluginMessageFilter::DispatchResourceReplyForTest(reply_params, 85 nested_message); 86 } 87 88 PP_Resource CreateDecoder() { 89 PP_Resource result = decoder_iface()->Create(pp_instance()); 90 if (result) { 91 ProxyAutoLock lock; 92 ppapi::Resource* resource = 93 GetGlobals()->GetResourceTracker()->GetResource(result); 94 proxy::VideoDecoderResource* decoder = 95 static_cast<proxy::VideoDecoderResource*>(resource); 96 decoder->SetForTest(); 97 } 98 99 return result; 100 } 101 102 PP_Resource CreateGraphics3d() { 103 ProxyAutoLock lock; 104 105 HostResource host_resource; 106 host_resource.SetHostResource(pp_instance(), kGraphics3D); 107 scoped_refptr<ppapi::proxy::Graphics3D> graphics_3d( 108 new ppapi::proxy::Graphics3D(host_resource)); 109 return graphics_3d->GetReference(); 110 } 111 112 PP_Resource CreateAndInitializeDecoder() { 113 PP_Resource decoder = CreateDecoder(); 114 LockingResourceReleaser graphics3d(CreateGraphics3d()); 115 MockCompletionCallback cb; 116 int32_t result = decoder_iface()->Initialize( 117 decoder, 118 graphics3d.get(), 119 PP_VIDEOPROFILE_H264MAIN, 120 PP_TRUE /* allow_software_fallback */, 121 PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback, 122 &cb)); 123 if (result != PP_OK_COMPLETIONPENDING) 124 return 0; 125 ResourceMessageCallParams params; 126 IPC::Message msg; 127 if (!sink().GetFirstResourceCallMatching( 128 PpapiHostMsg_VideoDecoder_Initialize::ID, ¶ms, &msg)) 129 return 0; 130 sink().ClearMessages(); 131 SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_InitializeReply()); 132 return decoder; 133 } 134 135 int32_t CallDecode(PP_Resource pp_decoder, 136 MockCompletionCallback* cb, 137 const PpapiHostMsg_VideoDecoder_GetShm* expected_shm_msg) { 138 // Set up a handler in case the resource sends a sync message to create 139 // shared memory. 140 PpapiPluginMsg_VideoDecoder_GetShmReply shm_msg_reply(kShmSize); 141 ResourceSyncCallHandler shm_msg_handler( 142 &sink(), PpapiHostMsg_VideoDecoder_GetShm::ID, PP_OK, shm_msg_reply); 143 sink().AddFilter(&shm_msg_handler); 144 145 base::SharedMemory shm; 146 if (expected_shm_msg) { 147 shm.CreateAnonymous(kShmSize); 148 base::SharedMemoryHandle shm_handle; 149 shm.ShareToProcess(base::GetCurrentProcessHandle(), &shm_handle); 150 SerializedHandle serialized_handle(shm_handle, kShmSize); 151 shm_msg_handler.set_serialized_handle(&serialized_handle); 152 } 153 154 memset(decode_buffer_, 0x55, kDecodeBufferSize); 155 int32_t result = 156 decoder_iface()->Decode(pp_decoder, 157 kDecodeId, 158 kDecodeBufferSize, 159 decode_buffer_, 160 PP_MakeOptionalCompletionCallback( 161 &MockCompletionCallback::Callback, cb)); 162 163 if (expected_shm_msg) { 164 uint32_t shm_id, shm_size, expected_shm_id, expected_shm_size; 165 UnpackMessage<PpapiHostMsg_VideoDecoder_GetShm>( 166 *expected_shm_msg, &expected_shm_id, &expected_shm_size); 167 if (shm_msg_handler.last_handled_msg().type() == 0 || 168 !UnpackMessage<PpapiHostMsg_VideoDecoder_GetShm>( 169 shm_msg_handler.last_handled_msg(), &shm_id, &shm_size) || 170 shm_id != expected_shm_id || 171 shm_size != expected_shm_size) { 172 // Signal that the expected shm message wasn't sent by failing. 173 result = PP_ERROR_FAILED; 174 } 175 } 176 177 sink().RemoveFilter(&shm_msg_handler); 178 return result; 179 } 180 181 int32_t CallGetPicture(PP_Resource pp_decoder, 182 PP_VideoPicture* picture, 183 MockCompletionCallback* cb) { 184 int32_t result = 185 decoder_iface()->GetPicture(pp_decoder, 186 picture, 187 PP_MakeOptionalCompletionCallback( 188 &MockCompletionCallback::Callback, cb)); 189 return result; 190 } 191 192 void CallRecyclePicture(PP_Resource pp_decoder, 193 const PP_VideoPicture& picture) { 194 decoder_iface()->RecyclePicture(pp_decoder, &picture); 195 } 196 197 int32_t CallFlush(PP_Resource pp_decoder, MockCompletionCallback* cb) { 198 int32_t result = 199 decoder_iface()->Flush(pp_decoder, 200 PP_MakeOptionalCompletionCallback( 201 &MockCompletionCallback::Callback, cb)); 202 return result; 203 } 204 205 int32_t CallReset(PP_Resource pp_decoder, MockCompletionCallback* cb) { 206 int32_t result = 207 decoder_iface()->Reset(pp_decoder, 208 PP_MakeOptionalCompletionCallback( 209 &MockCompletionCallback::Callback, cb)); 210 return result; 211 } 212 213 void SendDecodeReply(const ResourceMessageCallParams& params, 214 uint32_t shm_id) { 215 SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_DecodeReply(shm_id)); 216 } 217 218 void SendPictureReady(const ResourceMessageCallParams& params, 219 uint32_t decode_count, 220 uint32_t texture_id) { 221 SendReply( 222 params, 223 PP_OK, 224 PpapiPluginMsg_VideoDecoder_PictureReady(decode_count, texture_id)); 225 } 226 227 void SendFlushReply(const ResourceMessageCallParams& params) { 228 SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_FlushReply()); 229 } 230 231 void SendResetReply(const ResourceMessageCallParams& params) { 232 SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_ResetReply()); 233 } 234 235 void SendRequestTextures(const ResourceMessageCallParams& params) { 236 SendReply(params, 237 PP_OK, 238 PpapiPluginMsg_VideoDecoder_RequestTextures( 239 kNumRequestedTextures, 240 PP_MakeSize(320, 240), 241 GL_TEXTURE_2D, 242 std::vector<gpu::Mailbox>())); 243 } 244 245 void SendNotifyError(const ResourceMessageCallParams& params, int32_t error) { 246 SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_NotifyError(error)); 247 } 248 249 bool CheckDecodeMsg(ResourceMessageCallParams* params, 250 uint32_t* shm_id, 251 uint32_t* size, 252 int32_t* decode_id) { 253 IPC::Message msg; 254 if (!sink().GetFirstResourceCallMatching( 255 PpapiHostMsg_VideoDecoder_Decode::ID, params, &msg)) 256 return false; 257 sink().ClearMessages(); 258 return UnpackMessage<PpapiHostMsg_VideoDecoder_Decode>( 259 msg, shm_id, size, decode_id); 260 } 261 262 bool CheckRecyclePictureMsg(ResourceMessageCallParams* params, 263 uint32_t* texture_id) { 264 IPC::Message msg; 265 if (!sink().GetFirstResourceCallMatching( 266 PpapiHostMsg_VideoDecoder_RecyclePicture::ID, params, &msg)) 267 return false; 268 sink().ClearMessages(); 269 return UnpackMessage<PpapiHostMsg_VideoDecoder_RecyclePicture>(msg, 270 texture_id); 271 } 272 273 bool CheckFlushMsg(ResourceMessageCallParams* params) { 274 return CheckMsg(params, PpapiHostMsg_VideoDecoder_Flush::ID); 275 } 276 277 bool CheckResetMsg(ResourceMessageCallParams* params) { 278 return CheckMsg(params, PpapiHostMsg_VideoDecoder_Reset::ID); 279 } 280 281 void ClearCallbacks(PP_Resource pp_decoder) { 282 ResourceMessageCallParams params; 283 MockCompletionCallback cb; 284 285 // Reset to abort Decode and GetPicture callbacks. 286 CallReset(pp_decoder, &cb); 287 // Initialize params so we can reply to the Reset. 288 CheckResetMsg(¶ms); 289 // Run the Reset callback. 290 SendResetReply(params); 291 } 292 293 private: 294 bool CheckMsg(ResourceMessageCallParams* params, int id) { 295 IPC::Message msg; 296 if (!sink().GetFirstResourceCallMatching(id, params, &msg)) 297 return false; 298 sink().ClearMessages(); 299 return true; 300 } 301 302 const PPB_VideoDecoder_0_1* decoder_iface_; 303 304 char decode_buffer_[kDecodeBufferSize]; 305 }; 306 307 } // namespace 308 309 TEST_F(VideoDecoderResourceTest, Initialize) { 310 // Initialize with 0 graphics3d_context should fail. 311 { 312 LockingResourceReleaser decoder(CreateDecoder()); 313 MockCompletionCallback cb; 314 int32_t result = decoder_iface()->Initialize( 315 decoder.get(), 316 0 /* invalid 3d graphics */, 317 PP_VIDEOPROFILE_H264MAIN, 318 kAllowSoftwareFallback, 319 PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback, 320 &cb)); 321 ASSERT_EQ(PP_ERROR_BADRESOURCE, result); 322 } 323 // Initialize with bad profile value should fail. 324 { 325 LockingResourceReleaser decoder(CreateDecoder()); 326 MockCompletionCallback cb; 327 int32_t result = decoder_iface()->Initialize( 328 decoder.get(), 329 1 /* non-zero resource */, 330 static_cast<PP_VideoProfile>(-1), 331 kAllowSoftwareFallback, 332 PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback, 333 &cb)); 334 ASSERT_EQ(PP_ERROR_BADARGUMENT, result); 335 } 336 // Initialize with valid graphics3d_context and profile should succeed. 337 { 338 LockingResourceReleaser decoder(CreateDecoder()); 339 LockingResourceReleaser graphics3d(CreateGraphics3d()); 340 MockCompletionCallback cb; 341 int32_t result = decoder_iface()->Initialize( 342 decoder.get(), 343 graphics3d.get(), 344 PP_VIDEOPROFILE_H264MAIN, 345 kAllowSoftwareFallback, 346 PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback, 347 &cb)); 348 ASSERT_EQ(PP_OK_COMPLETIONPENDING, result); 349 ASSERT_TRUE(decoder_iface()->IsVideoDecoder(decoder.get())); 350 351 // Another attempt while pending should fail. 352 result = decoder_iface()->Initialize( 353 decoder.get(), 354 graphics3d.get(), 355 PP_VIDEOPROFILE_H264MAIN, 356 kAllowSoftwareFallback, 357 PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback, 358 &cb)); 359 ASSERT_EQ(PP_ERROR_INPROGRESS, result); 360 361 // Check for host message and send a reply to complete initialization. 362 ResourceMessageCallParams params; 363 IPC::Message msg; 364 ASSERT_TRUE(sink().GetFirstResourceCallMatching( 365 PpapiHostMsg_VideoDecoder_Initialize::ID, ¶ms, &msg)); 366 sink().ClearMessages(); 367 SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_InitializeReply()); 368 ASSERT_TRUE(cb.called()); 369 ASSERT_EQ(PP_OK, cb.result()); 370 } 371 } 372 373 TEST_F(VideoDecoderResourceTest, Uninitialized) { 374 // Operations on uninitialized decoders should fail. 375 LockingResourceReleaser decoder(CreateDecoder()); 376 MockCompletionCallback uncalled_cb; 377 378 ASSERT_EQ(PP_ERROR_FAILED, CallDecode(decoder.get(), &uncalled_cb, NULL)); 379 ASSERT_FALSE(uncalled_cb.called()); 380 381 ASSERT_EQ(PP_ERROR_FAILED, CallGetPicture(decoder.get(), NULL, &uncalled_cb)); 382 ASSERT_FALSE(uncalled_cb.called()); 383 384 ASSERT_EQ(PP_ERROR_FAILED, CallFlush(decoder.get(), &uncalled_cb)); 385 ASSERT_FALSE(uncalled_cb.called()); 386 387 ASSERT_EQ(PP_ERROR_FAILED, CallReset(decoder.get(), &uncalled_cb)); 388 ASSERT_FALSE(uncalled_cb.called()); 389 } 390 391 // TODO(bbudge) Fix sync message testing on Windows 64 bit builds. The reply 392 // message for GetShm isn't received, causing Decode to fail. 393 // http://crbug.com/379260 394 #if !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS) 395 TEST_F(VideoDecoderResourceTest, DecodeAndGetPicture) { 396 LockingResourceReleaser decoder(CreateAndInitializeDecoder()); 397 ResourceMessageCallParams params, params2; 398 MockCompletionCallback decode_cb, get_picture_cb, uncalled_cb; 399 400 uint32_t shm_id; 401 uint32_t decode_size; 402 int32_t decode_id; 403 // Call Decode until we have the maximum pending, minus one. 404 for (uint32_t i = 0; i < kMaximumPendingDecodes - 1; i++) { 405 PpapiHostMsg_VideoDecoder_GetShm shm_msg(i, kDecodeBufferSize); 406 ASSERT_EQ(PP_OK, CallDecode(decoder.get(), &uncalled_cb, &shm_msg)); 407 ASSERT_FALSE(uncalled_cb.called()); 408 CheckDecodeMsg(¶ms, &shm_id, &decode_size, &decode_id); 409 ASSERT_EQ(i, shm_id); 410 ASSERT_EQ(kDecodeBufferSize, decode_size); 411 // The resource generates uids internally, starting at 1. 412 int32_t uid = i + 1; 413 ASSERT_EQ(uid, decode_id); 414 } 415 // Once we've allocated the maximum number of buffers, we must wait. 416 PpapiHostMsg_VideoDecoder_GetShm shm_msg(7U, kDecodeBufferSize); 417 ASSERT_EQ(PP_OK_COMPLETIONPENDING, 418 CallDecode(decoder.get(), &decode_cb, &shm_msg)); 419 CheckDecodeMsg(¶ms, &shm_id, &decode_size, &decode_id); 420 ASSERT_EQ(7U, shm_id); 421 ASSERT_EQ(kDecodeBufferSize, decode_size); 422 423 // Calling Decode when another Decode is pending should fail. 424 ASSERT_EQ(PP_ERROR_INPROGRESS, CallDecode(decoder.get(), &uncalled_cb, NULL)); 425 ASSERT_FALSE(uncalled_cb.called()); 426 // Free up the first decode buffer. 427 SendDecodeReply(params, 0U); 428 // The decoder should run the pending callback. 429 ASSERT_TRUE(decode_cb.called()); 430 ASSERT_EQ(PP_OK, decode_cb.result()); 431 decode_cb.Reset(); 432 433 // Now try to get a picture. No picture ready message has been received yet. 434 PP_VideoPicture picture; 435 ASSERT_EQ(PP_OK_COMPLETIONPENDING, 436 CallGetPicture(decoder.get(), &picture, &get_picture_cb)); 437 ASSERT_FALSE(get_picture_cb.called()); 438 // Calling GetPicture when another GetPicture is pending should fail. 439 ASSERT_EQ(PP_ERROR_INPROGRESS, 440 CallGetPicture(decoder.get(), &picture, &uncalled_cb)); 441 ASSERT_FALSE(uncalled_cb.called()); 442 // Send 'request textures' message to initialize textures. 443 SendRequestTextures(params); 444 // Send a picture ready message for Decode call 1. The GetPicture callback 445 // should complete. 446 SendPictureReady(params, 1U, kTextureId1); 447 ASSERT_TRUE(get_picture_cb.called()); 448 ASSERT_EQ(PP_OK, get_picture_cb.result()); 449 ASSERT_EQ(kDecodeId, picture.decode_id); 450 get_picture_cb.Reset(); 451 452 // Send a picture ready message for Decode call 2. Since there is no pending 453 // GetPicture call, the picture should be queued. 454 SendPictureReady(params, 2U, kTextureId2); 455 // The next GetPicture should return synchronously. 456 ASSERT_EQ(PP_OK, CallGetPicture(decoder.get(), &picture, &uncalled_cb)); 457 ASSERT_FALSE(uncalled_cb.called()); 458 ASSERT_EQ(kDecodeId, picture.decode_id); 459 } 460 #endif // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS) 461 462 // TODO(bbudge) Fix sync message testing on Windows 64 bit builds. The reply 463 // message for GetShm isn't received, causing Decode to fail. 464 // http://crbug.com/379260 465 #if !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS) 466 TEST_F(VideoDecoderResourceTest, RecyclePicture) { 467 LockingResourceReleaser decoder(CreateAndInitializeDecoder()); 468 ResourceMessageCallParams params; 469 MockCompletionCallback decode_cb, get_picture_cb, uncalled_cb; 470 471 // Get to a state where we have a picture to recycle. 472 PpapiHostMsg_VideoDecoder_GetShm shm_msg(0U, kDecodeBufferSize); 473 ASSERT_EQ(PP_OK, CallDecode(decoder.get(), &decode_cb, &shm_msg)); 474 uint32_t shm_id; 475 uint32_t decode_size; 476 int32_t decode_id; 477 CheckDecodeMsg(¶ms, &shm_id, &decode_size, &decode_id); 478 SendDecodeReply(params, 0U); 479 // Send 'request textures' message to initialize textures. 480 SendRequestTextures(params); 481 // Call GetPicture and send 'picture ready' message to get a picture to 482 // recycle. 483 PP_VideoPicture picture; 484 ASSERT_EQ(PP_OK_COMPLETIONPENDING, 485 CallGetPicture(decoder.get(), &picture, &get_picture_cb)); 486 SendPictureReady(params, 0U, kTextureId1); 487 ASSERT_EQ(kTextureId1, picture.texture_id); 488 489 CallRecyclePicture(decoder.get(), picture); 490 uint32_t texture_id; 491 ASSERT_TRUE(CheckRecyclePictureMsg(¶ms, &texture_id)); 492 ASSERT_EQ(kTextureId1, texture_id); 493 494 ClearCallbacks(decoder.get()); 495 } 496 #endif // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS) 497 498 TEST_F(VideoDecoderResourceTest, Flush) { 499 LockingResourceReleaser decoder(CreateAndInitializeDecoder()); 500 ResourceMessageCallParams params, params2; 501 MockCompletionCallback flush_cb, get_picture_cb, uncalled_cb; 502 503 ASSERT_EQ(PP_OK_COMPLETIONPENDING, CallFlush(decoder.get(), &flush_cb)); 504 ASSERT_FALSE(flush_cb.called()); 505 ASSERT_TRUE(CheckFlushMsg(¶ms)); 506 507 ASSERT_EQ(PP_ERROR_FAILED, CallDecode(decoder.get(), &uncalled_cb, NULL)); 508 ASSERT_FALSE(uncalled_cb.called()); 509 510 // Plugin can call GetPicture while Flush is pending. 511 ASSERT_EQ(PP_OK_COMPLETIONPENDING, 512 CallGetPicture(decoder.get(), NULL, &get_picture_cb)); 513 ASSERT_FALSE(get_picture_cb.called()); 514 515 ASSERT_EQ(PP_ERROR_INPROGRESS, CallFlush(decoder.get(), &uncalled_cb)); 516 ASSERT_FALSE(uncalled_cb.called()); 517 518 ASSERT_EQ(PP_ERROR_FAILED, CallReset(decoder.get(), &uncalled_cb)); 519 ASSERT_FALSE(uncalled_cb.called()); 520 521 // Plugin can call RecyclePicture while Flush is pending. 522 PP_VideoPicture picture; 523 picture.texture_id = kTextureId1; 524 CallRecyclePicture(decoder.get(), picture); 525 uint32_t texture_id; 526 ASSERT_TRUE(CheckRecyclePictureMsg(¶ms2, &texture_id)); 527 528 SendFlushReply(params); 529 // Any pending GetPicture call is aborted. 530 ASSERT_TRUE(get_picture_cb.called()); 531 ASSERT_EQ(PP_ERROR_ABORTED, get_picture_cb.result()); 532 ASSERT_TRUE(flush_cb.called()); 533 ASSERT_EQ(PP_OK, flush_cb.result()); 534 } 535 536 // TODO(bbudge) Test Reset when we can run the message loop to get aborted 537 // callbacks to run. 538 539 // TODO(bbudge) Fix sync message testing on Windows 64 bit builds. The reply 540 // message for GetShm isn't received, causing Decode to fail. 541 // http://crbug.com/379260 542 #if !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS) 543 TEST_F(VideoDecoderResourceTest, NotifyError) { 544 LockingResourceReleaser decoder(CreateAndInitializeDecoder()); 545 ResourceMessageCallParams params; 546 MockCompletionCallback decode_cb, get_picture_cb, uncalled_cb; 547 548 // Call Decode and GetPicture to have some pending requests. 549 PpapiHostMsg_VideoDecoder_GetShm shm_msg(0U, kDecodeBufferSize); 550 ASSERT_EQ(PP_OK, CallDecode(decoder.get(), &decode_cb, &shm_msg)); 551 ASSERT_FALSE(decode_cb.called()); 552 ASSERT_EQ(PP_OK_COMPLETIONPENDING, 553 CallGetPicture(decoder.get(), NULL, &get_picture_cb)); 554 ASSERT_FALSE(get_picture_cb.called()); 555 556 // Send the decoder resource an unsolicited notify error message. We first 557 // need to initialize 'params' so the message is routed to the decoder. 558 uint32_t shm_id; 559 uint32_t decode_size; 560 int32_t decode_id; 561 CheckDecodeMsg(¶ms, &shm_id, &decode_size, &decode_id); 562 SendNotifyError(params, PP_ERROR_RESOURCE_FAILED); 563 564 // Any pending message should be run with the reported error. 565 ASSERT_TRUE(get_picture_cb.called()); 566 ASSERT_EQ(PP_ERROR_RESOURCE_FAILED, get_picture_cb.result()); 567 568 // All further calls return the reported error. 569 ASSERT_EQ(PP_ERROR_RESOURCE_FAILED, 570 CallDecode(decoder.get(), &uncalled_cb, NULL)); 571 ASSERT_FALSE(uncalled_cb.called()); 572 ASSERT_EQ(PP_ERROR_RESOURCE_FAILED, 573 CallGetPicture(decoder.get(), NULL, &uncalled_cb)); 574 ASSERT_FALSE(uncalled_cb.called()); 575 ASSERT_EQ(PP_ERROR_RESOURCE_FAILED, CallFlush(decoder.get(), &uncalled_cb)); 576 ASSERT_FALSE(uncalled_cb.called()); 577 ASSERT_EQ(PP_ERROR_RESOURCE_FAILED, CallReset(decoder.get(), &uncalled_cb)); 578 ASSERT_FALSE(uncalled_cb.called()); 579 } 580 #endif // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS) 581 582 } // namespace proxy 583 } // namespace ppapi 584