Home | History | Annotate | Download | only in proxy
      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