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 "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