Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / plugin_service_impl_browsertest.cc
blobb81b78cf08cfc4fb0b1cf9fd2144d0c971b38c32
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.
5 #include "content/browser/plugin_service_impl.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/path_service.h"
11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/plugin_service_filter.h"
13 #include "content/public/browser/resource_context.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/public/common/content_switches.h"
16 #include "content/public/test/content_browser_test.h"
17 #include "content/public/test/test_browser_thread.h"
18 #include "content/public/test/test_utils.h"
19 #include "content/shell/browser/shell.h"
20 #include "testing/gmock/include/gmock/gmock.h"
22 namespace content {
24 const char kNPAPITestPluginMimeType[] = "application/vnd.npapi-test";
26 void OpenChannel(PluginProcessHost::Client* client) {
27 DCHECK_CURRENTLY_ON(BrowserThread::IO);
28 // Start opening the channel
29 PluginServiceImpl::GetInstance()->OpenChannelToNpapiPlugin(
30 0, 0, GURL(), GURL(), kNPAPITestPluginMimeType, client);
33 // Mock up of the Client and the Listener classes that would supply the
34 // communication channel with the plugin.
35 class MockPluginProcessHostClient : public PluginProcessHost::Client,
36 public IPC::Listener {
37 public:
38 MockPluginProcessHostClient(ResourceContext* context, bool expect_fail)
39 : context_(context),
40 channel_(NULL),
41 set_plugin_info_called_(false),
42 expect_fail_(expect_fail) {
45 ~MockPluginProcessHostClient() override {
46 if (channel_)
47 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_);
50 // PluginProcessHost::Client implementation.
51 int ID() override { return 42; }
52 bool OffTheRecord() override { return false; }
53 ResourceContext* GetResourceContext() override { return context_; }
54 void OnFoundPluginProcessHost(PluginProcessHost* host) override {}
55 void OnSentPluginChannelRequest() override {}
57 void OnChannelOpened(const IPC::ChannelHandle& handle) override {
58 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
59 ASSERT_TRUE(set_plugin_info_called_);
60 ASSERT_TRUE(!channel_);
61 channel_ = IPC::Channel::CreateClient(handle, this).release();
62 ASSERT_TRUE(channel_->Connect());
65 void SetPluginInfo(const WebPluginInfo& info) override {
66 ASSERT_TRUE(info.mime_types.size());
67 ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type);
68 set_plugin_info_called_ = true;
71 void OnError() override { Fail(); }
73 // IPC::Listener implementation.
74 bool OnMessageReceived(const IPC::Message& message) override {
75 Fail();
76 return false;
78 void OnChannelConnected(int32 peer_pid) override {
79 if (expect_fail_)
80 FAIL();
81 QuitMessageLoop();
83 void OnChannelError() override { Fail(); }
84 #if defined(OS_POSIX)
85 void OnChannelDenied() override { Fail(); }
86 void OnChannelListenError() override { Fail(); }
87 #endif
89 private:
90 void Fail() {
91 if (!expect_fail_)
92 FAIL();
93 QuitMessageLoop();
96 void QuitMessageLoop() {
97 BrowserThread::PostTask(
98 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
101 ResourceContext* context_;
102 IPC::Channel* channel_;
103 bool set_plugin_info_called_;
104 bool expect_fail_;
105 DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient);
108 class MockPluginServiceFilter : public content::PluginServiceFilter {
109 public:
110 MockPluginServiceFilter() {}
112 bool IsPluginAvailable(int render_process_id,
113 int render_view_id,
114 const void* context,
115 const GURL& url,
116 const GURL& policy_url,
117 WebPluginInfo* plugin) override {
118 return true;
121 bool CanLoadPlugin(int render_process_id,
122 const base::FilePath& path) override {
123 return false;
127 class PluginServiceTest : public ContentBrowserTest {
128 public:
129 PluginServiceTest() {}
131 ResourceContext* GetResourceContext() {
132 return shell()->web_contents()->GetBrowserContext()->GetResourceContext();
135 void SetUpCommandLine(base::CommandLine* command_line) override {
136 #if defined(OS_MACOSX)
137 base::FilePath browser_directory;
138 PathService::Get(base::DIR_MODULE, &browser_directory);
139 command_line->AppendSwitchPath(switches::kExtraPluginDir,
140 browser_directory.AppendASCII("plugins"));
141 #endif
142 // TODO(jam): since these plugin tests are running under Chrome, we need to
143 // tell it to disable its security features for old plugins. Once this is
144 // running under content_browsertests, these flags won't be needed.
145 // http://crbug.com/90448
146 // switches::kAlwaysAuthorizePlugins
147 command_line->AppendSwitch("always-authorize-plugins");
151 // Try to open a channel to the test plugin. Minimal plugin process spawning
152 // test for the PluginService interface.
153 IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToPlugin) {
154 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
155 return;
156 MockPluginProcessHostClient mock_client(GetResourceContext(), false);
157 BrowserThread::PostTask(
158 BrowserThread::IO, FROM_HERE,
159 base::Bind(&OpenChannel, &mock_client));
160 RunMessageLoop();
163 IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToDeniedPlugin) {
164 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
165 return;
166 MockPluginServiceFilter filter;
167 PluginServiceImpl::GetInstance()->SetFilter(&filter);
168 MockPluginProcessHostClient mock_client(GetResourceContext(), true);
169 BrowserThread::PostTask(
170 BrowserThread::IO, FROM_HERE,
171 base::Bind(&OpenChannel, &mock_client));
172 RunMessageLoop();
175 // A strict mock that fails if any of the methods are called. They shouldn't be
176 // called since the request should get canceled before then.
177 class MockCanceledPluginServiceClient : public PluginProcessHost::Client {
178 public:
179 MockCanceledPluginServiceClient(ResourceContext* context)
180 : context_(context),
181 get_resource_context_called_(false) {
184 virtual ~MockCanceledPluginServiceClient() {}
186 // Client implementation.
187 MOCK_METHOD0(ID, int());
188 virtual ResourceContext* GetResourceContext() override {
189 get_resource_context_called_ = true;
190 return context_;
192 MOCK_METHOD0(OffTheRecord, bool());
193 MOCK_METHOD1(OnFoundPluginProcessHost, void(PluginProcessHost* host));
194 MOCK_METHOD0(OnSentPluginChannelRequest, void());
195 MOCK_METHOD1(OnChannelOpened, void(const IPC::ChannelHandle& handle));
196 MOCK_METHOD1(SetPluginInfo, void(const WebPluginInfo& info));
197 MOCK_METHOD0(OnError, void());
199 bool get_resource_context_called() const {
200 return get_resource_context_called_;
203 private:
204 ResourceContext* context_;
205 bool get_resource_context_called_;
207 DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginServiceClient);
210 void QuitUIMessageLoopFromIOThread() {
211 BrowserThread::PostTask(
212 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
215 void OpenChannelAndThenCancel(PluginProcessHost::Client* client) {
216 OpenChannel(client);
217 // Immediately cancel it. This is guaranteed to work since PluginService needs
218 // to consult its filter on the FILE thread.
219 PluginServiceImpl::GetInstance()->CancelOpenChannelToNpapiPlugin(client);
220 // Before we terminate the test, add a roundtrip through the FILE thread to
221 // make sure that it's had a chance to post back to the IO thread. Then signal
222 // the UI thread to stop and exit the test.
223 BrowserThread::PostTaskAndReply(
224 BrowserThread::FILE, FROM_HERE,
225 base::Bind(&base::DoNothing),
226 base::Bind(&QuitUIMessageLoopFromIOThread));
229 // Should not attempt to open a channel, since it should be canceled early on.
230 IN_PROC_BROWSER_TEST_F(PluginServiceTest, CancelOpenChannelToPluginService) {
231 ::testing::StrictMock<MockCanceledPluginServiceClient> mock_client(
232 GetResourceContext());
233 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
234 base::Bind(OpenChannelAndThenCancel, &mock_client));
235 RunMessageLoop();
236 EXPECT_TRUE(mock_client.get_resource_context_called());
239 class MockCanceledBeforeSentPluginProcessHostClient
240 : public MockCanceledPluginServiceClient {
241 public:
242 MockCanceledBeforeSentPluginProcessHostClient(
243 ResourceContext* context)
244 : MockCanceledPluginServiceClient(context),
245 set_plugin_info_called_(false),
246 on_found_plugin_process_host_called_(false),
247 host_(NULL) {}
249 ~MockCanceledBeforeSentPluginProcessHostClient() override {}
251 // Client implementation.
252 void SetPluginInfo(const WebPluginInfo& info) override {
253 DCHECK_CURRENTLY_ON(BrowserThread::IO);
254 ASSERT_TRUE(info.mime_types.size());
255 ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type);
256 set_plugin_info_called_ = true;
258 void OnFoundPluginProcessHost(PluginProcessHost* host) override {
259 DCHECK_CURRENTLY_ON(BrowserThread::IO);
260 set_on_found_plugin_process_host_called();
261 set_host(host);
262 // This gets called right before we request the plugin<=>renderer channel,
263 // so we have to post a task to cancel it.
264 base::MessageLoop::current()->PostTask(
265 FROM_HERE,
266 base::Bind(&PluginProcessHost::CancelPendingRequest,
267 base::Unretained(host),
268 this));
269 base::MessageLoop::current()->PostTask(
270 FROM_HERE, base::Bind(&QuitUIMessageLoopFromIOThread));
273 bool set_plugin_info_called() const {
274 return set_plugin_info_called_;
277 bool on_found_plugin_process_host_called() const {
278 return on_found_plugin_process_host_called_;
281 protected:
282 void set_on_found_plugin_process_host_called() {
283 on_found_plugin_process_host_called_ = true;
285 void set_host(PluginProcessHost* host) {
286 host_ = host;
289 PluginProcessHost* host() const { return host_; }
291 private:
292 bool set_plugin_info_called_;
293 bool on_found_plugin_process_host_called_;
294 PluginProcessHost* host_;
296 DISALLOW_COPY_AND_ASSIGN(MockCanceledBeforeSentPluginProcessHostClient);
299 IN_PROC_BROWSER_TEST_F(
300 PluginServiceTest, CancelBeforeSentOpenChannelToPluginProcessHost) {
301 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
302 return;
303 ::testing::StrictMock<MockCanceledBeforeSentPluginProcessHostClient>
304 mock_client(GetResourceContext());
305 BrowserThread::PostTask(
306 BrowserThread::IO, FROM_HERE,
307 base::Bind(&OpenChannel, &mock_client));
308 RunMessageLoop();
309 EXPECT_TRUE(mock_client.get_resource_context_called());
310 EXPECT_TRUE(mock_client.set_plugin_info_called());
311 EXPECT_TRUE(mock_client.on_found_plugin_process_host_called());
314 class MockCanceledAfterSentPluginProcessHostClient
315 : public MockCanceledBeforeSentPluginProcessHostClient {
316 public:
317 MockCanceledAfterSentPluginProcessHostClient(
318 ResourceContext* context)
319 : MockCanceledBeforeSentPluginProcessHostClient(context),
320 on_sent_plugin_channel_request_called_(false) {}
321 ~MockCanceledAfterSentPluginProcessHostClient() override {}
323 // Client implementation.
325 int ID() override { return 42; }
326 bool OffTheRecord() override { return false; }
328 // We override this guy again since we don't want to cancel yet.
329 void OnFoundPluginProcessHost(PluginProcessHost* host) override {
330 DCHECK_CURRENTLY_ON(BrowserThread::IO);
331 set_on_found_plugin_process_host_called();
332 set_host(host);
335 void OnSentPluginChannelRequest() override {
336 on_sent_plugin_channel_request_called_ = true;
337 host()->CancelSentRequest(this);
338 BrowserThread::PostTask(
339 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
342 bool on_sent_plugin_channel_request_called() const {
343 return on_sent_plugin_channel_request_called_;
346 private:
347 bool on_sent_plugin_channel_request_called_;
349 DISALLOW_COPY_AND_ASSIGN(MockCanceledAfterSentPluginProcessHostClient);
352 // Should not attempt to open a channel, since it should be canceled early on.
353 IN_PROC_BROWSER_TEST_F(
354 PluginServiceTest, CancelAfterSentOpenChannelToPluginProcessHost) {
355 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
356 return;
357 ::testing::StrictMock<MockCanceledAfterSentPluginProcessHostClient>
358 mock_client(GetResourceContext());
359 BrowserThread::PostTask(
360 BrowserThread::IO, FROM_HERE,
361 base::Bind(&OpenChannel, &mock_client));
362 RunMessageLoop();
363 EXPECT_TRUE(mock_client.get_resource_context_called());
364 EXPECT_TRUE(mock_client.set_plugin_info_called());
365 EXPECT_TRUE(mock_client.on_found_plugin_process_host_called());
366 EXPECT_TRUE(mock_client.on_sent_plugin_channel_request_called());
369 } // namespace content