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/ppapi_proxy_test.h" 6 7 #include <sstream> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/message_loop/message_loop_proxy.h" 12 #include "base/observer_list.h" 13 #include "base/run_loop.h" 14 #include "ipc/ipc_sync_channel.h" 15 #include "ppapi/c/pp_errors.h" 16 #include "ppapi/c/private/ppb_proxy_private.h" 17 #include "ppapi/proxy/ppapi_messages.h" 18 #include "ppapi/proxy/ppb_message_loop_proxy.h" 19 #include "ppapi/shared_impl/proxy_lock.h" 20 21 namespace ppapi { 22 namespace proxy { 23 24 namespace { 25 // HostDispatcher requires a PPB_Proxy_Private, so we always provide a fallback 26 // do-nothing implementation. 27 void PluginCrashed(PP_Module module) { 28 NOTREACHED(); 29 }; 30 31 PP_Instance GetInstanceForResource(PP_Resource resource) { 32 // If a test relies on this, we need to implement it. 33 NOTREACHED(); 34 return 0; 35 } 36 37 void SetReserveInstanceIDCallback(PP_Module module, 38 PP_Bool (*is_seen)(PP_Module, PP_Instance)) { 39 // This function gets called in HostDispatcher's constructor. We simply don't 40 // worry about Instance uniqueness in tests, so we can ignore the call. 41 } 42 43 void AddRefModule(PP_Module module) {} 44 void ReleaseModule(PP_Module module) {} 45 PP_Bool IsInModuleDestructor(PP_Module module) { return PP_FALSE; } 46 47 PPB_Proxy_Private ppb_proxy_private = { 48 &PluginCrashed, 49 &GetInstanceForResource, 50 &SetReserveInstanceIDCallback, 51 &AddRefModule, 52 &ReleaseModule, 53 &IsInModuleDestructor 54 }; 55 56 // We allow multiple harnesses at a time to respond to 'GetInterface' calls. 57 // We assume that only 1 harness's GetInterface function will ever support a 58 // given interface name. In practice, there will either be only 1 GetInterface 59 // handler (for PluginProxyTest or HostProxyTest), or there will be only 2 60 // GetInterface handlers (for TwoWayTest). In the latter case, one handler is 61 // for the PluginProxyTestHarness and should only respond for PPP interfaces, 62 // and the other handler is for the HostProxyTestHarness which should only 63 // ever respond for PPB interfaces. 64 ObserverList<ProxyTestHarnessBase> get_interface_handlers_; 65 66 const void* MockGetInterface(const char* name) { 67 ObserverList<ProxyTestHarnessBase>::Iterator it = 68 get_interface_handlers_; 69 while (ProxyTestHarnessBase* observer = it.GetNext()) { 70 const void* interface = observer->GetInterface(name); 71 if (interface) 72 return interface; 73 } 74 if (strcmp(name, PPB_PROXY_PRIVATE_INTERFACE) == 0) 75 return &ppb_proxy_private; 76 return NULL; 77 } 78 79 void SetUpRemoteHarness(ProxyTestHarnessBase* harness, 80 const IPC::ChannelHandle& handle, 81 base::MessageLoopProxy* ipc_message_loop_proxy, 82 base::WaitableEvent* shutdown_event, 83 base::WaitableEvent* harness_set_up) { 84 harness->SetUpHarnessWithChannel(handle, ipc_message_loop_proxy, 85 shutdown_event, false); 86 harness_set_up->Signal(); 87 } 88 89 void TearDownRemoteHarness(ProxyTestHarnessBase* harness, 90 base::WaitableEvent* harness_torn_down) { 91 harness->TearDownHarness(); 92 harness_torn_down->Signal(); 93 } 94 95 void RunTaskOnRemoteHarness(const base::Closure& task, 96 base::WaitableEvent* task_complete) { 97 task.Run(); 98 task_complete->Signal(); 99 } 100 101 } // namespace 102 103 // ProxyTestHarnessBase -------------------------------------------------------- 104 105 ProxyTestHarnessBase::ProxyTestHarnessBase() : pp_module_(0x98765), 106 pp_instance_(0x12345) { 107 get_interface_handlers_.AddObserver(this); 108 } 109 110 ProxyTestHarnessBase::~ProxyTestHarnessBase() { 111 get_interface_handlers_.RemoveObserver(this); 112 } 113 114 const void* ProxyTestHarnessBase::GetInterface(const char* name) { 115 return registered_interfaces_[name]; 116 } 117 118 void ProxyTestHarnessBase::RegisterTestInterface(const char* name, 119 const void* test_interface) { 120 registered_interfaces_[name] = test_interface; 121 } 122 123 bool ProxyTestHarnessBase::SupportsInterface(const char* name) { 124 sink().ClearMessages(); 125 126 // IPC doesn't actually write to this when we send a message manually 127 // not actually using IPC. 128 bool unused_result = false; 129 PpapiMsg_SupportsInterface msg(name, &unused_result); 130 GetDispatcher()->OnMessageReceived(msg); 131 132 const IPC::Message* reply_msg = 133 sink().GetUniqueMessageMatching(IPC_REPLY_ID); 134 EXPECT_TRUE(reply_msg); 135 if (!reply_msg) 136 return false; 137 138 TupleTypes<PpapiMsg_SupportsInterface::ReplyParam>::ValueTuple reply_data; 139 EXPECT_TRUE(PpapiMsg_SupportsInterface::ReadReplyParam( 140 reply_msg, &reply_data)); 141 142 sink().ClearMessages(); 143 return reply_data.a; 144 } 145 146 // PluginProxyTestHarness ------------------------------------------------------ 147 148 PluginProxyTestHarness::PluginProxyTestHarness( 149 GlobalsConfiguration globals_config) 150 : globals_config_(globals_config) { 151 } 152 153 PluginProxyTestHarness::~PluginProxyTestHarness() { 154 } 155 156 PpapiGlobals* PluginProxyTestHarness::GetGlobals() { 157 return plugin_globals_.get(); 158 } 159 160 Dispatcher* PluginProxyTestHarness::GetDispatcher() { 161 return plugin_dispatcher_.get(); 162 } 163 164 void PluginProxyTestHarness::SetUpHarness() { 165 // These must be first since the dispatcher set-up uses them. 166 CreatePluginGlobals(); 167 // Some of the methods called during set-up check that the lock is held. 168 ProxyAutoLock lock; 169 170 resource_tracker().DidCreateInstance(pp_instance()); 171 172 plugin_dispatcher_.reset(new PluginDispatcher( 173 &MockGetInterface, 174 PpapiPermissions(), 175 false)); 176 plugin_dispatcher_->InitWithTestSink(&sink()); 177 // The plugin proxy delegate is needed for 178 // |PluginProxyDelegate::GetBrowserSender| which is used 179 // in |ResourceCreationProxy::GetConnection| to get the channel to the 180 // browser. In this case we just use the |plugin_dispatcher_| as the channel 181 // for test purposes. 182 plugin_delegate_mock_.set_browser_sender(plugin_dispatcher_.get()); 183 PluginGlobals::Get()->set_plugin_proxy_delegate(&plugin_delegate_mock_); 184 plugin_dispatcher_->DidCreateInstance(pp_instance()); 185 } 186 187 void PluginProxyTestHarness::SetUpHarnessWithChannel( 188 const IPC::ChannelHandle& channel_handle, 189 base::MessageLoopProxy* ipc_message_loop, 190 base::WaitableEvent* shutdown_event, 191 bool is_client) { 192 // These must be first since the dispatcher set-up uses them. 193 CreatePluginGlobals(); 194 // Some of the methods called during set-up check that the lock is held. 195 ProxyAutoLock lock; 196 197 resource_tracker().DidCreateInstance(pp_instance()); 198 plugin_delegate_mock_.Init(ipc_message_loop, shutdown_event); 199 200 plugin_dispatcher_.reset(new PluginDispatcher( 201 &MockGetInterface, 202 PpapiPermissions(), 203 false)); 204 plugin_dispatcher_->InitPluginWithChannel(&plugin_delegate_mock_, 205 base::kNullProcessId, 206 channel_handle, 207 is_client); 208 plugin_delegate_mock_.set_browser_sender(plugin_dispatcher_.get()); 209 PluginGlobals::Get()->set_plugin_proxy_delegate(&plugin_delegate_mock_); 210 plugin_dispatcher_->DidCreateInstance(pp_instance()); 211 } 212 213 void PluginProxyTestHarness::TearDownHarness() { 214 { 215 // Some of the methods called during tear-down check that the lock is held. 216 ProxyAutoLock lock; 217 218 plugin_dispatcher_->DidDestroyInstance(pp_instance()); 219 plugin_dispatcher_.reset(); 220 221 resource_tracker().DidDeleteInstance(pp_instance()); 222 } 223 plugin_globals_.reset(); 224 } 225 226 void PluginProxyTestHarness::CreatePluginGlobals() { 227 if (globals_config_ == PER_THREAD_GLOBALS) { 228 plugin_globals_.reset(new PluginGlobals(PpapiGlobals::PerThreadForTest())); 229 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); 230 // Enable locking in case some other unit test ran before us and disabled 231 // locking. 232 ProxyLock::EnableLockingOnThreadForTest(); 233 } else { 234 plugin_globals_.reset(new PluginGlobals()); 235 ProxyLock::EnableLockingOnThreadForTest(); 236 } 237 } 238 239 base::MessageLoopProxy* 240 PluginProxyTestHarness::PluginDelegateMock::GetIPCMessageLoop() { 241 return ipc_message_loop_; 242 } 243 244 base::WaitableEvent* 245 PluginProxyTestHarness::PluginDelegateMock::GetShutdownEvent() { 246 return shutdown_event_; 247 } 248 249 IPC::PlatformFileForTransit 250 PluginProxyTestHarness::PluginDelegateMock::ShareHandleWithRemote( 251 base::PlatformFile handle, 252 base::ProcessId /* remote_pid */, 253 bool should_close_source) { 254 return IPC::GetFileHandleForProcess(handle, 255 base::Process::Current().handle(), 256 should_close_source); 257 } 258 259 std::set<PP_Instance>* 260 PluginProxyTestHarness::PluginDelegateMock::GetGloballySeenInstanceIDSet() { 261 return &instance_id_set_; 262 } 263 264 uint32 PluginProxyTestHarness::PluginDelegateMock::Register( 265 PluginDispatcher* plugin_dispatcher) { 266 return 0; 267 } 268 269 void PluginProxyTestHarness::PluginDelegateMock::Unregister( 270 uint32 plugin_dispatcher_id) { 271 } 272 273 IPC::Sender* PluginProxyTestHarness::PluginDelegateMock::GetBrowserSender() { 274 return browser_sender_; 275 } 276 277 std::string PluginProxyTestHarness::PluginDelegateMock::GetUILanguage() { 278 return std::string("en-US"); 279 } 280 281 void PluginProxyTestHarness::PluginDelegateMock::PreCacheFont( 282 const void* logfontw) { 283 } 284 285 void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL( 286 const std::string& url) { 287 } 288 289 PP_Resource PluginProxyTestHarness::PluginDelegateMock::CreateBrowserFont( 290 Connection connection, 291 PP_Instance instance, 292 const PP_BrowserFont_Trusted_Description& desc, 293 const Preferences& prefs) { 294 return 0; 295 } 296 297 // PluginProxyTest ------------------------------------------------------------- 298 299 PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS) { 300 } 301 302 PluginProxyTest::~PluginProxyTest() { 303 } 304 305 void PluginProxyTest::SetUp() { 306 SetUpHarness(); 307 } 308 309 void PluginProxyTest::TearDown() { 310 TearDownHarness(); 311 } 312 313 // PluginProxyMultiThreadTest -------------------------------------------------- 314 315 PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() { 316 } 317 318 PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() { 319 } 320 321 void PluginProxyMultiThreadTest::RunTest() { 322 main_thread_message_loop_proxy_ = 323 PpapiGlobals::Get()->GetMainThreadMessageLoop(); 324 ASSERT_EQ(main_thread_message_loop_proxy_.get(), 325 base::MessageLoopProxy::current()); 326 nested_main_thread_message_loop_.reset(new base::RunLoop()); 327 328 secondary_thread_.reset(new base::DelegateSimpleThread( 329 this, "PluginProxyMultiThreadTest")); 330 331 { 332 ProxyAutoLock auto_lock; 333 334 // MessageLoopResource assumes that the proxy lock has been acquired. 335 secondary_thread_message_loop_ = new MessageLoopResource(pp_instance()); 336 337 ASSERT_EQ(PP_OK, 338 secondary_thread_message_loop_->PostWork( 339 PP_MakeCompletionCallback( 340 &PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread, 341 this), 342 0)); 343 } 344 345 SetUpTestOnMainThread(); 346 347 secondary_thread_->Start(); 348 nested_main_thread_message_loop_->Run(); 349 secondary_thread_->Join(); 350 351 { 352 ProxyAutoLock auto_lock; 353 354 // The destruction requires a valid PpapiGlobals instance, so we should 355 // explicitly release it. 356 secondary_thread_message_loop_ = NULL; 357 } 358 359 secondary_thread_.reset(NULL); 360 nested_main_thread_message_loop_.reset(NULL); 361 main_thread_message_loop_proxy_ = NULL; 362 } 363 364 void PluginProxyMultiThreadTest::CheckOnThread(ThreadType thread_type) { 365 ProxyAutoLock auto_lock; 366 if (thread_type == MAIN_THREAD) { 367 ASSERT_TRUE(MessageLoopResource::GetCurrent()->is_main_thread_loop()); 368 } else { 369 ASSERT_EQ(secondary_thread_message_loop_.get(), 370 MessageLoopResource::GetCurrent()); 371 } 372 } 373 374 void PluginProxyMultiThreadTest::PostQuitForMainThread() { 375 main_thread_message_loop_proxy_->PostTask( 376 FROM_HERE, 377 base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop, 378 base::Unretained(this))); 379 } 380 381 void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() { 382 ProxyAutoLock auto_lock; 383 secondary_thread_message_loop_->PostQuit(PP_TRUE); 384 } 385 386 void PluginProxyMultiThreadTest::Run() { 387 ProxyAutoLock auto_lock; 388 ASSERT_EQ(PP_OK, secondary_thread_message_loop_->AttachToCurrentThread()); 389 ASSERT_EQ(PP_OK, secondary_thread_message_loop_->Run()); 390 secondary_thread_message_loop_->DetachFromThread(); 391 } 392 393 void PluginProxyMultiThreadTest::QuitNestedLoop() { 394 nested_main_thread_message_loop_->Quit(); 395 } 396 397 // static 398 void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread( 399 void* user_data, 400 int32_t result) { 401 EXPECT_EQ(PP_OK, result); 402 PluginProxyMultiThreadTest* thiz = 403 static_cast<PluginProxyMultiThreadTest*>(user_data); 404 thiz->CheckOnThread(SECONDARY_THREAD); 405 thiz->SetUpTestOnSecondaryThread(); 406 } 407 408 // HostProxyTestHarness -------------------------------------------------------- 409 410 class HostProxyTestHarness::MockSyncMessageStatusReceiver 411 : public HostDispatcher::SyncMessageStatusReceiver { 412 public: 413 virtual void BeginBlockOnSyncMessage() OVERRIDE {} 414 virtual void EndBlockOnSyncMessage() OVERRIDE {} 415 }; 416 417 HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config) 418 : globals_config_(globals_config), 419 status_receiver_(new MockSyncMessageStatusReceiver) { 420 } 421 422 HostProxyTestHarness::~HostProxyTestHarness() { 423 } 424 425 PpapiGlobals* HostProxyTestHarness::GetGlobals() { 426 return host_globals_.get(); 427 } 428 429 Dispatcher* HostProxyTestHarness::GetDispatcher() { 430 return host_dispatcher_.get(); 431 } 432 433 void HostProxyTestHarness::SetUpHarness() { 434 // These must be first since the dispatcher set-up uses them. 435 CreateHostGlobals(); 436 437 host_dispatcher_.reset(new HostDispatcher( 438 pp_module(), 439 &MockGetInterface, 440 status_receiver_.release(), 441 PpapiPermissions::AllPermissions())); 442 host_dispatcher_->InitWithTestSink(&sink()); 443 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get()); 444 } 445 446 void HostProxyTestHarness::SetUpHarnessWithChannel( 447 const IPC::ChannelHandle& channel_handle, 448 base::MessageLoopProxy* ipc_message_loop, 449 base::WaitableEvent* shutdown_event, 450 bool is_client) { 451 // These must be first since the dispatcher set-up uses them. 452 CreateHostGlobals(); 453 454 delegate_mock_.Init(ipc_message_loop, shutdown_event); 455 456 host_dispatcher_.reset(new HostDispatcher( 457 pp_module(), 458 &MockGetInterface, 459 status_receiver_.release(), 460 PpapiPermissions::AllPermissions())); 461 ppapi::Preferences preferences; 462 host_dispatcher_->InitHostWithChannel(&delegate_mock_, 463 base::kNullProcessId, channel_handle, 464 is_client, preferences); 465 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get()); 466 } 467 468 void HostProxyTestHarness::TearDownHarness() { 469 HostDispatcher::RemoveForInstance(pp_instance()); 470 host_dispatcher_.reset(); 471 host_globals_.reset(); 472 } 473 474 void HostProxyTestHarness::CreateHostGlobals() { 475 if (globals_config_ == PER_THREAD_GLOBALS) { 476 host_globals_.reset(new TestGlobals(PpapiGlobals::PerThreadForTest())); 477 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); 478 // The host side of the proxy does not lock. 479 ProxyLock::DisableLockingOnThreadForTest(); 480 } else { 481 ProxyLock::DisableLockingOnThreadForTest(); 482 host_globals_.reset(new TestGlobals()); 483 } 484 } 485 486 base::MessageLoopProxy* 487 HostProxyTestHarness::DelegateMock::GetIPCMessageLoop() { 488 return ipc_message_loop_; 489 } 490 491 base::WaitableEvent* HostProxyTestHarness::DelegateMock::GetShutdownEvent() { 492 return shutdown_event_; 493 } 494 495 IPC::PlatformFileForTransit 496 HostProxyTestHarness::DelegateMock::ShareHandleWithRemote( 497 base::PlatformFile handle, 498 base::ProcessId /* remote_pid */, 499 bool should_close_source) { 500 return IPC::GetFileHandleForProcess(handle, 501 base::Process::Current().handle(), 502 should_close_source); 503 } 504 505 506 // HostProxyTest --------------------------------------------------------------- 507 508 HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS) { 509 } 510 511 HostProxyTest::~HostProxyTest() { 512 } 513 514 void HostProxyTest::SetUp() { 515 SetUpHarness(); 516 } 517 518 void HostProxyTest::TearDown() { 519 TearDownHarness(); 520 } 521 522 // TwoWayTest --------------------------------------------------------------- 523 524 TwoWayTest::TwoWayTest(TwoWayTest::TwoWayTestMode test_mode) 525 : test_mode_(test_mode), 526 host_(ProxyTestHarnessBase::PER_THREAD_GLOBALS), 527 plugin_(ProxyTestHarnessBase::PER_THREAD_GLOBALS), 528 io_thread_("TwoWayTest_IOThread"), 529 plugin_thread_("TwoWayTest_PluginThread"), 530 remote_harness_(NULL), 531 local_harness_(NULL), 532 channel_created_(true, false), 533 shutdown_event_(true, false) { 534 if (test_mode == TEST_PPP_INTERFACE) { 535 remote_harness_ = &plugin_; 536 local_harness_ = &host_; 537 } else { 538 remote_harness_ = &host_; 539 local_harness_ = &plugin_; 540 } 541 } 542 543 TwoWayTest::~TwoWayTest() { 544 shutdown_event_.Signal(); 545 } 546 547 void TwoWayTest::SetUp() { 548 base::Thread::Options options; 549 options.message_loop_type = base::MessageLoop::TYPE_IO; 550 io_thread_.StartWithOptions(options); 551 plugin_thread_.Start(); 552 553 // Construct the IPC handle name using the process ID so we can safely run 554 // multiple |TwoWayTest|s concurrently. 555 std::ostringstream handle_name; 556 handle_name << "TwoWayTestChannel" << base::GetCurrentProcId(); 557 IPC::ChannelHandle handle(handle_name.str()); 558 base::WaitableEvent remote_harness_set_up(true, false); 559 plugin_thread_.message_loop_proxy()->PostTask( 560 FROM_HERE, 561 base::Bind(&SetUpRemoteHarness, 562 remote_harness_, 563 handle, 564 io_thread_.message_loop_proxy(), 565 &shutdown_event_, 566 &remote_harness_set_up)); 567 remote_harness_set_up.Wait(); 568 local_harness_->SetUpHarnessWithChannel(handle, 569 io_thread_.message_loop_proxy().get(), 570 &shutdown_event_, 571 true); // is_client 572 } 573 574 void TwoWayTest::TearDown() { 575 base::WaitableEvent remote_harness_torn_down(true, false); 576 plugin_thread_.message_loop_proxy()->PostTask( 577 FROM_HERE, 578 base::Bind(&TearDownRemoteHarness, 579 remote_harness_, 580 &remote_harness_torn_down)); 581 remote_harness_torn_down.Wait(); 582 583 local_harness_->TearDownHarness(); 584 585 io_thread_.Stop(); 586 } 587 588 void TwoWayTest::PostTaskOnRemoteHarness(const base::Closure& task) { 589 base::WaitableEvent task_complete(true, false); 590 plugin_thread_.message_loop_proxy()->PostTask(FROM_HERE, 591 base::Bind(&RunTaskOnRemoteHarness, 592 task, 593 &task_complete)); 594 task_complete.Wait(); 595 } 596 597 598 } // namespace proxy 599 } // namespace ppapi 600