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"
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
)
41 set_plugin_info_called_(false) {
44 virtual ~MockPluginProcessHostClient() {
46 BrowserThread::DeleteSoon(BrowserThread::IO
, FROM_HERE
, channel_
);
49 // PluginProcessHost::Client implementation.
50 virtual int ID() OVERRIDE
{ return 42; }
51 virtual bool OffTheRecord() OVERRIDE
{ return false; }
52 virtual ResourceContext
* GetResourceContext() OVERRIDE
{
55 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{}
56 virtual void OnSentPluginChannelRequest() OVERRIDE
{}
58 virtual void OnChannelOpened(const IPC::ChannelHandle
& handle
) OVERRIDE
{
59 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
60 ASSERT_TRUE(set_plugin_info_called_
);
61 ASSERT_TRUE(!channel_
);
62 channel_
= new IPC::Channel(handle
, IPC::Channel::MODE_CLIENT
, this);
63 ASSERT_TRUE(channel_
->Connect());
66 virtual void SetPluginInfo(const webkit::WebPluginInfo
& info
) OVERRIDE
{
67 ASSERT_TRUE(info
.mime_types
.size());
68 ASSERT_EQ(kNPAPITestPluginMimeType
, info
.mime_types
[0].mime_type
);
69 set_plugin_info_called_
= true;
72 virtual void OnError() OVERRIDE
{
76 // IPC::Listener implementation.
77 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
{
81 virtual void OnChannelConnected(int32 peer_pid
) OVERRIDE
{
84 virtual void OnChannelError() OVERRIDE
{
88 virtual void OnChannelDenied() OVERRIDE
{
91 virtual void OnChannelListenError() OVERRIDE
{
102 void QuitMessageLoop() {
103 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
104 MessageLoop::QuitClosure());
107 ResourceContext
* context_
;
108 IPC::Channel
* channel_
;
109 bool set_plugin_info_called_
;
110 DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient
);
113 class PluginServiceTest
: public ContentBrowserTest
{
115 PluginServiceTest() {}
117 ResourceContext
* GetResourceContext() {
118 return shell()->web_contents()->GetBrowserContext()->GetResourceContext();
121 virtual void SetUpCommandLine(CommandLine
* command_line
) {
123 FilePath browser_directory
;
124 PathService::Get(base::DIR_MODULE
, &browser_directory
);
125 command_line
->AppendSwitchPath(switches::kExtraPluginDir
,
126 browser_directory
.AppendASCII("plugins"));
128 // TODO(jam): since these plugin tests are running under Chrome, we need to
129 // tell it to disable its security features for old plugins. Once this is
130 // running under content_browsertests, these flags won't be needed.
131 // http://crbug.com/90448
132 // switches::kAlwaysAuthorizePlugins
133 command_line
->AppendSwitch("always-authorize-plugins");
137 // Try to open a channel to the test plugin. Minimal plugin process spawning
138 // test for the PluginService interface.
139 IN_PROC_BROWSER_TEST_F(PluginServiceTest
, OpenChannelToPlugin
) {
140 MockPluginProcessHostClient
mock_client(GetResourceContext());
141 BrowserThread::PostTask(
142 BrowserThread::IO
, FROM_HERE
,
143 base::Bind(&OpenChannel
, &mock_client
));
147 // A strict mock that fails if any of the methods are called. They shouldn't be
148 // called since the request should get canceled before then.
149 class MockCanceledPluginServiceClient
: public PluginProcessHost::Client
{
151 MockCanceledPluginServiceClient(ResourceContext
* context
)
153 get_resource_context_called_(false) {
156 virtual ~MockCanceledPluginServiceClient() {}
158 // Client implementation.
159 MOCK_METHOD0(ID
, int());
160 virtual ResourceContext
* GetResourceContext() OVERRIDE
{
161 get_resource_context_called_
= true;
164 MOCK_METHOD0(OffTheRecord
, bool());
165 MOCK_METHOD1(OnFoundPluginProcessHost
, void(PluginProcessHost
* host
));
166 MOCK_METHOD0(OnSentPluginChannelRequest
, void());
167 MOCK_METHOD1(OnChannelOpened
, void(const IPC::ChannelHandle
& handle
));
168 MOCK_METHOD1(SetPluginInfo
, void(const webkit::WebPluginInfo
& info
));
169 MOCK_METHOD0(OnError
, void());
171 bool get_resource_context_called() const {
172 return get_resource_context_called_
;
176 ResourceContext
* context_
;
177 bool get_resource_context_called_
;
179 DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginServiceClient
);
182 void QuitUIMessageLoopFromIOThread() {
183 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
184 MessageLoop::QuitClosure());
187 void OpenChannelAndThenCancel(PluginProcessHost::Client
* client
) {
189 // Immediately cancel it. This is guaranteed to work since PluginService needs
190 // to consult its filter on the FILE thread.
191 PluginServiceImpl::GetInstance()->CancelOpenChannelToNpapiPlugin(client
);
192 // Before we terminate the test, add a roundtrip through the FILE thread to
193 // make sure that it's had a chance to post back to the IO thread. Then signal
194 // the UI thread to stop and exit the test.
195 BrowserThread::PostTaskAndReply(
196 BrowserThread::FILE, FROM_HERE
,
197 base::Bind(&base::DoNothing
),
198 base::Bind(&QuitUIMessageLoopFromIOThread
));
201 // Should not attempt to open a channel, since it should be canceled early on.
202 IN_PROC_BROWSER_TEST_F(PluginServiceTest
, CancelOpenChannelToPluginService
) {
203 ::testing::StrictMock
<MockCanceledPluginServiceClient
> mock_client(
204 GetResourceContext());
205 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
206 base::Bind(OpenChannelAndThenCancel
, &mock_client
));
208 EXPECT_TRUE(mock_client
.get_resource_context_called());
211 class MockCanceledBeforeSentPluginProcessHostClient
212 : public MockCanceledPluginServiceClient
{
214 MockCanceledBeforeSentPluginProcessHostClient(
215 ResourceContext
* context
)
216 : MockCanceledPluginServiceClient(context
),
217 set_plugin_info_called_(false),
218 on_found_plugin_process_host_called_(false),
221 virtual ~MockCanceledBeforeSentPluginProcessHostClient() {}
223 // Client implementation.
224 virtual void SetPluginInfo(const webkit::WebPluginInfo
& info
) OVERRIDE
{
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
226 ASSERT_TRUE(info
.mime_types
.size());
227 ASSERT_EQ(kNPAPITestPluginMimeType
, info
.mime_types
[0].mime_type
);
228 set_plugin_info_called_
= true;
230 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{
231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
232 set_on_found_plugin_process_host_called();
234 // This gets called right before we request the plugin<=>renderer channel,
235 // so we have to post a task to cancel it.
236 MessageLoop::current()->PostTask(
238 base::Bind(&PluginProcessHost::CancelPendingRequest
,
239 base::Unretained(host
), this));
240 MessageLoop::current()->PostTask(
242 base::Bind(&QuitUIMessageLoopFromIOThread
));
245 bool set_plugin_info_called() const {
246 return set_plugin_info_called_
;
249 bool on_found_plugin_process_host_called() const {
250 return on_found_plugin_process_host_called_
;
254 void set_on_found_plugin_process_host_called() {
255 on_found_plugin_process_host_called_
= true;
257 void set_host(PluginProcessHost
* host
) {
261 PluginProcessHost
* host() const { return host_
; }
264 bool set_plugin_info_called_
;
265 bool on_found_plugin_process_host_called_
;
266 PluginProcessHost
* host_
;
268 DISALLOW_COPY_AND_ASSIGN(MockCanceledBeforeSentPluginProcessHostClient
);
271 IN_PROC_BROWSER_TEST_F(
272 PluginServiceTest
, CancelBeforeSentOpenChannelToPluginProcessHost
) {
273 ::testing::StrictMock
<MockCanceledBeforeSentPluginProcessHostClient
>
274 mock_client(GetResourceContext());
275 BrowserThread::PostTask(
276 BrowserThread::IO
, FROM_HERE
,
277 base::Bind(&OpenChannel
, &mock_client
));
279 EXPECT_TRUE(mock_client
.get_resource_context_called());
280 EXPECT_TRUE(mock_client
.set_plugin_info_called());
281 EXPECT_TRUE(mock_client
.on_found_plugin_process_host_called());
284 class MockCanceledAfterSentPluginProcessHostClient
285 : public MockCanceledBeforeSentPluginProcessHostClient
{
287 MockCanceledAfterSentPluginProcessHostClient(
288 ResourceContext
* context
)
289 : MockCanceledBeforeSentPluginProcessHostClient(context
),
290 on_sent_plugin_channel_request_called_(false) {}
291 virtual ~MockCanceledAfterSentPluginProcessHostClient() {}
293 // Client implementation.
295 virtual int ID() OVERRIDE
{ return 42; }
296 virtual bool OffTheRecord() OVERRIDE
{ return false; }
298 // We override this guy again since we don't want to cancel yet.
299 virtual void OnFoundPluginProcessHost(PluginProcessHost
* host
) OVERRIDE
{
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
301 set_on_found_plugin_process_host_called();
305 virtual void OnSentPluginChannelRequest() OVERRIDE
{
306 on_sent_plugin_channel_request_called_
= true;
307 host()->CancelSentRequest(this);
308 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
309 MessageLoop::QuitClosure());
312 bool on_sent_plugin_channel_request_called() const {
313 return on_sent_plugin_channel_request_called_
;
317 bool on_sent_plugin_channel_request_called_
;
319 DISALLOW_COPY_AND_ASSIGN(MockCanceledAfterSentPluginProcessHostClient
);
322 // Should not attempt to open a channel, since it should be canceled early on.
323 IN_PROC_BROWSER_TEST_F(
324 PluginServiceTest
, CancelAfterSentOpenChannelToPluginProcessHost
) {
325 ::testing::StrictMock
<MockCanceledAfterSentPluginProcessHostClient
>
326 mock_client(GetResourceContext());
327 BrowserThread::PostTask(
328 BrowserThread::IO
, FROM_HERE
,
329 base::Bind(&OpenChannel
, &mock_client
));
331 EXPECT_TRUE(mock_client
.get_resource_context_called());
332 EXPECT_TRUE(mock_client
.set_plugin_info_called());
333 EXPECT_TRUE(mock_client
.on_found_plugin_process_host_called());
334 EXPECT_TRUE(mock_client
.on_sent_plugin_channel_request_called());
337 } // namespace content