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 <vector>
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/command_line.h"
10#include "base/file_util.h"
11#include "base/memory/ref_counted.h"
12#include "base/path_service.h"
13#include "base/process/kill.h"
14#include "base/strings/utf_string_conversions.h"
15#include "chrome/browser/plugins/plugin_prefs.h"
16#include "chrome/browser/ui/browser.h"
17#include "chrome/browser/ui/tabs/tab_strip_model.h"
18#include "chrome/test/base/in_process_browser_test.h"
19#include "chrome/test/base/ui_test_utils.h"
20#include "content/public/browser/browser_child_process_host_iterator.h"
21#include "content/public/browser/browser_thread.h"
22#include "content/public/browser/child_process_data.h"
23#include "content/public/browser/plugin_service.h"
24#include "content/public/common/content_constants.h"
25#include "content/public/common/content_paths.h"
26#include "content/public/common/process_type.h"
27#include "content/public/common/webplugininfo.h"
28#include "content/public/test/browser_test_utils.h"
29#include "content/public/test/test_utils.h"
30#include "net/base/net_util.h"
31
32using content::BrowserThread;
33
34namespace {
35
36class CallbackBarrier : public base::RefCountedThreadSafe<CallbackBarrier> {
37 public:
38  explicit CallbackBarrier(const base::Closure& target_callback)
39      : target_callback_(target_callback),
40        outstanding_callbacks_(0),
41        did_enable_(true) {
42  }
43
44  base::Callback<void(bool)> CreateCallback() {
45    outstanding_callbacks_++;
46    return base::Bind(&CallbackBarrier::MayRunTargetCallback, this);
47  }
48
49 private:
50  friend class base::RefCountedThreadSafe<CallbackBarrier>;
51
52  ~CallbackBarrier() {
53    EXPECT_TRUE(target_callback_.is_null());
54  }
55
56  void MayRunTargetCallback(bool did_enable) {
57    EXPECT_GT(outstanding_callbacks_, 0);
58    did_enable_ = did_enable_ && did_enable;
59    if (--outstanding_callbacks_ == 0) {
60      EXPECT_TRUE(did_enable_);
61      target_callback_.Run();
62      target_callback_.Reset();
63    }
64  }
65
66  base::Closure target_callback_;
67  int outstanding_callbacks_;
68  bool did_enable_;
69};
70
71}  // namespace
72
73class ChromePluginTest : public InProcessBrowserTest {
74 protected:
75  ChromePluginTest() {}
76
77  static GURL GetURL(const char* filename) {
78    base::FilePath path;
79    PathService::Get(content::DIR_TEST_DATA, &path);
80    path = path.AppendASCII("plugin").AppendASCII(filename);
81    CHECK(base::PathExists(path));
82    return net::FilePathToFileURL(path);
83  }
84
85  static void LoadAndWait(Browser* window, const GURL& url, bool pass) {
86    content::WebContents* web_contents =
87        window->tab_strip_model()->GetActiveWebContents();
88    string16 expected_title(ASCIIToUTF16(pass ? "OK" : "plugin_not_found"));
89    content::TitleWatcher title_watcher(web_contents, expected_title);
90    title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL"));
91    title_watcher.AlsoWaitForTitle(ASCIIToUTF16(
92        pass ? "plugin_not_found" : "OK"));
93    ui_test_utils::NavigateToURL(window, url);
94    ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
95  }
96
97  static void CrashFlash() {
98    scoped_refptr<content::MessageLoopRunner> runner =
99        new content::MessageLoopRunner;
100    BrowserThread::PostTask(
101        BrowserThread::IO,
102        FROM_HERE,
103        base::Bind(&CrashFlashInternal, runner->QuitClosure()));
104    runner->Run();
105  }
106
107  static void GetFlashPath(std::vector<base::FilePath>* paths) {
108    paths->clear();
109    std::vector<content::WebPluginInfo> plugins = GetPlugins();
110    for (std::vector<content::WebPluginInfo>::const_iterator it =
111             plugins.begin(); it != plugins.end(); ++it) {
112      if (it->name == ASCIIToUTF16(content::kFlashPluginName))
113        paths->push_back(it->path);
114    }
115  }
116
117  static std::vector<content::WebPluginInfo> GetPlugins() {
118    std::vector<content::WebPluginInfo> plugins;
119    scoped_refptr<content::MessageLoopRunner> runner =
120        new content::MessageLoopRunner;
121    content::PluginService::GetInstance()->GetPlugins(
122        base::Bind(&GetPluginsInfoCallback, &plugins, runner->QuitClosure()));
123    runner->Run();
124    return plugins;
125  }
126
127  static void EnableFlash(bool enable, Profile* profile) {
128    std::vector<base::FilePath> paths;
129    GetFlashPath(&paths);
130    ASSERT_FALSE(paths.empty());
131
132    PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile).get();
133    scoped_refptr<content::MessageLoopRunner> runner =
134        new content::MessageLoopRunner;
135    scoped_refptr<CallbackBarrier> callback_barrier(
136        new CallbackBarrier(runner->QuitClosure()));
137    for (std::vector<base::FilePath>::iterator iter = paths.begin();
138         iter != paths.end(); ++iter) {
139      plugin_prefs->EnablePlugin(enable, *iter,
140                                 callback_barrier->CreateCallback());
141    }
142    runner->Run();
143  }
144
145  static void EnsureFlashProcessCount(int expected) {
146    int actual = 0;
147    scoped_refptr<content::MessageLoopRunner> runner =
148        new content::MessageLoopRunner;
149    BrowserThread::PostTask(
150        BrowserThread::IO,
151        FROM_HERE,
152        base::Bind(&CountPluginProcesses, &actual, runner->QuitClosure()));
153    runner->Run();
154    ASSERT_EQ(expected, actual);
155  }
156
157 private:
158  static void CrashFlashInternal(const base::Closure& quit_task) {
159    bool found = false;
160    for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
161      if (iter.GetData().process_type != content::PROCESS_TYPE_PLUGIN &&
162          iter.GetData().process_type != content::PROCESS_TYPE_PPAPI_PLUGIN) {
163        continue;
164      }
165      base::KillProcess(iter.GetData().handle, 0, true);
166      found = true;
167    }
168    ASSERT_TRUE(found) << "Didn't find Flash process!";
169    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit_task);
170  }
171
172  static void GetPluginsInfoCallback(
173      std::vector<content::WebPluginInfo>* rv,
174      const base::Closure& quit_task,
175      const std::vector<content::WebPluginInfo>& plugins) {
176    *rv = plugins;
177    quit_task.Run();
178  }
179
180  static void CountPluginProcesses(int* count, const base::Closure& quit_task) {
181    for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
182      if (iter.GetData().process_type == content::PROCESS_TYPE_PLUGIN ||
183          iter.GetData().process_type == content::PROCESS_TYPE_PPAPI_PLUGIN) {
184        (*count)++;
185      }
186    }
187    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit_task);
188  }
189};
190
191// Tests a bunch of basic scenarios with Flash.
192// This test fails under ASan on Mac, see http://crbug.com/147004.
193// It fails elsewhere, too.  See http://crbug.com/152071.
194IN_PROC_BROWSER_TEST_F(ChromePluginTest, DISABLED_Flash) {
195  // Official builds always have bundled Flash.
196#if !defined(OFFICIAL_BUILD)
197  std::vector<base::FilePath> flash_paths;
198  GetFlashPath(&flash_paths);
199  if (flash_paths.empty()) {
200    LOG(INFO) << "Test not running because couldn't find Flash.";
201    return;
202  }
203#endif
204
205  GURL url = GetURL("flash.html");
206  EnsureFlashProcessCount(0);
207
208  // Try a single tab.
209  ASSERT_NO_FATAL_FAILURE(LoadAndWait(browser(), url, true));
210  EnsureFlashProcessCount(1);
211  Profile* profile = browser()->profile();
212  // Try another tab.
213  ASSERT_NO_FATAL_FAILURE(LoadAndWait(CreateBrowser(profile), url, true));
214  // Try an incognito window.
215  ASSERT_NO_FATAL_FAILURE(LoadAndWait(CreateIncognitoBrowser(), url, true));
216  EnsureFlashProcessCount(1);
217
218  // Now kill Flash process and verify it reloads.
219  CrashFlash();
220  EnsureFlashProcessCount(0);
221
222  ASSERT_NO_FATAL_FAILURE(LoadAndWait(browser(), url, true));
223  EnsureFlashProcessCount(1);
224
225  // Now try disabling it.
226  EnableFlash(false, profile);
227  CrashFlash();
228
229  ASSERT_NO_FATAL_FAILURE(LoadAndWait(browser(), url, false));
230  EnsureFlashProcessCount(0);
231
232  // Now enable it again.
233  EnableFlash(true, profile);
234  ASSERT_NO_FATAL_FAILURE(LoadAndWait(browser(), url, true));
235  EnsureFlashProcessCount(1);
236}
237
238// Verify that the official builds have the known set of plugins.
239IN_PROC_BROWSER_TEST_F(ChromePluginTest, InstalledPlugins) {
240#if !defined(OFFICIAL_BUILD)
241  return;
242#endif
243  const char* expected[] = {
244    "Chrome PDF Viewer",
245    "Shockwave Flash",
246    "Native Client",
247    "Chrome Remote Desktop Viewer",
248#if defined(OS_CHROMEOS)
249    "Google Talk Plugin",
250    "Google Talk Plugin Video Accelerator",
251    "Netflix",
252#endif
253  };
254
255  std::vector<content::WebPluginInfo> plugins = GetPlugins();
256  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) {
257    size_t j = 0;
258    for (; j < plugins.size(); ++j) {
259      if (plugins[j].name == ASCIIToUTF16(expected[i]))
260        break;
261    }
262    ASSERT_TRUE(j != plugins.size()) << "Didn't find " << expected[i];
263  }
264}
265