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_loader_posix.h"
6
7#include "base/at_exit.h"
8#include "base/bind.h"
9#include "base/files/file_path.h"
10#include "base/memory/ref_counted.h"
11#include "base/message_loop/message_loop.h"
12#include "base/strings/utf_string_conversions.h"
13#include "content/browser/browser_thread_impl.h"
14#include "content/common/plugin_list.h"
15#include "testing/gmock/include/gmock/gmock.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18using base::ASCIIToUTF16;
19
20namespace content {
21
22class MockPluginLoaderPosix : public PluginLoaderPosix {
23 public:
24  MOCK_METHOD0(LoadPluginsInternal, void(void));
25
26  size_t number_of_pending_callbacks() {
27    return callbacks_.size();
28  }
29
30  std::vector<base::FilePath>* canonical_list() {
31    return &canonical_list_;
32  }
33
34  size_t next_load_index() {
35    return next_load_index_;
36  }
37
38  const std::vector<WebPluginInfo>& loaded_plugins() {
39    return loaded_plugins_;
40  }
41
42  std::vector<WebPluginInfo>* internal_plugins() {
43    return &internal_plugins_;
44  }
45
46  void RealLoadPluginsInternal() {
47    PluginLoaderPosix::LoadPluginsInternal();
48  }
49
50  void TestOnPluginLoaded(uint32 index, const WebPluginInfo& plugin) {
51    OnPluginLoaded(index, plugin);
52  }
53
54  void TestOnPluginLoadFailed(uint32 index, const base::FilePath& path) {
55    OnPluginLoadFailed(index, path);
56  }
57
58 protected:
59  virtual ~MockPluginLoaderPosix() {}
60};
61
62void VerifyCallback(int* run_count, const std::vector<WebPluginInfo>&) {
63  ++(*run_count);
64}
65
66class PluginLoaderPosixTest : public testing::Test {
67 public:
68  PluginLoaderPosixTest()
69      : plugin1_(ASCIIToUTF16("plugin1"), base::FilePath("/tmp/one.plugin"),
70                 ASCIIToUTF16("1.0"), base::string16()),
71        plugin2_(ASCIIToUTF16("plugin2"), base::FilePath("/tmp/two.plugin"),
72                 ASCIIToUTF16("2.0"), base::string16()),
73        plugin3_(ASCIIToUTF16("plugin3"), base::FilePath("/tmp/three.plugin"),
74                 ASCIIToUTF16("3.0"), base::string16()),
75        file_thread_(BrowserThread::FILE, &message_loop_),
76        io_thread_(BrowserThread::IO, &message_loop_),
77        plugin_loader_(new MockPluginLoaderPosix) {
78  }
79
80  virtual void SetUp() OVERRIDE {
81    PluginServiceImpl::GetInstance()->Init();
82  }
83
84  base::MessageLoop* message_loop() { return &message_loop_; }
85  MockPluginLoaderPosix* plugin_loader() { return plugin_loader_.get(); }
86
87  void AddThreePlugins() {
88    plugin_loader_->canonical_list()->clear();
89    plugin_loader_->canonical_list()->push_back(plugin1_.path);
90    plugin_loader_->canonical_list()->push_back(plugin2_.path);
91    plugin_loader_->canonical_list()->push_back(plugin3_.path);
92  }
93
94  // Data used for testing.
95  WebPluginInfo plugin1_;
96  WebPluginInfo plugin2_;
97  WebPluginInfo plugin3_;
98
99 private:
100  // Destroys PluginService and PluginList.
101  base::ShadowingAtExitManager at_exit_manager_;
102
103  base::MessageLoopForIO message_loop_;
104  BrowserThreadImpl file_thread_;
105  BrowserThreadImpl io_thread_;
106
107  scoped_refptr<MockPluginLoaderPosix> plugin_loader_;
108};
109
110TEST_F(PluginLoaderPosixTest, QueueRequests) {
111  int did_callback = 0;
112  PluginService::GetPluginsCallback callback =
113      base::Bind(&VerifyCallback, base::Unretained(&did_callback));
114
115  plugin_loader()->GetPlugins(callback);
116  plugin_loader()->GetPlugins(callback);
117
118  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
119  message_loop()->RunUntilIdle();
120
121  EXPECT_EQ(0, did_callback);
122
123  plugin_loader()->canonical_list()->clear();
124  plugin_loader()->canonical_list()->push_back(plugin1_.path);
125  plugin_loader()->TestOnPluginLoaded(0, plugin1_);
126  message_loop()->RunUntilIdle();
127
128  EXPECT_EQ(2, did_callback);
129}
130
131TEST_F(PluginLoaderPosixTest, QueueRequestsAndInvalidate) {
132  int did_callback = 0;
133  PluginService::GetPluginsCallback callback =
134      base::Bind(&VerifyCallback, base::Unretained(&did_callback));
135
136  plugin_loader()->GetPlugins(callback);
137
138  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
139  message_loop()->RunUntilIdle();
140
141  EXPECT_EQ(0, did_callback);
142  ::testing::Mock::VerifyAndClearExpectations(plugin_loader());
143
144  // Invalidate the plugin list, then queue up another request.
145  PluginList::Singleton()->RefreshPlugins();
146  plugin_loader()->GetPlugins(callback);
147
148  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
149
150  plugin_loader()->canonical_list()->clear();
151  plugin_loader()->canonical_list()->push_back(plugin1_.path);
152  plugin_loader()->TestOnPluginLoaded(0, plugin1_);
153  message_loop()->RunUntilIdle();
154
155  // Only the first request should have been fulfilled.
156  EXPECT_EQ(1, did_callback);
157
158  plugin_loader()->canonical_list()->clear();
159  plugin_loader()->canonical_list()->push_back(plugin1_.path);
160  plugin_loader()->TestOnPluginLoaded(0, plugin1_);
161  message_loop()->RunUntilIdle();
162
163  EXPECT_EQ(2, did_callback);
164}
165
166TEST_F(PluginLoaderPosixTest, ThreeSuccessfulLoads) {
167  int did_callback = 0;
168  PluginService::GetPluginsCallback callback =
169      base::Bind(&VerifyCallback, base::Unretained(&did_callback));
170
171  plugin_loader()->GetPlugins(callback);
172
173  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
174  message_loop()->RunUntilIdle();
175
176  AddThreePlugins();
177
178  EXPECT_EQ(0u, plugin_loader()->next_load_index());
179
180  const std::vector<WebPluginInfo>& plugins(plugin_loader()->loaded_plugins());
181
182  plugin_loader()->TestOnPluginLoaded(0, plugin1_);
183  EXPECT_EQ(1u, plugin_loader()->next_load_index());
184  EXPECT_EQ(1u, plugins.size());
185  EXPECT_EQ(plugin1_.name, plugins[0].name);
186
187  message_loop()->RunUntilIdle();
188  EXPECT_EQ(0, did_callback);
189
190  plugin_loader()->TestOnPluginLoaded(1, plugin2_);
191  EXPECT_EQ(2u, plugin_loader()->next_load_index());
192  EXPECT_EQ(2u, plugins.size());
193  EXPECT_EQ(plugin2_.name, plugins[1].name);
194
195  message_loop()->RunUntilIdle();
196  EXPECT_EQ(0, did_callback);
197
198  plugin_loader()->TestOnPluginLoaded(2, plugin3_);
199  EXPECT_EQ(3u, plugins.size());
200  EXPECT_EQ(plugin3_.name, plugins[2].name);
201
202  message_loop()->RunUntilIdle();
203  EXPECT_EQ(1, did_callback);
204}
205
206TEST_F(PluginLoaderPosixTest, ThreeSuccessfulLoadsThenCrash) {
207  int did_callback = 0;
208  PluginService::GetPluginsCallback callback =
209      base::Bind(&VerifyCallback, base::Unretained(&did_callback));
210
211  plugin_loader()->GetPlugins(callback);
212
213  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(2);
214  message_loop()->RunUntilIdle();
215
216  AddThreePlugins();
217
218  EXPECT_EQ(0u, plugin_loader()->next_load_index());
219
220  const std::vector<WebPluginInfo>& plugins(plugin_loader()->loaded_plugins());
221
222  plugin_loader()->TestOnPluginLoaded(0, plugin1_);
223  EXPECT_EQ(1u, plugin_loader()->next_load_index());
224  EXPECT_EQ(1u, plugins.size());
225  EXPECT_EQ(plugin1_.name, plugins[0].name);
226
227  message_loop()->RunUntilIdle();
228  EXPECT_EQ(0, did_callback);
229
230  plugin_loader()->TestOnPluginLoaded(1, plugin2_);
231  EXPECT_EQ(2u, plugin_loader()->next_load_index());
232  EXPECT_EQ(2u, plugins.size());
233  EXPECT_EQ(plugin2_.name, plugins[1].name);
234
235  message_loop()->RunUntilIdle();
236  EXPECT_EQ(0, did_callback);
237
238  plugin_loader()->TestOnPluginLoaded(2, plugin3_);
239  EXPECT_EQ(3u, plugins.size());
240  EXPECT_EQ(plugin3_.name, plugins[2].name);
241
242  message_loop()->RunUntilIdle();
243  EXPECT_EQ(1, did_callback);
244
245  plugin_loader()->OnProcessCrashed(42);
246}
247
248TEST_F(PluginLoaderPosixTest, TwoFailures) {
249  int did_callback = 0;
250  PluginService::GetPluginsCallback callback =
251      base::Bind(&VerifyCallback, base::Unretained(&did_callback));
252
253  plugin_loader()->GetPlugins(callback);
254
255  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
256  message_loop()->RunUntilIdle();
257
258  AddThreePlugins();
259
260  EXPECT_EQ(0u, plugin_loader()->next_load_index());
261
262  const std::vector<WebPluginInfo>& plugins(plugin_loader()->loaded_plugins());
263
264  plugin_loader()->TestOnPluginLoadFailed(0, plugin1_.path);
265  EXPECT_EQ(1u, plugin_loader()->next_load_index());
266  EXPECT_EQ(0u, plugins.size());
267
268  message_loop()->RunUntilIdle();
269  EXPECT_EQ(0, did_callback);
270
271  plugin_loader()->TestOnPluginLoaded(1, plugin2_);
272  EXPECT_EQ(2u, plugin_loader()->next_load_index());
273  EXPECT_EQ(1u, plugins.size());
274  EXPECT_EQ(plugin2_.name, plugins[0].name);
275
276  message_loop()->RunUntilIdle();
277  EXPECT_EQ(0, did_callback);
278
279  plugin_loader()->TestOnPluginLoadFailed(2, plugin3_.path);
280  EXPECT_EQ(1u, plugins.size());
281
282  message_loop()->RunUntilIdle();
283  EXPECT_EQ(1, did_callback);
284}
285
286TEST_F(PluginLoaderPosixTest, CrashedProcess) {
287  int did_callback = 0;
288  PluginService::GetPluginsCallback callback =
289      base::Bind(&VerifyCallback, base::Unretained(&did_callback));
290
291  plugin_loader()->GetPlugins(callback);
292
293  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
294  message_loop()->RunUntilIdle();
295
296  AddThreePlugins();
297
298  EXPECT_EQ(0u, plugin_loader()->next_load_index());
299
300  const std::vector<WebPluginInfo>& plugins(plugin_loader()->loaded_plugins());
301
302  plugin_loader()->TestOnPluginLoaded(0, plugin1_);
303  EXPECT_EQ(1u, plugin_loader()->next_load_index());
304  EXPECT_EQ(1u, plugins.size());
305  EXPECT_EQ(plugin1_.name, plugins[0].name);
306
307  message_loop()->RunUntilIdle();
308  EXPECT_EQ(0, did_callback);
309
310  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
311  plugin_loader()->OnProcessCrashed(42);
312  EXPECT_EQ(1u, plugin_loader()->canonical_list()->size());
313  EXPECT_EQ(0u, plugin_loader()->next_load_index());
314  EXPECT_EQ(plugin3_.path.value(),
315            plugin_loader()->canonical_list()->at(0).value());
316}
317
318TEST_F(PluginLoaderPosixTest, InternalPlugin) {
319  int did_callback = 0;
320  PluginService::GetPluginsCallback callback =
321      base::Bind(&VerifyCallback, base::Unretained(&did_callback));
322
323  plugin_loader()->GetPlugins(callback);
324
325  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
326  message_loop()->RunUntilIdle();
327
328  plugin2_.path = base::FilePath("/internal/plugin.plugin");
329
330  AddThreePlugins();
331
332  plugin_loader()->internal_plugins()->clear();
333  plugin_loader()->internal_plugins()->push_back(plugin2_);
334
335  EXPECT_EQ(0u, plugin_loader()->next_load_index());
336
337  const std::vector<WebPluginInfo>& plugins(plugin_loader()->loaded_plugins());
338
339  plugin_loader()->TestOnPluginLoaded(0, plugin1_);
340  EXPECT_EQ(1u, plugin_loader()->next_load_index());
341  EXPECT_EQ(1u, plugins.size());
342  EXPECT_EQ(plugin1_.name, plugins[0].name);
343
344  message_loop()->RunUntilIdle();
345  EXPECT_EQ(0, did_callback);
346
347  // Internal plugins can fail to load if they're built-in with manual
348  // entrypoint functions.
349  plugin_loader()->TestOnPluginLoadFailed(1, plugin2_.path);
350  EXPECT_EQ(2u, plugin_loader()->next_load_index());
351  EXPECT_EQ(2u, plugins.size());
352  EXPECT_EQ(plugin2_.name, plugins[1].name);
353  EXPECT_EQ(0u, plugin_loader()->internal_plugins()->size());
354
355  message_loop()->RunUntilIdle();
356  EXPECT_EQ(0, did_callback);
357
358  plugin_loader()->TestOnPluginLoaded(2, plugin3_);
359  EXPECT_EQ(3u, plugins.size());
360  EXPECT_EQ(plugin3_.name, plugins[2].name);
361
362  message_loop()->RunUntilIdle();
363  EXPECT_EQ(1, did_callback);
364}
365
366TEST_F(PluginLoaderPosixTest, AllCrashed) {
367  int did_callback = 0;
368  PluginService::GetPluginsCallback callback =
369      base::Bind(&VerifyCallback, base::Unretained(&did_callback));
370
371  plugin_loader()->GetPlugins(callback);
372
373  // Spin the loop so that the canonical list of plugins can be set.
374  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1);
375  message_loop()->RunUntilIdle();
376  AddThreePlugins();
377
378  EXPECT_EQ(0u, plugin_loader()->next_load_index());
379
380  // Mock the first two calls like normal.
381  testing::Expectation first =
382      EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(2);
383  // On the last call, go through the default impl.
384  EXPECT_CALL(*plugin_loader(), LoadPluginsInternal())
385      .After(first)
386      .WillOnce(
387          testing::Invoke(plugin_loader(),
388                          &MockPluginLoaderPosix::RealLoadPluginsInternal));
389  plugin_loader()->OnProcessCrashed(42);
390  plugin_loader()->OnProcessCrashed(42);
391  plugin_loader()->OnProcessCrashed(42);
392
393  message_loop()->RunUntilIdle();
394  EXPECT_EQ(1, did_callback);
395
396  EXPECT_EQ(0u, plugin_loader()->loaded_plugins().size());
397}
398
399}  // namespace content
400