Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / plugin_service_impl_browsertest.cc
blob102c4926c55e5fd1a499db1f8bd8f9d7041dc7c6
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/test_browser_thread.h"
17 #include "content/public/test/test_utils.h"
18 #include "content/shell/shell.h"
19 #include "content/test/content_browser_test.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "webkit/plugins/npapi/plugin_list.h"
22 #include "webkit/plugins/npapi/plugin_utils.h"
24 namespace content {
26 const char kNPAPITestPluginMimeType[] = "application/vnd.npapi-test";
28 void OpenChannel(PluginProcessHost::Client* client) {
29 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
30 // Start opening the channel
31 PluginServiceImpl::GetInstance()->OpenChannelToNpapiPlugin(
32 0, 0, GURL(), GURL(), kNPAPITestPluginMimeType, client);
35 // Mock up of the Client and the Listener classes that would supply the
36 // communication channel with the plugin.
37 class MockPluginProcessHostClient : public PluginProcessHost::Client,
38 public IPC::Listener {
39 public:
40 MockPluginProcessHostClient(ResourceContext* context, bool expect_fail)
41 : context_(context),
42 channel_(NULL),
43 set_plugin_info_called_(false),
44 expect_fail_(expect_fail) {
47 virtual ~MockPluginProcessHostClient() {
48 if (channel_)
49 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_);
52 // PluginProcessHost::Client implementation.
53 virtual int ID() OVERRIDE { return 42; }
54 virtual bool OffTheRecord() OVERRIDE { return false; }
55 virtual ResourceContext* GetResourceContext() OVERRIDE {
56 return context_;
58 virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {}
59 virtual void OnSentPluginChannelRequest() OVERRIDE {}
61 virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE {
62 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
63 ASSERT_TRUE(set_plugin_info_called_);
64 ASSERT_TRUE(!channel_);
65 channel_ = new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this);
66 ASSERT_TRUE(channel_->Connect());
69 virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE {
70 ASSERT_TRUE(info.mime_types.size());
71 ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type);
72 set_plugin_info_called_ = true;
75 virtual void OnError() OVERRIDE {
76 Fail();
79 // IPC::Listener implementation.
80 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
81 Fail();
82 return false;
84 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
85 if (expect_fail_)
86 FAIL();
87 QuitMessageLoop();
89 virtual void OnChannelError() OVERRIDE {
90 Fail();
92 #if defined(OS_POSIX)
93 virtual void OnChannelDenied() OVERRIDE {
94 Fail();
96 virtual void OnChannelListenError() OVERRIDE {
97 Fail();
99 #endif
101 private:
102 void Fail() {
103 if (!expect_fail_)
104 FAIL();
105 QuitMessageLoop();
108 void QuitMessageLoop() {
109 BrowserThread::PostTask(
110 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
113 ResourceContext* context_;
114 IPC::Channel* channel_;
115 bool set_plugin_info_called_;
116 bool expect_fail_;
117 DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient);
120 class MockPluginServiceFilter : public content::PluginServiceFilter {
121 public:
122 MockPluginServiceFilter() {}
124 virtual bool IsPluginAvailable(
125 int render_process_id,
126 int render_view_id,
127 const void* context,
128 const GURL& url,
129 const GURL& policy_url,
130 webkit::WebPluginInfo* plugin) OVERRIDE { return true; }
132 virtual bool CanLoadPlugin(
133 int render_process_id,
134 const base::FilePath& path) OVERRIDE { return false; }
137 class PluginServiceTest : public ContentBrowserTest {
138 public:
139 PluginServiceTest() {}
141 ResourceContext* GetResourceContext() {
142 return shell()->web_contents()->GetBrowserContext()->GetResourceContext();
145 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
146 #if defined(OS_MACOSX)
147 base::FilePath browser_directory;
148 PathService::Get(base::DIR_MODULE, &browser_directory);
149 command_line->AppendSwitchPath(switches::kExtraPluginDir,
150 browser_directory.AppendASCII("plugins"));
151 #endif
152 // TODO(jam): since these plugin tests are running under Chrome, we need to
153 // tell it to disable its security features for old plugins. Once this is
154 // running under content_browsertests, these flags won't be needed.
155 // http://crbug.com/90448
156 // switches::kAlwaysAuthorizePlugins
157 command_line->AppendSwitch("always-authorize-plugins");
161 // Try to open a channel to the test plugin. Minimal plugin process spawning
162 // test for the PluginService interface.
163 IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToPlugin) {
164 if (!webkit::npapi::NPAPIPluginsSupported())
165 return;
166 MockPluginProcessHostClient mock_client(GetResourceContext(), false);
167 BrowserThread::PostTask(
168 BrowserThread::IO, FROM_HERE,
169 base::Bind(&OpenChannel, &mock_client));
170 RunMessageLoop();
173 IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToDeniedPlugin) {
174 if (!webkit::npapi::NPAPIPluginsSupported())
175 return;
176 MockPluginServiceFilter filter;
177 PluginServiceImpl::GetInstance()->SetFilter(&filter);
178 MockPluginProcessHostClient mock_client(GetResourceContext(), true);
179 BrowserThread::PostTask(
180 BrowserThread::IO, FROM_HERE,
181 base::Bind(&OpenChannel, &mock_client));
182 RunMessageLoop();
185 // A strict mock that fails if any of the methods are called. They shouldn't be
186 // called since the request should get canceled before then.
187 class MockCanceledPluginServiceClient : public PluginProcessHost::Client {
188 public:
189 MockCanceledPluginServiceClient(ResourceContext* context)
190 : context_(context),
191 get_resource_context_called_(false) {
194 virtual ~MockCanceledPluginServiceClient() {}
196 // Client implementation.
197 MOCK_METHOD0(ID, int());
198 virtual ResourceContext* GetResourceContext() OVERRIDE {
199 get_resource_context_called_ = true;
200 return context_;
202 MOCK_METHOD0(OffTheRecord, bool());
203 MOCK_METHOD1(OnFoundPluginProcessHost, void(PluginProcessHost* host));
204 MOCK_METHOD0(OnSentPluginChannelRequest, void());
205 MOCK_METHOD1(OnChannelOpened, void(const IPC::ChannelHandle& handle));
206 MOCK_METHOD1(SetPluginInfo, void(const webkit::WebPluginInfo& info));
207 MOCK_METHOD0(OnError, void());
209 bool get_resource_context_called() const {
210 return get_resource_context_called_;
213 private:
214 ResourceContext* context_;
215 bool get_resource_context_called_;
217 DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginServiceClient);
220 void QuitUIMessageLoopFromIOThread() {
221 BrowserThread::PostTask(
222 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
225 void OpenChannelAndThenCancel(PluginProcessHost::Client* client) {
226 OpenChannel(client);
227 // Immediately cancel it. This is guaranteed to work since PluginService needs
228 // to consult its filter on the FILE thread.
229 PluginServiceImpl::GetInstance()->CancelOpenChannelToNpapiPlugin(client);
230 // Before we terminate the test, add a roundtrip through the FILE thread to
231 // make sure that it's had a chance to post back to the IO thread. Then signal
232 // the UI thread to stop and exit the test.
233 BrowserThread::PostTaskAndReply(
234 BrowserThread::FILE, FROM_HERE,
235 base::Bind(&base::DoNothing),
236 base::Bind(&QuitUIMessageLoopFromIOThread));
239 // Should not attempt to open a channel, since it should be canceled early on.
240 IN_PROC_BROWSER_TEST_F(PluginServiceTest, CancelOpenChannelToPluginService) {
241 ::testing::StrictMock<MockCanceledPluginServiceClient> mock_client(
242 GetResourceContext());
243 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
244 base::Bind(OpenChannelAndThenCancel, &mock_client));
245 RunMessageLoop();
246 EXPECT_TRUE(mock_client.get_resource_context_called());
249 class MockCanceledBeforeSentPluginProcessHostClient
250 : public MockCanceledPluginServiceClient {
251 public:
252 MockCanceledBeforeSentPluginProcessHostClient(
253 ResourceContext* context)
254 : MockCanceledPluginServiceClient(context),
255 set_plugin_info_called_(false),
256 on_found_plugin_process_host_called_(false),
257 host_(NULL) {}
259 virtual ~MockCanceledBeforeSentPluginProcessHostClient() {}
261 // Client implementation.
262 virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
264 ASSERT_TRUE(info.mime_types.size());
265 ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type);
266 set_plugin_info_called_ = true;
268 virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
270 set_on_found_plugin_process_host_called();
271 set_host(host);
272 // This gets called right before we request the plugin<=>renderer channel,
273 // so we have to post a task to cancel it.
274 base::MessageLoop::current()->PostTask(
275 FROM_HERE,
276 base::Bind(&PluginProcessHost::CancelPendingRequest,
277 base::Unretained(host),
278 this));
279 base::MessageLoop::current()->PostTask(
280 FROM_HERE, base::Bind(&QuitUIMessageLoopFromIOThread));
283 bool set_plugin_info_called() const {
284 return set_plugin_info_called_;
287 bool on_found_plugin_process_host_called() const {
288 return on_found_plugin_process_host_called_;
291 protected:
292 void set_on_found_plugin_process_host_called() {
293 on_found_plugin_process_host_called_ = true;
295 void set_host(PluginProcessHost* host) {
296 host_ = host;
299 PluginProcessHost* host() const { return host_; }
301 private:
302 bool set_plugin_info_called_;
303 bool on_found_plugin_process_host_called_;
304 PluginProcessHost* host_;
306 DISALLOW_COPY_AND_ASSIGN(MockCanceledBeforeSentPluginProcessHostClient);
309 IN_PROC_BROWSER_TEST_F(
310 PluginServiceTest, CancelBeforeSentOpenChannelToPluginProcessHost) {
311 if (!webkit::npapi::NPAPIPluginsSupported())
312 return;
313 ::testing::StrictMock<MockCanceledBeforeSentPluginProcessHostClient>
314 mock_client(GetResourceContext());
315 BrowserThread::PostTask(
316 BrowserThread::IO, FROM_HERE,
317 base::Bind(&OpenChannel, &mock_client));
318 RunMessageLoop();
319 EXPECT_TRUE(mock_client.get_resource_context_called());
320 EXPECT_TRUE(mock_client.set_plugin_info_called());
321 EXPECT_TRUE(mock_client.on_found_plugin_process_host_called());
324 class MockCanceledAfterSentPluginProcessHostClient
325 : public MockCanceledBeforeSentPluginProcessHostClient {
326 public:
327 MockCanceledAfterSentPluginProcessHostClient(
328 ResourceContext* context)
329 : MockCanceledBeforeSentPluginProcessHostClient(context),
330 on_sent_plugin_channel_request_called_(false) {}
331 virtual ~MockCanceledAfterSentPluginProcessHostClient() {}
333 // Client implementation.
335 virtual int ID() OVERRIDE { return 42; }
336 virtual bool OffTheRecord() OVERRIDE { return false; }
338 // We override this guy again since we don't want to cancel yet.
339 virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
341 set_on_found_plugin_process_host_called();
342 set_host(host);
345 virtual void OnSentPluginChannelRequest() OVERRIDE {
346 on_sent_plugin_channel_request_called_ = true;
347 host()->CancelSentRequest(this);
348 BrowserThread::PostTask(
349 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
352 bool on_sent_plugin_channel_request_called() const {
353 return on_sent_plugin_channel_request_called_;
356 private:
357 bool on_sent_plugin_channel_request_called_;
359 DISALLOW_COPY_AND_ASSIGN(MockCanceledAfterSentPluginProcessHostClient);
362 // Should not attempt to open a channel, since it should be canceled early on.
363 IN_PROC_BROWSER_TEST_F(
364 PluginServiceTest, CancelAfterSentOpenChannelToPluginProcessHost) {
365 if (!webkit::npapi::NPAPIPluginsSupported())
366 return;
367 ::testing::StrictMock<MockCanceledAfterSentPluginProcessHostClient>
368 mock_client(GetResourceContext());
369 BrowserThread::PostTask(
370 BrowserThread::IO, FROM_HERE,
371 base::Bind(&OpenChannel, &mock_client));
372 RunMessageLoop();
373 EXPECT_TRUE(mock_client.get_resource_context_called());
374 EXPECT_TRUE(mock_client.set_plugin_info_called());
375 EXPECT_TRUE(mock_client.on_found_plugin_process_host_called());
376 EXPECT_TRUE(mock_client.on_sent_plugin_channel_request_called());
379 } // namespace content