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