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