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 21namespace ppapi { 22namespace proxy { 23 24namespace { 25// HostDispatcher requires a PPB_Proxy_Private, so we always provide a fallback 26// do-nothing implementation. 27void PluginCrashed(PP_Module module) { 28 NOTREACHED(); 29}; 30 31PP_Instance GetInstanceForResource(PP_Resource resource) { 32 // If a test relies on this, we need to implement it. 33 NOTREACHED(); 34 return 0; 35} 36 37void 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 43void AddRefModule(PP_Module module) {} 44void ReleaseModule(PP_Module module) {} 45PP_Bool IsInModuleDestructor(PP_Module module) { return PP_FALSE; } 46 47PPB_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. 64ObserverList<ProxyTestHarnessBase> get_interface_handlers_; 65 66const 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 79void 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 89void TearDownRemoteHarness(ProxyTestHarnessBase* harness, 90 base::WaitableEvent* harness_torn_down) { 91 harness->TearDownHarness(); 92 harness_torn_down->Signal(); 93} 94 95void 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 105ProxyTestHarnessBase::ProxyTestHarnessBase() : pp_module_(0x98765), 106 pp_instance_(0x12345) { 107 get_interface_handlers_.AddObserver(this); 108} 109 110ProxyTestHarnessBase::~ProxyTestHarnessBase() { 111 get_interface_handlers_.RemoveObserver(this); 112} 113 114const void* ProxyTestHarnessBase::GetInterface(const char* name) { 115 return registered_interfaces_[name]; 116} 117 118void ProxyTestHarnessBase::RegisterTestInterface(const char* name, 119 const void* test_interface) { 120 registered_interfaces_[name] = test_interface; 121} 122 123bool 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 148PluginProxyTestHarness::PluginProxyTestHarness( 149 GlobalsConfiguration globals_config) 150 : globals_config_(globals_config) { 151} 152 153PluginProxyTestHarness::~PluginProxyTestHarness() { 154} 155 156PpapiGlobals* PluginProxyTestHarness::GetGlobals() { 157 return plugin_globals_.get(); 158} 159 160Dispatcher* PluginProxyTestHarness::GetDispatcher() { 161 return plugin_dispatcher_.get(); 162} 163 164void 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 187void 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 213void 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 226void 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 239base::MessageLoopProxy* 240PluginProxyTestHarness::PluginDelegateMock::GetIPCMessageLoop() { 241 return ipc_message_loop_; 242} 243 244base::WaitableEvent* 245PluginProxyTestHarness::PluginDelegateMock::GetShutdownEvent() { 246 return shutdown_event_; 247} 248 249IPC::PlatformFileForTransit 250PluginProxyTestHarness::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 259std::set<PP_Instance>* 260PluginProxyTestHarness::PluginDelegateMock::GetGloballySeenInstanceIDSet() { 261 return &instance_id_set_; 262} 263 264uint32 PluginProxyTestHarness::PluginDelegateMock::Register( 265 PluginDispatcher* plugin_dispatcher) { 266 return 0; 267} 268 269void PluginProxyTestHarness::PluginDelegateMock::Unregister( 270 uint32 plugin_dispatcher_id) { 271} 272 273IPC::Sender* PluginProxyTestHarness::PluginDelegateMock::GetBrowserSender() { 274 return browser_sender_; 275} 276 277std::string PluginProxyTestHarness::PluginDelegateMock::GetUILanguage() { 278 return std::string("en-US"); 279} 280 281void PluginProxyTestHarness::PluginDelegateMock::PreCacheFont( 282 const void* logfontw) { 283} 284 285void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL( 286 const std::string& url) { 287} 288 289PP_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 299PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS) { 300} 301 302PluginProxyTest::~PluginProxyTest() { 303} 304 305void PluginProxyTest::SetUp() { 306 SetUpHarness(); 307} 308 309void PluginProxyTest::TearDown() { 310 TearDownHarness(); 311} 312 313// PluginProxyMultiThreadTest -------------------------------------------------- 314 315PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() { 316} 317 318PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() { 319} 320 321void 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 364void 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 374void PluginProxyMultiThreadTest::PostQuitForMainThread() { 375 main_thread_message_loop_proxy_->PostTask( 376 FROM_HERE, 377 base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop, 378 base::Unretained(this))); 379} 380 381void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() { 382 ProxyAutoLock auto_lock; 383 secondary_thread_message_loop_->PostQuit(PP_TRUE); 384} 385 386void 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 393void PluginProxyMultiThreadTest::QuitNestedLoop() { 394 nested_main_thread_message_loop_->Quit(); 395} 396 397// static 398void 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 410class HostProxyTestHarness::MockSyncMessageStatusReceiver 411 : public HostDispatcher::SyncMessageStatusReceiver { 412 public: 413 virtual void BeginBlockOnSyncMessage() OVERRIDE {} 414 virtual void EndBlockOnSyncMessage() OVERRIDE {} 415}; 416 417HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config) 418 : globals_config_(globals_config), 419 status_receiver_(new MockSyncMessageStatusReceiver) { 420} 421 422HostProxyTestHarness::~HostProxyTestHarness() { 423} 424 425PpapiGlobals* HostProxyTestHarness::GetGlobals() { 426 return host_globals_.get(); 427} 428 429Dispatcher* HostProxyTestHarness::GetDispatcher() { 430 return host_dispatcher_.get(); 431} 432 433void 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 446void 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 468void HostProxyTestHarness::TearDownHarness() { 469 HostDispatcher::RemoveForInstance(pp_instance()); 470 host_dispatcher_.reset(); 471 host_globals_.reset(); 472} 473 474void 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 486base::MessageLoopProxy* 487HostProxyTestHarness::DelegateMock::GetIPCMessageLoop() { 488 return ipc_message_loop_; 489} 490 491base::WaitableEvent* HostProxyTestHarness::DelegateMock::GetShutdownEvent() { 492 return shutdown_event_; 493} 494 495IPC::PlatformFileForTransit 496HostProxyTestHarness::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 508HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS) { 509} 510 511HostProxyTest::~HostProxyTest() { 512} 513 514void HostProxyTest::SetUp() { 515 SetUpHarness(); 516} 517 518void HostProxyTest::TearDown() { 519 TearDownHarness(); 520} 521 522// TwoWayTest --------------------------------------------------------------- 523 524TwoWayTest::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 543TwoWayTest::~TwoWayTest() { 544 shutdown_event_.Signal(); 545} 546 547void 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 574void 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 588void 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