• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 
41 using content::BrowserThread;
42 
43 namespace {
44 
45 class CallbackBarrier : public base::RefCountedThreadSafe<CallbackBarrier> {
46  public:
CallbackBarrier(const base::Closure & target_callback)47   explicit CallbackBarrier(const base::Closure& target_callback)
48       : target_callback_(target_callback),
49         outstanding_callbacks_(0),
50         did_enable_(true) {
51   }
52 
CreateCallback()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 
~CallbackBarrier()61   ~CallbackBarrier() {
62     EXPECT_TRUE(target_callback_.is_null());
63   }
64 
MayRunTargetCallback(bool did_enable)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 
82 class ChromePluginTest : public InProcessBrowserTest {
83  protected:
ChromePluginTest()84   ChromePluginTest() {}
85 
GetURL(const char * filename)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 
LoadAndWait(Browser * window,const GURL & url,bool pass)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 
CrashFlash()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 
GetFlashPath(std::vector<base::FilePath> * paths)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 
GetPlugins()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 
EnableFlash(bool enable,Profile * profile)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 
EnsureFlashProcessCount(int expected)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:
CrashFlashInternal(const base::Closure & quit_task)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 
GetPluginsInfoCallback(std::vector<content::WebPluginInfo> * rv,const base::Closure & quit_task,const std::vector<content::WebPluginInfo> & plugins)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 
CountPluginProcesses(int * count,const base::Closure & quit_task)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.
IN_PROC_BROWSER_TEST_F(ChromePluginTest,DISABLED_Flash)204 IN_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.
IN_PROC_BROWSER_TEST_F(ChromePluginTest,InstalledPlugins)250 IN_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 
275 #if defined(OS_WIN)
276 
277 namespace {
278 
EnumerateChildren(HWND hwnd,LPARAM l_param)279 BOOL CALLBACK EnumerateChildren(HWND hwnd, LPARAM l_param) {
280   HWND* child = reinterpret_cast<HWND*>(l_param);
281   *child = hwnd;
282   // The first child window is the plugin, then its children. So stop
283   // enumerating after the first callback.
284   return FALSE;
285 }
286 
287 }  // namespace
288 
289 // Test that if a background tab loads an NPAPI plugin, they are displayed after
290 // switching to that page.  http://crbug.com/335900
IN_PROC_BROWSER_TEST_F(ChromePluginTest,WindowedNPAPIPluginHidden)291 IN_PROC_BROWSER_TEST_F(ChromePluginTest, WindowedNPAPIPluginHidden) {
292   browser()->profile()->GetPrefs()->SetBoolean(prefs::kPluginsAlwaysAuthorize,
293                                                true);
294 
295   // First load the page in the background and wait for the NPAPI plugin's
296   // window to be created.
297   GURL url = ui_test_utils::GetTestUrl(
298       base::FilePath(),
299       base::FilePath().AppendASCII("windowed_npapi_plugin.html"));
300 
301   ui_test_utils::NavigateToURLWithDisposition(
302       browser(), url, NEW_BACKGROUND_TAB,
303       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
304 
305   // We create a third window just to trigger the second one to update its
306   // constrained window list. Normally this would be triggered by the status bar
307   // animation closing after the user middle clicked a link.
308   ui_test_utils::NavigateToURLWithDisposition(
309       browser(), GURL("about:blank"), NEW_BACKGROUND_TAB,
310       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
311 
312   base::string16 expected_title(base::ASCIIToUTF16("created"));
313   content::WebContents* tab =
314       browser()->tab_strip_model()->GetWebContentsAt(1);
315   if (tab->GetTitle() != expected_title) {
316     content::TitleWatcher title_watcher(tab, expected_title);
317     EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
318   }
319 
320   // Now activate the tab and verify that the plugin painted.
321   browser()->tab_strip_model()->ActivateTabAt(1, true);
322 
323   base::string16 expected_title2(base::ASCIIToUTF16("shown"));
324   content::TitleWatcher title_watcher2(tab, expected_title2);
325   EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle());
326 
327   HWND child = NULL;
328   HWND hwnd = tab->GetNativeView()->GetHost()->GetAcceleratedWidget();
329   EnumChildWindows(hwnd, EnumerateChildren,reinterpret_cast<LPARAM>(&child));
330 
331   RECT region;
332   int result = GetWindowRgnBox(child, &region);
333   ASSERT_NE(result, NULLREGION);
334 }
335 
336 #endif
337