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