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