root/content/browser/plugin_service_impl_browsertest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. OpenChannel
  2. expect_fail_
  3. ID
  4. OffTheRecord
  5. GetResourceContext
  6. OnFoundPluginProcessHost
  7. OnSentPluginChannelRequest
  8. OnChannelOpened
  9. SetPluginInfo
  10. OnError
  11. OnMessageReceived
  12. OnChannelConnected
  13. OnChannelError
  14. OnChannelDenied
  15. OnChannelListenError
  16. Fail
  17. QuitMessageLoop
  18. IsPluginAvailable
  19. CanLoadPlugin
  20. GetResourceContext
  21. SetUpCommandLine
  22. IN_PROC_BROWSER_TEST_F
  23. IN_PROC_BROWSER_TEST_F
  24. get_resource_context_called_
  25. GetResourceContext
  26. get_resource_context_called
  27. QuitUIMessageLoopFromIOThread
  28. OpenChannelAndThenCancel
  29. IN_PROC_BROWSER_TEST_F
  30. host_
  31. SetPluginInfo
  32. OnFoundPluginProcessHost
  33. set_plugin_info_called
  34. on_found_plugin_process_host_called
  35. set_on_found_plugin_process_host_called
  36. set_host
  37. host
  38. IN_PROC_BROWSER_TEST_F
  39. on_sent_plugin_channel_request_called_
  40. ID
  41. OffTheRecord
  42. OnFoundPluginProcessHost
  43. OnSentPluginChannelRequest
  44. on_sent_plugin_channel_request_called
  45. IN_PROC_BROWSER_TEST_F

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/plugin_service_impl.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/path_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/plugin_service_filter.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/test_browser_thread.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "testing/gmock/include/gmock/gmock.h"

namespace content {

const char kNPAPITestPluginMimeType[] = "application/vnd.npapi-test";

void OpenChannel(PluginProcessHost::Client* client) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  // Start opening the channel
  PluginServiceImpl::GetInstance()->OpenChannelToNpapiPlugin(
      0, 0, GURL(), GURL(), kNPAPITestPluginMimeType, client);
}

// Mock up of the Client and the Listener classes that would supply the
// communication channel with the plugin.
class MockPluginProcessHostClient : public PluginProcessHost::Client,
                                    public IPC::Listener {
 public:
  MockPluginProcessHostClient(ResourceContext* context, bool expect_fail)
      : context_(context),
        channel_(NULL),
        set_plugin_info_called_(false),
        expect_fail_(expect_fail) {
  }

  virtual ~MockPluginProcessHostClient() {
    if (channel_)
      BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_);
  }

  // PluginProcessHost::Client implementation.
  virtual int ID() OVERRIDE { return 42; }
  virtual bool OffTheRecord() OVERRIDE { return false; }
  virtual ResourceContext* GetResourceContext() OVERRIDE {
    return context_;
  }
  virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {}
  virtual void OnSentPluginChannelRequest() OVERRIDE {}

  virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE {
    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
    ASSERT_TRUE(set_plugin_info_called_);
    ASSERT_TRUE(!channel_);
    channel_ = new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this);
    ASSERT_TRUE(channel_->Connect());
  }

  virtual void SetPluginInfo(const WebPluginInfo& info) OVERRIDE {
    ASSERT_TRUE(info.mime_types.size());
    ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type);
    set_plugin_info_called_ = true;
  }

  virtual void OnError() OVERRIDE {
    Fail();
  }

  // IPC::Listener implementation.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
    Fail();
    return false;
  }
  virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
    if (expect_fail_)
      FAIL();
    QuitMessageLoop();
  }
  virtual void OnChannelError() OVERRIDE {
    Fail();
  }
#if defined(OS_POSIX)
  virtual void OnChannelDenied() OVERRIDE {
    Fail();
  }
  virtual void OnChannelListenError() OVERRIDE {
    Fail();
  }
#endif

 private:
  void Fail() {
    if (!expect_fail_)
      FAIL();
    QuitMessageLoop();
  }

  void QuitMessageLoop() {
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
  }

  ResourceContext* context_;
  IPC::Channel* channel_;
  bool set_plugin_info_called_;
  bool expect_fail_;
  DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient);
};

class MockPluginServiceFilter : public content::PluginServiceFilter {
 public:
  MockPluginServiceFilter() {}

  virtual bool IsPluginAvailable(
      int render_process_id,
      int render_view_id,
      const void* context,
      const GURL& url,
      const GURL& policy_url,
      WebPluginInfo* plugin) OVERRIDE { return true; }

  virtual bool CanLoadPlugin(
      int render_process_id,
      const base::FilePath& path) OVERRIDE { return false; }
};

class PluginServiceTest : public ContentBrowserTest {
 public:
  PluginServiceTest() {}

  ResourceContext* GetResourceContext() {
    return shell()->web_contents()->GetBrowserContext()->GetResourceContext();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
#if defined(OS_MACOSX)
    base::FilePath browser_directory;
    PathService::Get(base::DIR_MODULE, &browser_directory);
    command_line->AppendSwitchPath(switches::kExtraPluginDir,
                                   browser_directory.AppendASCII("plugins"));
#endif
    // TODO(jam): since these plugin tests are running under Chrome, we need to
    // tell it to disable its security features for old plugins. Once this is
    // running under content_browsertests, these flags won't be needed.
    // http://crbug.com/90448
    // switches::kAlwaysAuthorizePlugins
    command_line->AppendSwitch("always-authorize-plugins");
  }
};

// Try to open a channel to the test plugin. Minimal plugin process spawning
// test for the PluginService interface.
IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToPlugin) {
  if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
    return;
  MockPluginProcessHostClient mock_client(GetResourceContext(), false);
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&OpenChannel, &mock_client));
  RunMessageLoop();
}

IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToDeniedPlugin) {
  if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
    return;
  MockPluginServiceFilter filter;
  PluginServiceImpl::GetInstance()->SetFilter(&filter);
  MockPluginProcessHostClient mock_client(GetResourceContext(), true);
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&OpenChannel, &mock_client));
  RunMessageLoop();
}

// A strict mock that fails if any of the methods are called. They shouldn't be
// called since the request should get canceled before then.
class MockCanceledPluginServiceClient : public PluginProcessHost::Client {
 public:
  MockCanceledPluginServiceClient(ResourceContext* context)
      : context_(context),
        get_resource_context_called_(false) {
  }

  virtual ~MockCanceledPluginServiceClient() {}

  // Client implementation.
  MOCK_METHOD0(ID, int());
  virtual ResourceContext* GetResourceContext() OVERRIDE {
    get_resource_context_called_ = true;
    return context_;
  }
  MOCK_METHOD0(OffTheRecord, bool());
  MOCK_METHOD1(OnFoundPluginProcessHost, void(PluginProcessHost* host));
  MOCK_METHOD0(OnSentPluginChannelRequest, void());
  MOCK_METHOD1(OnChannelOpened, void(const IPC::ChannelHandle& handle));
  MOCK_METHOD1(SetPluginInfo, void(const WebPluginInfo& info));
  MOCK_METHOD0(OnError, void());

  bool get_resource_context_called() const {
    return get_resource_context_called_;
  }

 private:
  ResourceContext* context_;
  bool get_resource_context_called_;

  DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginServiceClient);
};

void QuitUIMessageLoopFromIOThread() {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
}

void OpenChannelAndThenCancel(PluginProcessHost::Client* client) {
  OpenChannel(client);
  // Immediately cancel it. This is guaranteed to work since PluginService needs
  // to consult its filter on the FILE thread.
  PluginServiceImpl::GetInstance()->CancelOpenChannelToNpapiPlugin(client);
  // Before we terminate the test, add a roundtrip through the FILE thread to
  // make sure that it's had a chance to post back to the IO thread. Then signal
  // the UI thread to stop and exit the test.
  BrowserThread::PostTaskAndReply(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&base::DoNothing),
      base::Bind(&QuitUIMessageLoopFromIOThread));
}

// Should not attempt to open a channel, since it should be canceled early on.
IN_PROC_BROWSER_TEST_F(PluginServiceTest, CancelOpenChannelToPluginService) {
  ::testing::StrictMock<MockCanceledPluginServiceClient> mock_client(
      GetResourceContext());
  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
                          base::Bind(OpenChannelAndThenCancel, &mock_client));
  RunMessageLoop();
  EXPECT_TRUE(mock_client.get_resource_context_called());
}

class MockCanceledBeforeSentPluginProcessHostClient
    : public MockCanceledPluginServiceClient {
 public:
  MockCanceledBeforeSentPluginProcessHostClient(
      ResourceContext* context)
      : MockCanceledPluginServiceClient(context),
        set_plugin_info_called_(false),
        on_found_plugin_process_host_called_(false),
        host_(NULL) {}

  virtual ~MockCanceledBeforeSentPluginProcessHostClient() {}

  // Client implementation.
  virtual void SetPluginInfo(const WebPluginInfo& info) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    ASSERT_TRUE(info.mime_types.size());
    ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type);
    set_plugin_info_called_ = true;
  }
  virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    set_on_found_plugin_process_host_called();
    set_host(host);
    // This gets called right before we request the plugin<=>renderer channel,
    // so we have to post a task to cancel it.
    base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&PluginProcessHost::CancelPendingRequest,
                   base::Unretained(host),
                   this));
    base::MessageLoop::current()->PostTask(
        FROM_HERE, base::Bind(&QuitUIMessageLoopFromIOThread));
  }

  bool set_plugin_info_called() const {
    return set_plugin_info_called_;
  }

  bool on_found_plugin_process_host_called() const {
    return on_found_plugin_process_host_called_;
  }

 protected:
  void set_on_found_plugin_process_host_called() {
    on_found_plugin_process_host_called_ = true;
  }
  void set_host(PluginProcessHost* host) {
    host_ = host;
  }

  PluginProcessHost* host() const { return host_; }

 private:
  bool set_plugin_info_called_;
  bool on_found_plugin_process_host_called_;
  PluginProcessHost* host_;

  DISALLOW_COPY_AND_ASSIGN(MockCanceledBeforeSentPluginProcessHostClient);
};

IN_PROC_BROWSER_TEST_F(
    PluginServiceTest, CancelBeforeSentOpenChannelToPluginProcessHost) {
  if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
    return;
  ::testing::StrictMock<MockCanceledBeforeSentPluginProcessHostClient>
      mock_client(GetResourceContext());
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&OpenChannel, &mock_client));
  RunMessageLoop();
  EXPECT_TRUE(mock_client.get_resource_context_called());
  EXPECT_TRUE(mock_client.set_plugin_info_called());
  EXPECT_TRUE(mock_client.on_found_plugin_process_host_called());
}

class MockCanceledAfterSentPluginProcessHostClient
    : public MockCanceledBeforeSentPluginProcessHostClient {
 public:
  MockCanceledAfterSentPluginProcessHostClient(
      ResourceContext* context)
      : MockCanceledBeforeSentPluginProcessHostClient(context),
        on_sent_plugin_channel_request_called_(false) {}
  virtual ~MockCanceledAfterSentPluginProcessHostClient() {}

  // Client implementation.

  virtual int ID() OVERRIDE { return 42; }
  virtual bool OffTheRecord() OVERRIDE { return false; }

  // We override this guy again since we don't want to cancel yet.
  virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    set_on_found_plugin_process_host_called();
    set_host(host);
  }

  virtual void OnSentPluginChannelRequest() OVERRIDE {
    on_sent_plugin_channel_request_called_ = true;
    host()->CancelSentRequest(this);
    BrowserThread::PostTask(
        BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
  }

  bool on_sent_plugin_channel_request_called() const {
    return on_sent_plugin_channel_request_called_;
  }

 private:
  bool on_sent_plugin_channel_request_called_;

  DISALLOW_COPY_AND_ASSIGN(MockCanceledAfterSentPluginProcessHostClient);
};

// Should not attempt to open a channel, since it should be canceled early on.
IN_PROC_BROWSER_TEST_F(
    PluginServiceTest, CancelAfterSentOpenChannelToPluginProcessHost) {
  if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
    return;
  ::testing::StrictMock<MockCanceledAfterSentPluginProcessHostClient>
      mock_client(GetResourceContext());
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&OpenChannel, &mock_client));
  RunMessageLoop();
  EXPECT_TRUE(mock_client.get_resource_context_called());
  EXPECT_TRUE(mock_client.set_plugin_info_called());
  EXPECT_TRUE(mock_client.on_found_plugin_process_host_called());
  EXPECT_TRUE(mock_client.on_sent_plugin_channel_request_called());
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */