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 virtual ~MockPluginProcessHostClient() {
47 BrowserThread::DeleteSoon(BrowserThread::IO
, FROM_HERE
, channel_
);
50 // PluginProcessHost::Client implementation.
51 virtual int ID() OVERRIDE
{ return 42; }
52 virtual bool OffTheRecord() OVERRIDE
{ return false; }
53 virtual ResourceContext
* GetResourceContext() OVERRIDE
{
56 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{}
57 virtual void OnSentPluginChannelRequest() OVERRIDE
{}
59 virtual void OnChannelOpened(const IPC::ChannelHandle
& handle
) OVERRIDE
{
60 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
61 ASSERT_TRUE(set_plugin_info_called_
);
62 ASSERT_TRUE(!channel_
);
63 channel_
= IPC::Channel::CreateClient(handle
, this).release();
64 ASSERT_TRUE(channel_
->Connect());
67 virtual void SetPluginInfo(const WebPluginInfo
& info
) OVERRIDE
{
68 ASSERT_TRUE(info
.mime_types
.size());
69 ASSERT_EQ(kNPAPITestPluginMimeType
, info
.mime_types
[0].mime_type
);
70 set_plugin_info_called_
= true;
73 virtual void OnError() OVERRIDE
{
77 // IPC::Listener implementation.
78 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
{
82 virtual void OnChannelConnected(int32 peer_pid
) OVERRIDE
{
87 virtual void OnChannelError() OVERRIDE
{
91 virtual void OnChannelDenied() OVERRIDE
{
94 virtual void OnChannelListenError() OVERRIDE
{
106 void QuitMessageLoop() {
107 BrowserThread::PostTask(
108 BrowserThread::UI
, FROM_HERE
, base::MessageLoop::QuitClosure());
111 ResourceContext
* context_
;
112 IPC::Channel
* channel_
;
113 bool set_plugin_info_called_
;
115 DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient
);
118 class MockPluginServiceFilter
: public content::PluginServiceFilter
{
120 MockPluginServiceFilter() {}
122 virtual bool IsPluginAvailable(
123 int render_process_id
,
127 const GURL
& policy_url
,
128 WebPluginInfo
* plugin
) OVERRIDE
{ return true; }
130 virtual bool CanLoadPlugin(
131 int render_process_id
,
132 const base::FilePath
& path
) OVERRIDE
{ return false; }
135 class PluginServiceTest
: public ContentBrowserTest
{
137 PluginServiceTest() {}
139 ResourceContext
* GetResourceContext() {
140 return shell()->web_contents()->GetBrowserContext()->GetResourceContext();
143 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
144 #if defined(OS_MACOSX)
145 base::FilePath browser_directory
;
146 PathService::Get(base::DIR_MODULE
, &browser_directory
);
147 command_line
->AppendSwitchPath(switches::kExtraPluginDir
,
148 browser_directory
.AppendASCII("plugins"));
150 // TODO(jam): since these plugin tests are running under Chrome, we need to
151 // tell it to disable its security features for old plugins. Once this is
152 // running under content_browsertests, these flags won't be needed.
153 // http://crbug.com/90448
154 // switches::kAlwaysAuthorizePlugins
155 command_line
->AppendSwitch("always-authorize-plugins");
159 // Try to open a channel to the test plugin. Minimal plugin process spawning
160 // test for the PluginService interface.
161 IN_PROC_BROWSER_TEST_F(PluginServiceTest
, OpenChannelToPlugin
) {
162 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
164 MockPluginProcessHostClient
mock_client(GetResourceContext(), false);
165 BrowserThread::PostTask(
166 BrowserThread::IO
, FROM_HERE
,
167 base::Bind(&OpenChannel
, &mock_client
));
171 IN_PROC_BROWSER_TEST_F(PluginServiceTest
, OpenChannelToDeniedPlugin
) {
172 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
174 MockPluginServiceFilter filter
;
175 PluginServiceImpl::GetInstance()->SetFilter(&filter
);
176 MockPluginProcessHostClient
mock_client(GetResourceContext(), true);
177 BrowserThread::PostTask(
178 BrowserThread::IO
, FROM_HERE
,
179 base::Bind(&OpenChannel
, &mock_client
));
183 // A strict mock that fails if any of the methods are called. They shouldn't be
184 // called since the request should get canceled before then.
185 class MockCanceledPluginServiceClient
: public PluginProcessHost::Client
{
187 MockCanceledPluginServiceClient(ResourceContext
* context
)
189 get_resource_context_called_(false) {
192 virtual ~MockCanceledPluginServiceClient() {}
194 // Client implementation.
195 MOCK_METHOD0(ID
, int());
196 virtual ResourceContext
* GetResourceContext() OVERRIDE
{
197 get_resource_context_called_
= true;
200 MOCK_METHOD0(OffTheRecord
, bool());
201 MOCK_METHOD1(OnFoundPluginProcessHost
, void(PluginProcessHost
* host
));
202 MOCK_METHOD0(OnSentPluginChannelRequest
, void());
203 MOCK_METHOD1(OnChannelOpened
, void(const IPC::ChannelHandle
& handle
));
204 MOCK_METHOD1(SetPluginInfo
, void(const WebPluginInfo
& info
));
205 MOCK_METHOD0(OnError
, void());
207 bool get_resource_context_called() const {
208 return get_resource_context_called_
;
212 ResourceContext
* context_
;
213 bool get_resource_context_called_
;
215 DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginServiceClient
);
218 void QuitUIMessageLoopFromIOThread() {
219 BrowserThread::PostTask(
220 BrowserThread::UI
, FROM_HERE
, base::MessageLoop::QuitClosure());
223 void OpenChannelAndThenCancel(PluginProcessHost::Client
* client
) {
225 // Immediately cancel it. This is guaranteed to work since PluginService needs
226 // to consult its filter on the FILE thread.
227 PluginServiceImpl::GetInstance()->CancelOpenChannelToNpapiPlugin(client
);
228 // Before we terminate the test, add a roundtrip through the FILE thread to
229 // make sure that it's had a chance to post back to the IO thread. Then signal
230 // the UI thread to stop and exit the test.
231 BrowserThread::PostTaskAndReply(
232 BrowserThread::FILE, FROM_HERE
,
233 base::Bind(&base::DoNothing
),
234 base::Bind(&QuitUIMessageLoopFromIOThread
));
237 // Should not attempt to open a channel, since it should be canceled early on.
238 IN_PROC_BROWSER_TEST_F(PluginServiceTest
, CancelOpenChannelToPluginService
) {
239 ::testing::StrictMock
<MockCanceledPluginServiceClient
> mock_client(
240 GetResourceContext());
241 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
242 base::Bind(OpenChannelAndThenCancel
, &mock_client
));
244 EXPECT_TRUE(mock_client
.get_resource_context_called());
247 class MockCanceledBeforeSentPluginProcessHostClient
248 : public MockCanceledPluginServiceClient
{
250 MockCanceledBeforeSentPluginProcessHostClient(
251 ResourceContext
* context
)
252 : MockCanceledPluginServiceClient(context
),
253 set_plugin_info_called_(false),
254 on_found_plugin_process_host_called_(false),
257 virtual ~MockCanceledBeforeSentPluginProcessHostClient() {}
259 // Client implementation.
260 virtual void SetPluginInfo(const WebPluginInfo
& info
) OVERRIDE
{
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
262 ASSERT_TRUE(info
.mime_types
.size());
263 ASSERT_EQ(kNPAPITestPluginMimeType
, info
.mime_types
[0].mime_type
);
264 set_plugin_info_called_
= true;
266 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
268 set_on_found_plugin_process_host_called();
270 // This gets called right before we request the plugin<=>renderer channel,
271 // so we have to post a task to cancel it.
272 base::MessageLoop::current()->PostTask(
274 base::Bind(&PluginProcessHost::CancelPendingRequest
,
275 base::Unretained(host
),
277 base::MessageLoop::current()->PostTask(
278 FROM_HERE
, base::Bind(&QuitUIMessageLoopFromIOThread
));
281 bool set_plugin_info_called() const {
282 return set_plugin_info_called_
;
285 bool on_found_plugin_process_host_called() const {
286 return on_found_plugin_process_host_called_
;
290 void set_on_found_plugin_process_host_called() {
291 on_found_plugin_process_host_called_
= true;
293 void set_host(PluginProcessHost
* host
) {
297 PluginProcessHost
* host() const { return host_
; }
300 bool set_plugin_info_called_
;
301 bool on_found_plugin_process_host_called_
;
302 PluginProcessHost
* host_
;
304 DISALLOW_COPY_AND_ASSIGN(MockCanceledBeforeSentPluginProcessHostClient
);
307 IN_PROC_BROWSER_TEST_F(
308 PluginServiceTest
, CancelBeforeSentOpenChannelToPluginProcessHost
) {
309 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
311 ::testing::StrictMock
<MockCanceledBeforeSentPluginProcessHostClient
>
312 mock_client(GetResourceContext());
313 BrowserThread::PostTask(
314 BrowserThread::IO
, FROM_HERE
,
315 base::Bind(&OpenChannel
, &mock_client
));
317 EXPECT_TRUE(mock_client
.get_resource_context_called());
318 EXPECT_TRUE(mock_client
.set_plugin_info_called());
319 EXPECT_TRUE(mock_client
.on_found_plugin_process_host_called());
322 class MockCanceledAfterSentPluginProcessHostClient
323 : public MockCanceledBeforeSentPluginProcessHostClient
{
325 MockCanceledAfterSentPluginProcessHostClient(
326 ResourceContext
* context
)
327 : MockCanceledBeforeSentPluginProcessHostClient(context
),
328 on_sent_plugin_channel_request_called_(false) {}
329 virtual ~MockCanceledAfterSentPluginProcessHostClient() {}
331 // Client implementation.
333 virtual int ID() OVERRIDE
{ return 42; }
334 virtual bool OffTheRecord() OVERRIDE
{ return false; }
336 // We override this guy again since we don't want to cancel yet.
337 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{
338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
339 set_on_found_plugin_process_host_called();
343 virtual void OnSentPluginChannelRequest() OVERRIDE
{
344 on_sent_plugin_channel_request_called_
= true;
345 host()->CancelSentRequest(this);
346 BrowserThread::PostTask(
347 BrowserThread::UI
, FROM_HERE
, base::MessageLoop::QuitClosure());
350 bool on_sent_plugin_channel_request_called() const {
351 return on_sent_plugin_channel_request_called_
;
355 bool on_sent_plugin_channel_request_called_
;
357 DISALLOW_COPY_AND_ASSIGN(MockCanceledAfterSentPluginProcessHostClient
);
360 // Should not attempt to open a channel, since it should be canceled early on.
361 IN_PROC_BROWSER_TEST_F(
362 PluginServiceTest
, CancelAfterSentOpenChannelToPluginProcessHost
) {
363 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported())
365 ::testing::StrictMock
<MockCanceledAfterSentPluginProcessHostClient
>
366 mock_client(GetResourceContext());
367 BrowserThread::PostTask(
368 BrowserThread::IO
, FROM_HERE
,
369 base::Bind(&OpenChannel
, &mock_client
));
371 EXPECT_TRUE(mock_client
.get_resource_context_called());
372 EXPECT_TRUE(mock_client
.set_plugin_info_called());
373 EXPECT_TRUE(mock_client
.on_found_plugin_process_host_called());
374 EXPECT_TRUE(mock_client
.on_sent_plugin_channel_request_called());
377 } // namespace content