OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/plugin_service_impl.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/command_line.h" | |
10 #include "base/location.h" | |
11 #include "base/path_service.h" | |
12 #include "base/single_thread_task_runner.h" | |
13 #include "base/thread_task_runner_handle.h" | |
14 #include "content/public/browser/browser_context.h" | |
15 #include "content/public/browser/plugin_service_filter.h" | |
16 #include "content/public/browser/resource_context.h" | |
17 #include "content/public/browser/web_contents.h" | |
18 #include "content/public/common/content_switches.h" | |
19 #include "content/public/test/content_browser_test.h" | |
20 #include "content/public/test/test_browser_thread.h" | |
21 #include "content/public/test/test_utils.h" | |
22 #include "content/shell/browser/shell.h" | |
23 #include "testing/gmock/include/gmock/gmock.h" | |
24 | |
25 namespace content { | |
26 | |
27 const char kNPAPITestPluginMimeType[] = "application/vnd.npapi-test"; | |
28 | |
29 void OpenChannel(PluginProcessHost::Client* client) { | |
30 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
31 // Start opening the channel | |
32 PluginServiceImpl::GetInstance()->OpenChannelToNpapiPlugin( | |
33 0, 0, GURL(), GURL(), kNPAPITestPluginMimeType, client); | |
34 } | |
35 | |
36 // Mock up of the Client and the Listener classes that would supply the | |
37 // communication channel with the plugin. | |
38 class MockPluginProcessHostClient : public PluginProcessHost::Client, | |
39 public IPC::Listener { | |
40 public: | |
41 MockPluginProcessHostClient(ResourceContext* context, bool expect_fail) | |
42 : context_(context), | |
43 channel_(NULL), | |
44 set_plugin_info_called_(false), | |
45 expect_fail_(expect_fail) { | |
46 } | |
47 | |
48 ~MockPluginProcessHostClient() override { | |
49 if (channel_) | |
50 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_); | |
51 } | |
52 | |
53 // PluginProcessHost::Client implementation. | |
54 int ID() override { return 42; } | |
55 bool OffTheRecord() override { return false; } | |
56 ResourceContext* GetResourceContext() override { return context_; } | |
57 void OnFoundPluginProcessHost(PluginProcessHost* host) override {} | |
58 void OnSentPluginChannelRequest() override {} | |
59 | |
60 void OnChannelOpened(const IPC::ChannelHandle& handle) override { | |
61 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
62 ASSERT_TRUE(set_plugin_info_called_); | |
63 ASSERT_TRUE(!channel_); | |
64 channel_ = IPC::Channel::CreateClient(handle, this).release(); | |
65 ASSERT_TRUE(channel_->Connect()); | |
66 } | |
67 | |
68 void SetPluginInfo(const WebPluginInfo& info) override { | |
69 ASSERT_TRUE(info.mime_types.size()); | |
70 ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type); | |
71 set_plugin_info_called_ = true; | |
72 } | |
73 | |
74 void OnError() override { Fail(); } | |
75 | |
76 // IPC::Listener implementation. | |
77 bool OnMessageReceived(const IPC::Message& message) override { | |
78 Fail(); | |
79 return false; | |
80 } | |
81 void OnChannelConnected(int32 peer_pid) override { | |
82 if (expect_fail_) | |
83 FAIL(); | |
84 QuitMessageLoop(); | |
85 } | |
86 void OnChannelError() override { Fail(); } | |
87 #if defined(OS_POSIX) | |
88 void OnChannelDenied() override { Fail(); } | |
89 void OnChannelListenError() override { Fail(); } | |
90 #endif | |
91 | |
92 private: | |
93 void Fail() { | |
94 if (!expect_fail_) | |
95 FAIL(); | |
96 QuitMessageLoop(); | |
97 } | |
98 | |
99 void QuitMessageLoop() { | |
100 BrowserThread::PostTask( | |
101 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure()); | |
102 } | |
103 | |
104 ResourceContext* context_; | |
105 IPC::Channel* channel_; | |
106 bool set_plugin_info_called_; | |
107 bool expect_fail_; | |
108 DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient); | |
109 }; | |
110 | |
111 class MockPluginServiceFilter : public content::PluginServiceFilter { | |
112 public: | |
113 MockPluginServiceFilter() {} | |
114 | |
115 bool IsPluginAvailable(int render_process_id, | |
116 int render_view_id, | |
117 const void* context, | |
118 const GURL& url, | |
119 const GURL& policy_url, | |
120 WebPluginInfo* plugin) override { | |
121 return true; | |
122 } | |
123 | |
124 bool CanLoadPlugin(int render_process_id, | |
125 const base::FilePath& path) override { | |
126 return false; | |
127 } | |
128 }; | |
129 | |
130 class PluginServiceTest : public ContentBrowserTest { | |
131 public: | |
132 PluginServiceTest() {} | |
133 | |
134 ResourceContext* GetResourceContext() { | |
135 return shell()->web_contents()->GetBrowserContext()->GetResourceContext(); | |
136 } | |
137 | |
138 void SetUpCommandLine(base::CommandLine* command_line) override { | |
139 #if defined(OS_MACOSX) | |
140 base::FilePath browser_directory; | |
141 PathService::Get(base::DIR_MODULE, &browser_directory); | |
142 command_line->AppendSwitchPath(switches::kExtraPluginDir, | |
143 browser_directory.AppendASCII("plugins")); | |
144 #endif | |
145 // TODO(jam): since these plugin tests are running under Chrome, we need to | |
146 // tell it to disable its security features for old plugins. Once this is | |
147 // running under content_browsertests, these flags won't be needed. | |
148 // http://crbug.com/90448 | |
149 // switches::kAlwaysAuthorizePlugins | |
150 command_line->AppendSwitch("always-authorize-plugins"); | |
151 } | |
152 }; | |
153 | |
154 // Try to open a channel to the test plugin. Minimal plugin process spawning | |
155 // test for the PluginService interface. | |
156 IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToPlugin) { | |
157 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported()) | |
158 return; | |
159 MockPluginProcessHostClient mock_client(GetResourceContext(), false); | |
160 BrowserThread::PostTask( | |
161 BrowserThread::IO, FROM_HERE, | |
162 base::Bind(&OpenChannel, &mock_client)); | |
163 RunMessageLoop(); | |
164 } | |
165 | |
166 IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToDeniedPlugin) { | |
167 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported()) | |
168 return; | |
169 MockPluginServiceFilter filter; | |
170 PluginServiceImpl::GetInstance()->SetFilter(&filter); | |
171 MockPluginProcessHostClient mock_client(GetResourceContext(), true); | |
172 BrowserThread::PostTask( | |
173 BrowserThread::IO, FROM_HERE, | |
174 base::Bind(&OpenChannel, &mock_client)); | |
175 RunMessageLoop(); | |
176 } | |
177 | |
178 // A strict mock that fails if any of the methods are called. They shouldn't be | |
179 // called since the request should get canceled before then. | |
180 class MockCanceledPluginServiceClient : public PluginProcessHost::Client { | |
181 public: | |
182 MockCanceledPluginServiceClient(ResourceContext* context) | |
183 : context_(context), | |
184 get_resource_context_called_(false) { | |
185 } | |
186 | |
187 ~MockCanceledPluginServiceClient() override {} | |
188 | |
189 // Client implementation. | |
190 MOCK_METHOD0(ID, int()); | |
191 ResourceContext* GetResourceContext() override { | |
192 get_resource_context_called_ = true; | |
193 return context_; | |
194 } | |
195 MOCK_METHOD0(OffTheRecord, bool()); | |
196 MOCK_METHOD1(OnFoundPluginProcessHost, void(PluginProcessHost* host)); | |
197 MOCK_METHOD0(OnSentPluginChannelRequest, void()); | |
198 MOCK_METHOD1(OnChannelOpened, void(const IPC::ChannelHandle& handle)); | |
199 MOCK_METHOD1(SetPluginInfo, void(const WebPluginInfo& info)); | |
200 MOCK_METHOD0(OnError, void()); | |
201 | |
202 bool get_resource_context_called() const { | |
203 return get_resource_context_called_; | |
204 } | |
205 | |
206 private: | |
207 ResourceContext* context_; | |
208 bool get_resource_context_called_; | |
209 | |
210 DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginServiceClient); | |
211 }; | |
212 | |
213 void QuitUIMessageLoopFromIOThread() { | |
214 BrowserThread::PostTask( | |
215 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure()); | |
216 } | |
217 | |
218 void OpenChannelAndThenCancel(PluginProcessHost::Client* client) { | |
219 OpenChannel(client); | |
220 // Immediately cancel it. This is guaranteed to work since PluginService needs | |
221 // to consult its filter on the FILE thread. | |
222 PluginServiceImpl::GetInstance()->CancelOpenChannelToNpapiPlugin(client); | |
223 // Before we terminate the test, add a roundtrip through the FILE thread to | |
224 // make sure that it's had a chance to post back to the IO thread. Then signal | |
225 // the UI thread to stop and exit the test. | |
226 BrowserThread::PostTaskAndReply( | |
227 BrowserThread::FILE, FROM_HERE, | |
228 base::Bind(&base::DoNothing), | |
229 base::Bind(&QuitUIMessageLoopFromIOThread)); | |
230 } | |
231 | |
232 // Should not attempt to open a channel, since it should be canceled early on. | |
233 IN_PROC_BROWSER_TEST_F(PluginServiceTest, CancelOpenChannelToPluginService) { | |
234 ::testing::StrictMock<MockCanceledPluginServiceClient> mock_client( | |
235 GetResourceContext()); | |
236 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
237 base::Bind(OpenChannelAndThenCancel, &mock_client)); | |
238 RunMessageLoop(); | |
239 EXPECT_TRUE(mock_client.get_resource_context_called()); | |
240 } | |
241 | |
242 class MockCanceledBeforeSentPluginProcessHostClient | |
243 : public MockCanceledPluginServiceClient { | |
244 public: | |
245 MockCanceledBeforeSentPluginProcessHostClient( | |
246 ResourceContext* context) | |
247 : MockCanceledPluginServiceClient(context), | |
248 set_plugin_info_called_(false), | |
249 on_found_plugin_process_host_called_(false), | |
250 host_(NULL) {} | |
251 | |
252 ~MockCanceledBeforeSentPluginProcessHostClient() override {} | |
253 | |
254 // Client implementation. | |
255 void SetPluginInfo(const WebPluginInfo& info) override { | |
256 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
257 ASSERT_TRUE(info.mime_types.size()); | |
258 ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type); | |
259 set_plugin_info_called_ = true; | |
260 } | |
261 void OnFoundPluginProcessHost(PluginProcessHost* host) override { | |
262 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
263 set_on_found_plugin_process_host_called(); | |
264 set_host(host); | |
265 // This gets called right before we request the plugin<=>renderer channel, | |
266 // so we have to post a task to cancel it. | |
267 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
268 FROM_HERE, base::Bind(&PluginProcessHost::CancelPendingRequest, | |
269 base::Unretained(host), this)); | |
270 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
271 FROM_HERE, base::Bind(&QuitUIMessageLoopFromIOThread)); | |
272 } | |
273 | |
274 bool set_plugin_info_called() const { | |
275 return set_plugin_info_called_; | |
276 } | |
277 | |
278 bool on_found_plugin_process_host_called() const { | |
279 return on_found_plugin_process_host_called_; | |
280 } | |
281 | |
282 protected: | |
283 void set_on_found_plugin_process_host_called() { | |
284 on_found_plugin_process_host_called_ = true; | |
285 } | |
286 void set_host(PluginProcessHost* host) { | |
287 host_ = host; | |
288 } | |
289 | |
290 PluginProcessHost* host() const { return host_; } | |
291 | |
292 private: | |
293 bool set_plugin_info_called_; | |
294 bool on_found_plugin_process_host_called_; | |
295 PluginProcessHost* host_; | |
296 | |
297 DISALLOW_COPY_AND_ASSIGN(MockCanceledBeforeSentPluginProcessHostClient); | |
298 }; | |
299 | |
300 IN_PROC_BROWSER_TEST_F( | |
301 PluginServiceTest, CancelBeforeSentOpenChannelToPluginProcessHost) { | |
302 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported()) | |
303 return; | |
304 ::testing::StrictMock<MockCanceledBeforeSentPluginProcessHostClient> | |
305 mock_client(GetResourceContext()); | |
306 BrowserThread::PostTask( | |
307 BrowserThread::IO, FROM_HERE, | |
308 base::Bind(&OpenChannel, &mock_client)); | |
309 RunMessageLoop(); | |
310 EXPECT_TRUE(mock_client.get_resource_context_called()); | |
311 EXPECT_TRUE(mock_client.set_plugin_info_called()); | |
312 EXPECT_TRUE(mock_client.on_found_plugin_process_host_called()); | |
313 } | |
314 | |
315 class MockCanceledAfterSentPluginProcessHostClient | |
316 : public MockCanceledBeforeSentPluginProcessHostClient { | |
317 public: | |
318 MockCanceledAfterSentPluginProcessHostClient( | |
319 ResourceContext* context) | |
320 : MockCanceledBeforeSentPluginProcessHostClient(context), | |
321 on_sent_plugin_channel_request_called_(false) {} | |
322 ~MockCanceledAfterSentPluginProcessHostClient() override {} | |
323 | |
324 // Client implementation. | |
325 | |
326 int ID() override { return 42; } | |
327 bool OffTheRecord() override { return false; } | |
328 | |
329 // We override this guy again since we don't want to cancel yet. | |
330 void OnFoundPluginProcessHost(PluginProcessHost* host) override { | |
331 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
332 set_on_found_plugin_process_host_called(); | |
333 set_host(host); | |
334 } | |
335 | |
336 void OnSentPluginChannelRequest() override { | |
337 on_sent_plugin_channel_request_called_ = true; | |
338 host()->CancelSentRequest(this); | |
339 BrowserThread::PostTask( | |
340 BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure()); | |
341 } | |
342 | |
343 bool on_sent_plugin_channel_request_called() const { | |
344 return on_sent_plugin_channel_request_called_; | |
345 } | |
346 | |
347 private: | |
348 bool on_sent_plugin_channel_request_called_; | |
349 | |
350 DISALLOW_COPY_AND_ASSIGN(MockCanceledAfterSentPluginProcessHostClient); | |
351 }; | |
352 | |
353 // Should not attempt to open a channel, since it should be canceled early on. | |
354 IN_PROC_BROWSER_TEST_F( | |
355 PluginServiceTest, CancelAfterSentOpenChannelToPluginProcessHost) { | |
356 if (!PluginServiceImpl::GetInstance()->NPAPIPluginsSupported()) | |
357 return; | |
358 ::testing::StrictMock<MockCanceledAfterSentPluginProcessHostClient> | |
359 mock_client(GetResourceContext()); | |
360 BrowserThread::PostTask( | |
361 BrowserThread::IO, FROM_HERE, | |
362 base::Bind(&OpenChannel, &mock_client)); | |
363 RunMessageLoop(); | |
364 EXPECT_TRUE(mock_client.get_resource_context_called()); | |
365 EXPECT_TRUE(mock_client.set_plugin_info_called()); | |
366 EXPECT_TRUE(mock_client.on_found_plugin_process_host_called()); | |
367 EXPECT_TRUE(mock_client.on_sent_plugin_channel_request_called()); | |
368 } | |
369 | |
370 } // namespace content | |
OLD | NEW |