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 } else { 231 plugin_globals_.reset(new PluginGlobals()); 232 } 233 } 234 235 base::MessageLoopProxy* 236 PluginProxyTestHarness::PluginDelegateMock::GetIPCMessageLoop() { 237 return ipc_message_loop_; 238 } 239 240 base::WaitableEvent* 241 PluginProxyTestHarness::PluginDelegateMock::GetShutdownEvent() { 242 return shutdown_event_; 243 } 244 245 IPC::PlatformFileForTransit 246 PluginProxyTestHarness::PluginDelegateMock::ShareHandleWithRemote( 247 base::PlatformFile handle, 248 base::ProcessId /* remote_pid */, 249 bool should_close_source) { 250 return IPC::GetFileHandleForProcess(handle, 251 base::Process::Current().handle(), 252 should_close_source); 253 } 254 255 std::set<PP_Instance>* 256 PluginProxyTestHarness::PluginDelegateMock::GetGloballySeenInstanceIDSet() { 257 return &instance_id_set_; 258 } 259 260 uint32 PluginProxyTestHarness::PluginDelegateMock::Register( 261 PluginDispatcher* plugin_dispatcher) { 262 return 0; 263 } 264 265 void PluginProxyTestHarness::PluginDelegateMock::Unregister( 266 uint32 plugin_dispatcher_id) { 267 } 268 269 IPC::Sender* PluginProxyTestHarness::PluginDelegateMock::GetBrowserSender() { 270 return browser_sender_; 271 } 272 273 std::string PluginProxyTestHarness::PluginDelegateMock::GetUILanguage() { 274 return std::string("en-US"); 275 } 276 277 void PluginProxyTestHarness::PluginDelegateMock::PreCacheFont( 278 const void* logfontw) { 279 } 280 281 void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL( 282 const std::string& url) { 283 } 284 285 PP_Resource PluginProxyTestHarness::PluginDelegateMock::CreateBrowserFont( 286 Connection connection, 287 PP_Instance instance, 288 const PP_BrowserFont_Trusted_Description& desc, 289 const Preferences& prefs) { 290 return 0; 291 } 292 293 // PluginProxyTest ------------------------------------------------------------- 294 295 PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS) { 296 } 297 298 PluginProxyTest::~PluginProxyTest() { 299 } 300 301 void PluginProxyTest::SetUp() { 302 SetUpHarness(); 303 } 304 305 void PluginProxyTest::TearDown() { 306 TearDownHarness(); 307 } 308 309 // PluginProxyMultiThreadTest -------------------------------------------------- 310 311 PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() { 312 } 313 314 PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() { 315 } 316 317 void PluginProxyMultiThreadTest::RunTest() { 318 main_thread_message_loop_proxy_ = 319 PpapiGlobals::Get()->GetMainThreadMessageLoop(); 320 ASSERT_EQ(main_thread_message_loop_proxy_.get(), 321 base::MessageLoopProxy::current()); 322 nested_main_thread_message_loop_.reset(new base::RunLoop()); 323 324 secondary_thread_.reset(new base::DelegateSimpleThread( 325 this, "PluginProxyMultiThreadTest")); 326 327 { 328 ProxyAutoLock auto_lock; 329 330 // MessageLoopResource assumes that the proxy lock has been acquired. 331 secondary_thread_message_loop_ = new MessageLoopResource(pp_instance()); 332 333 ASSERT_EQ(PP_OK, 334 secondary_thread_message_loop_->PostWork( 335 PP_MakeCompletionCallback( 336 &PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread, 337 this), 338 0)); 339 } 340 341 SetUpTestOnMainThread(); 342 343 secondary_thread_->Start(); 344 nested_main_thread_message_loop_->Run(); 345 secondary_thread_->Join(); 346 347 { 348 ProxyAutoLock auto_lock; 349 350 // The destruction requires a valid PpapiGlobals instance, so we should 351 // explicitly release it. 352 secondary_thread_message_loop_ = NULL; 353 } 354 355 secondary_thread_.reset(NULL); 356 nested_main_thread_message_loop_.reset(NULL); 357 main_thread_message_loop_proxy_ = NULL; 358 } 359 360 void PluginProxyMultiThreadTest::CheckOnThread(ThreadType thread_type) { 361 ProxyAutoLock auto_lock; 362 if (thread_type == MAIN_THREAD) { 363 ASSERT_TRUE(MessageLoopResource::GetCurrent()->is_main_thread_loop()); 364 } else { 365 ASSERT_EQ(secondary_thread_message_loop_.get(), 366 MessageLoopResource::GetCurrent()); 367 } 368 } 369 370 void PluginProxyMultiThreadTest::PostQuitForMainThread() { 371 main_thread_message_loop_proxy_->PostTask( 372 FROM_HERE, 373 base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop, 374 base::Unretained(this))); 375 } 376 377 void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() { 378 ProxyAutoLock auto_lock; 379 secondary_thread_message_loop_->PostQuit(PP_TRUE); 380 } 381 382 void PluginProxyMultiThreadTest::Run() { 383 ProxyAutoLock auto_lock; 384 ASSERT_EQ(PP_OK, secondary_thread_message_loop_->AttachToCurrentThread()); 385 ASSERT_EQ(PP_OK, secondary_thread_message_loop_->Run()); 386 secondary_thread_message_loop_->DetachFromThread(); 387 } 388 389 void PluginProxyMultiThreadTest::QuitNestedLoop() { 390 nested_main_thread_message_loop_->Quit(); 391 } 392 393 // static 394 void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread( 395 void* user_data, 396 int32_t result) { 397 EXPECT_EQ(PP_OK, result); 398 PluginProxyMultiThreadTest* thiz = 399 static_cast<PluginProxyMultiThreadTest*>(user_data); 400 thiz->CheckOnThread(SECONDARY_THREAD); 401 thiz->SetUpTestOnSecondaryThread(); 402 } 403 404 // HostProxyTestHarness -------------------------------------------------------- 405 406 class HostProxyTestHarness::MockSyncMessageStatusReceiver 407 : public HostDispatcher::SyncMessageStatusReceiver { 408 public: 409 virtual void BeginBlockOnSyncMessage() OVERRIDE {} 410 virtual void EndBlockOnSyncMessage() OVERRIDE {} 411 }; 412 413 HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config) 414 : globals_config_(globals_config), 415 status_receiver_(new MockSyncMessageStatusReceiver) { 416 } 417 418 HostProxyTestHarness::~HostProxyTestHarness() { 419 } 420 421 PpapiGlobals* HostProxyTestHarness::GetGlobals() { 422 return host_globals_.get(); 423 } 424 425 Dispatcher* HostProxyTestHarness::GetDispatcher() { 426 return host_dispatcher_.get(); 427 } 428 429 void HostProxyTestHarness::SetUpHarness() { 430 // These must be first since the dispatcher set-up uses them. 431 CreateHostGlobals(); 432 433 host_dispatcher_.reset(new HostDispatcher( 434 pp_module(), 435 &MockGetInterface, 436 status_receiver_.release(), 437 PpapiPermissions::AllPermissions())); 438 host_dispatcher_->InitWithTestSink(&sink()); 439 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get()); 440 } 441 442 void HostProxyTestHarness::SetUpHarnessWithChannel( 443 const IPC::ChannelHandle& channel_handle, 444 base::MessageLoopProxy* ipc_message_loop, 445 base::WaitableEvent* shutdown_event, 446 bool is_client) { 447 // These must be first since the dispatcher set-up uses them. 448 CreateHostGlobals(); 449 450 delegate_mock_.Init(ipc_message_loop, shutdown_event); 451 452 host_dispatcher_.reset(new HostDispatcher( 453 pp_module(), 454 &MockGetInterface, 455 status_receiver_.release(), 456 PpapiPermissions::AllPermissions())); 457 ppapi::Preferences preferences; 458 host_dispatcher_->InitHostWithChannel(&delegate_mock_, 459 base::kNullProcessId, channel_handle, 460 is_client, preferences); 461 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get()); 462 } 463 464 void HostProxyTestHarness::TearDownHarness() { 465 HostDispatcher::RemoveForInstance(pp_instance()); 466 host_dispatcher_.reset(); 467 host_globals_.reset(); 468 } 469 470 void HostProxyTestHarness::CreateHostGlobals() { 471 if (globals_config_ == PER_THREAD_GLOBALS) { 472 host_globals_.reset(new TestGlobals(PpapiGlobals::PerThreadForTest())); 473 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); 474 } else { 475 host_globals_.reset(new TestGlobals()); 476 } 477 } 478 479 base::MessageLoopProxy* 480 HostProxyTestHarness::DelegateMock::GetIPCMessageLoop() { 481 return ipc_message_loop_; 482 } 483 484 base::WaitableEvent* HostProxyTestHarness::DelegateMock::GetShutdownEvent() { 485 return shutdown_event_; 486 } 487 488 IPC::PlatformFileForTransit 489 HostProxyTestHarness::DelegateMock::ShareHandleWithRemote( 490 base::PlatformFile handle, 491 base::ProcessId /* remote_pid */, 492 bool should_close_source) { 493 return IPC::GetFileHandleForProcess(handle, 494 base::Process::Current().handle(), 495 should_close_source); 496 } 497 498 499 // HostProxyTest --------------------------------------------------------------- 500 501 HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS) { 502 } 503 504 HostProxyTest::~HostProxyTest() { 505 } 506 507 void HostProxyTest::SetUp() { 508 SetUpHarness(); 509 } 510 511 void HostProxyTest::TearDown() { 512 TearDownHarness(); 513 } 514 515 // TwoWayTest --------------------------------------------------------------- 516 517 TwoWayTest::TwoWayTest(TwoWayTest::TwoWayTestMode test_mode) 518 : test_mode_(test_mode), 519 host_(ProxyTestHarnessBase::PER_THREAD_GLOBALS), 520 plugin_(ProxyTestHarnessBase::PER_THREAD_GLOBALS), 521 io_thread_("TwoWayTest_IOThread"), 522 plugin_thread_("TwoWayTest_PluginThread"), 523 remote_harness_(NULL), 524 local_harness_(NULL), 525 channel_created_(true, false), 526 shutdown_event_(true, false) { 527 if (test_mode == TEST_PPP_INTERFACE) { 528 remote_harness_ = &plugin_; 529 local_harness_ = &host_; 530 } else { 531 remote_harness_ = &host_; 532 local_harness_ = &plugin_; 533 } 534 } 535 536 TwoWayTest::~TwoWayTest() { 537 shutdown_event_.Signal(); 538 } 539 540 void TwoWayTest::SetUp() { 541 base::Thread::Options options; 542 options.message_loop_type = base::MessageLoop::TYPE_IO; 543 io_thread_.StartWithOptions(options); 544 plugin_thread_.Start(); 545 546 // Construct the IPC handle name using the process ID so we can safely run 547 // multiple |TwoWayTest|s concurrently. 548 std::ostringstream handle_name; 549 handle_name << "TwoWayTestChannel" << base::GetCurrentProcId(); 550 IPC::ChannelHandle handle(handle_name.str()); 551 base::WaitableEvent remote_harness_set_up(true, false); 552 plugin_thread_.message_loop_proxy()->PostTask( 553 FROM_HERE, 554 base::Bind(&SetUpRemoteHarness, 555 remote_harness_, 556 handle, 557 io_thread_.message_loop_proxy(), 558 &shutdown_event_, 559 &remote_harness_set_up)); 560 remote_harness_set_up.Wait(); 561 local_harness_->SetUpHarnessWithChannel(handle, 562 io_thread_.message_loop_proxy().get(), 563 &shutdown_event_, 564 true); // is_client 565 } 566 567 void TwoWayTest::TearDown() { 568 base::WaitableEvent remote_harness_torn_down(true, false); 569 plugin_thread_.message_loop_proxy()->PostTask( 570 FROM_HERE, 571 base::Bind(&TearDownRemoteHarness, 572 remote_harness_, 573 &remote_harness_torn_down)); 574 remote_harness_torn_down.Wait(); 575 576 local_harness_->TearDownHarness(); 577 578 io_thread_.Stop(); 579 } 580 581 void TwoWayTest::PostTaskOnRemoteHarness(const base::Closure& task) { 582 base::WaitableEvent task_complete(true, false); 583 plugin_thread_.message_loop_proxy()->PostTask(FROM_HERE, 584 base::Bind(&RunTaskOnRemoteHarness, 585 task, 586 &task_complete)); 587 task_complete.Wait(); 588 } 589 590 591 } // namespace proxy 592 } // namespace ppapi 593