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/resource_context.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/common/content_switches.h"
15 #include "content/public/test/test_browser_thread.h"
16 #include "content/public/test/test_utils.h"
17 #include "content/shell/shell.h"
18 #include "content/test/content_browser_test.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "webkit/plugins/npapi/plugin_list.h"
21 #include "webkit/plugins/npapi/plugin_utils.h"
25 const char kNPAPITestPluginMimeType
[] = "application/vnd.npapi-test";
27 void OpenChannel(PluginProcessHost::Client
* client
) {
28 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
29 // Start opening the channel
30 PluginServiceImpl::GetInstance()->OpenChannelToNpapiPlugin(
31 0, 0, GURL(), GURL(), kNPAPITestPluginMimeType
, client
);
34 // Mock up of the Client and the Listener classes that would supply the
35 // communication channel with the plugin.
36 class MockPluginProcessHostClient
: public PluginProcessHost::Client
,
37 public IPC::Listener
{
39 MockPluginProcessHostClient(ResourceContext
* context
)
42 set_plugin_info_called_(false) {
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_
= new IPC::Channel(handle
, IPC::Channel::MODE_CLIENT
, this);
64 ASSERT_TRUE(channel_
->Connect());
67 virtual void SetPluginInfo(const webkit::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
{
85 virtual void OnChannelError() OVERRIDE
{
89 virtual void OnChannelDenied() OVERRIDE
{
92 virtual void OnChannelListenError() OVERRIDE
{
103 void QuitMessageLoop() {
104 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
105 MessageLoop::QuitClosure());
108 ResourceContext
* context_
;
109 IPC::Channel
* channel_
;
110 bool set_plugin_info_called_
;
111 DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient
);
114 class PluginServiceTest
: public ContentBrowserTest
{
116 PluginServiceTest() {}
118 ResourceContext
* GetResourceContext() {
119 return shell()->web_contents()->GetBrowserContext()->GetResourceContext();
122 virtual void SetUpCommandLine(CommandLine
* command_line
) {
124 FilePath browser_directory
;
125 PathService::Get(base::DIR_MODULE
, &browser_directory
);
126 command_line
->AppendSwitchPath(switches::kExtraPluginDir
,
127 browser_directory
.AppendASCII("plugins"));
129 // TODO(jam): since these plugin tests are running under Chrome, we need to
130 // tell it to disable its security features for old plugins. Once this is
131 // running under content_browsertests, these flags won't be needed.
132 // http://crbug.com/90448
133 // switches::kAlwaysAuthorizePlugins
134 command_line
->AppendSwitch("always-authorize-plugins");
138 // Try to open a channel to the test plugin. Minimal plugin process spawning
139 // test for the PluginService interface.
140 IN_PROC_BROWSER_TEST_F(PluginServiceTest
, OpenChannelToPlugin
) {
141 if (!webkit::npapi::NPAPIPluginsSupported())
143 MockPluginProcessHostClient
mock_client(GetResourceContext());
144 BrowserThread::PostTask(
145 BrowserThread::IO
, FROM_HERE
,
146 base::Bind(&OpenChannel
, &mock_client
));
150 // A strict mock that fails if any of the methods are called. They shouldn't be
151 // called since the request should get canceled before then.
152 class MockCanceledPluginServiceClient
: public PluginProcessHost::Client
{
154 MockCanceledPluginServiceClient(ResourceContext
* context
)
156 get_resource_context_called_(false) {
159 virtual ~MockCanceledPluginServiceClient() {}
161 // Client implementation.
162 MOCK_METHOD0(ID
, int());
163 virtual ResourceContext
* GetResourceContext() OVERRIDE
{
164 get_resource_context_called_
= true;
167 MOCK_METHOD0(OffTheRecord
, bool());
168 MOCK_METHOD1(OnFoundPluginProcessHost
, void(PluginProcessHost
* host
));
169 MOCK_METHOD0(OnSentPluginChannelRequest
, void());
170 MOCK_METHOD1(OnChannelOpened
, void(const IPC::ChannelHandle
& handle
));
171 MOCK_METHOD1(SetPluginInfo
, void(const webkit::WebPluginInfo
& info
));
172 MOCK_METHOD0(OnError
, void());
174 bool get_resource_context_called() const {
175 return get_resource_context_called_
;
179 ResourceContext
* context_
;
180 bool get_resource_context_called_
;
182 DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginServiceClient
);
185 void QuitUIMessageLoopFromIOThread() {
186 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
187 MessageLoop::QuitClosure());
190 void OpenChannelAndThenCancel(PluginProcessHost::Client
* client
) {
192 // Immediately cancel it. This is guaranteed to work since PluginService needs
193 // to consult its filter on the FILE thread.
194 PluginServiceImpl::GetInstance()->CancelOpenChannelToNpapiPlugin(client
);
195 // Before we terminate the test, add a roundtrip through the FILE thread to
196 // make sure that it's had a chance to post back to the IO thread. Then signal
197 // the UI thread to stop and exit the test.
198 BrowserThread::PostTaskAndReply(
199 BrowserThread::FILE, FROM_HERE
,
200 base::Bind(&base::DoNothing
),
201 base::Bind(&QuitUIMessageLoopFromIOThread
));
204 // Should not attempt to open a channel, since it should be canceled early on.
205 IN_PROC_BROWSER_TEST_F(PluginServiceTest
, CancelOpenChannelToPluginService
) {
206 ::testing::StrictMock
<MockCanceledPluginServiceClient
> mock_client(
207 GetResourceContext());
208 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
209 base::Bind(OpenChannelAndThenCancel
, &mock_client
));
211 EXPECT_TRUE(mock_client
.get_resource_context_called());
214 class MockCanceledBeforeSentPluginProcessHostClient
215 : public MockCanceledPluginServiceClient
{
217 MockCanceledBeforeSentPluginProcessHostClient(
218 ResourceContext
* context
)
219 : MockCanceledPluginServiceClient(context
),
220 set_plugin_info_called_(false),
221 on_found_plugin_process_host_called_(false),
224 virtual ~MockCanceledBeforeSentPluginProcessHostClient() {}
226 // Client implementation.
227 virtual void SetPluginInfo(const webkit::WebPluginInfo
& info
) OVERRIDE
{
228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
229 ASSERT_TRUE(info
.mime_types
.size());
230 ASSERT_EQ(kNPAPITestPluginMimeType
, info
.mime_types
[0].mime_type
);
231 set_plugin_info_called_
= true;
233 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{
234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
235 set_on_found_plugin_process_host_called();
237 // This gets called right before we request the plugin<=>renderer channel,
238 // so we have to post a task to cancel it.
239 MessageLoop::current()->PostTask(
241 base::Bind(&PluginProcessHost::CancelPendingRequest
,
242 base::Unretained(host
), this));
243 MessageLoop::current()->PostTask(
245 base::Bind(&QuitUIMessageLoopFromIOThread
));
248 bool set_plugin_info_called() const {
249 return set_plugin_info_called_
;
252 bool on_found_plugin_process_host_called() const {
253 return on_found_plugin_process_host_called_
;
257 void set_on_found_plugin_process_host_called() {
258 on_found_plugin_process_host_called_
= true;
260 void set_host(PluginProcessHost
* host
) {
264 PluginProcessHost
* host() const { return host_
; }
267 bool set_plugin_info_called_
;
268 bool on_found_plugin_process_host_called_
;
269 PluginProcessHost
* host_
;
271 DISALLOW_COPY_AND_ASSIGN(MockCanceledBeforeSentPluginProcessHostClient
);
274 IN_PROC_BROWSER_TEST_F(
275 PluginServiceTest
, CancelBeforeSentOpenChannelToPluginProcessHost
) {
276 if (!webkit::npapi::NPAPIPluginsSupported())
278 ::testing::StrictMock
<MockCanceledBeforeSentPluginProcessHostClient
>
279 mock_client(GetResourceContext());
280 BrowserThread::PostTask(
281 BrowserThread::IO
, FROM_HERE
,
282 base::Bind(&OpenChannel
, &mock_client
));
284 EXPECT_TRUE(mock_client
.get_resource_context_called());
285 EXPECT_TRUE(mock_client
.set_plugin_info_called());
286 EXPECT_TRUE(mock_client
.on_found_plugin_process_host_called());
289 class MockCanceledAfterSentPluginProcessHostClient
290 : public MockCanceledBeforeSentPluginProcessHostClient
{
292 MockCanceledAfterSentPluginProcessHostClient(
293 ResourceContext
* context
)
294 : MockCanceledBeforeSentPluginProcessHostClient(context
),
295 on_sent_plugin_channel_request_called_(false) {}
296 virtual ~MockCanceledAfterSentPluginProcessHostClient() {}
298 // Client implementation.
300 virtual int ID() OVERRIDE
{ return 42; }
301 virtual bool OffTheRecord() OVERRIDE
{ return false; }
303 // We override this guy again since we don't want to cancel yet.
304 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
306 set_on_found_plugin_process_host_called();
310 virtual void OnSentPluginChannelRequest() OVERRIDE
{
311 on_sent_plugin_channel_request_called_
= true;
312 host()->CancelSentRequest(this);
313 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
314 MessageLoop::QuitClosure());
317 bool on_sent_plugin_channel_request_called() const {
318 return on_sent_plugin_channel_request_called_
;
322 bool on_sent_plugin_channel_request_called_
;
324 DISALLOW_COPY_AND_ASSIGN(MockCanceledAfterSentPluginProcessHostClient
);
327 // Should not attempt to open a channel, since it should be canceled early on.
328 IN_PROC_BROWSER_TEST_F(
329 PluginServiceTest
, CancelAfterSentOpenChannelToPluginProcessHost
) {
330 if (!webkit::npapi::NPAPIPluginsSupported())
332 ::testing::StrictMock
<MockCanceledAfterSentPluginProcessHostClient
>
333 mock_client(GetResourceContext());
334 BrowserThread::PostTask(
335 BrowserThread::IO
, FROM_HERE
,
336 base::Bind(&OpenChannel
, &mock_client
));
338 EXPECT_TRUE(mock_client
.get_resource_context_called());
339 EXPECT_TRUE(mock_client
.set_plugin_info_called());
340 EXPECT_TRUE(mock_client
.on_found_plugin_process_host_called());
341 EXPECT_TRUE(mock_client
.on_sent_plugin_channel_request_called());
344 } // namespace content