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"
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"
24 const char kNPAPITestPluginMimeType
[] = "application/vnd.npapi-test";
26 void OpenChannel(PluginProcessHost::Client
* client
) {
27 DCHECK(BrowserThread::CurrentlyOn(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
{
38 MockPluginProcessHostClient(ResourceContext
* context
, bool expect_fail
)
41 set_plugin_info_called_(false),
42 expect_fail_(expect_fail
) {
45 ~MockPluginProcessHostClient() override
{
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
{
78 void OnChannelConnected(int32 peer_pid
) override
{
83 void OnChannelError() override
{ Fail(); }
85 void OnChannelDenied() override
{ Fail(); }
86 void OnChannelListenError() override
{ Fail(); }
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_
;
105 DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient
);
108 class MockPluginServiceFilter
: public content::PluginServiceFilter
{
110 MockPluginServiceFilter() {}
112 bool IsPluginAvailable(int render_process_id
,
116 const GURL
& policy_url
,
117 WebPluginInfo
* plugin
) override
{
121 bool CanLoadPlugin(int render_process_id
,
122 const base::FilePath
& path
) override
{
127 class PluginServiceTest
: public ContentBrowserTest
{
129 PluginServiceTest() {}
131 ResourceContext
* GetResourceContext() {
132 return shell()->web_contents()->GetBrowserContext()->GetResourceContext();
135 void SetUpCommandLine(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"));
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())
156 MockPluginProcessHostClient
mock_client(GetResourceContext(), false);
157 BrowserThread::PostTask(
158 BrowserThread::IO
, FROM_HERE
,
159 base::Bind(&OpenChannel
, &mock_client
));
163 IN_PROC_BROWSER_TEST_F(PluginServiceTest
, OpenChannelToDeniedPlugin
) {
164 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
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
));
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
{
179 MockCanceledPluginServiceClient(ResourceContext
* 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;
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_
;
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
) {
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
));
236 EXPECT_TRUE(mock_client
.get_resource_context_called());
239 class MockCanceledBeforeSentPluginProcessHostClient
240 : public MockCanceledPluginServiceClient
{
242 MockCanceledBeforeSentPluginProcessHostClient(
243 ResourceContext
* context
)
244 : MockCanceledPluginServiceClient(context
),
245 set_plugin_info_called_(false),
246 on_found_plugin_process_host_called_(false),
249 ~MockCanceledBeforeSentPluginProcessHostClient() override
{}
251 // Client implementation.
252 void SetPluginInfo(const WebPluginInfo
& info
) override
{
253 DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::IO
));
260 set_on_found_plugin_process_host_called();
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(
266 base::Bind(&PluginProcessHost::CancelPendingRequest
,
267 base::Unretained(host
),
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_
;
282 void set_on_found_plugin_process_host_called() {
283 on_found_plugin_process_host_called_
= true;
285 void set_host(PluginProcessHost
* host
) {
289 PluginProcessHost
* host() const { return host_
; }
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())
303 ::testing::StrictMock
<MockCanceledBeforeSentPluginProcessHostClient
>
304 mock_client(GetResourceContext());
305 BrowserThread::PostTask(
306 BrowserThread::IO
, FROM_HERE
,
307 base::Bind(&OpenChannel
, &mock_client
));
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
{
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(BrowserThread::CurrentlyOn(BrowserThread::IO
));
331 set_on_found_plugin_process_host_called();
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_
;
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())
357 ::testing::StrictMock
<MockCanceledAfterSentPluginProcessHostClient
>
358 mock_client(GetResourceContext());
359 BrowserThread::PostTask(
360 BrowserThread::IO
, FROM_HERE
,
361 base::Bind(&OpenChannel
, &mock_client
));
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