Home | History | Annotate | Download | only in browser
      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/net_util.h"
     35 
     36 #if defined(OS_WIN)
     37 #include "content/public/browser/web_contents_view.h"
     38 #include "ui/aura/root_window.h"
     39 #include "ui/aura/window.h"
     40 #endif
     41 
     42 using content::BrowserThread;
     43 
     44 namespace {
     45 
     46 class CallbackBarrier : public base::RefCountedThreadSafe<CallbackBarrier> {
     47  public:
     48   explicit CallbackBarrier(const base::Closure& target_callback)
     49       : target_callback_(target_callback),
     50         outstanding_callbacks_(0),
     51         did_enable_(true) {
     52   }
     53 
     54   base::Callback<void(bool)> CreateCallback() {
     55     outstanding_callbacks_++;
     56     return base::Bind(&CallbackBarrier::MayRunTargetCallback, this);
     57   }
     58 
     59  private:
     60   friend class base::RefCountedThreadSafe<CallbackBarrier>;
     61 
     62   ~CallbackBarrier() {
     63     EXPECT_TRUE(target_callback_.is_null());
     64   }
     65 
     66   void MayRunTargetCallback(bool did_enable) {
     67     EXPECT_GT(outstanding_callbacks_, 0);
     68     did_enable_ = did_enable_ && did_enable;
     69     if (--outstanding_callbacks_ == 0) {
     70       EXPECT_TRUE(did_enable_);
     71       target_callback_.Run();
     72       target_callback_.Reset();
     73     }
     74   }
     75 
     76   base::Closure target_callback_;
     77   int outstanding_callbacks_;
     78   bool did_enable_;
     79 };
     80 
     81 }  // namespace
     82 
     83 class ChromePluginTest : public InProcessBrowserTest {
     84  protected:
     85   ChromePluginTest() {}
     86 
     87   static GURL GetURL(const char* filename) {
     88     base::FilePath path;
     89     PathService::Get(content::DIR_TEST_DATA, &path);
     90     path = path.AppendASCII("plugin").AppendASCII(filename);
     91     CHECK(base::PathExists(path));
     92     return net::FilePathToFileURL(path);
     93   }
     94 
     95   static void LoadAndWait(Browser* window, const GURL& url, bool pass) {
     96     content::WebContents* web_contents =
     97         window->tab_strip_model()->GetActiveWebContents();
     98     base::string16 expected_title(ASCIIToUTF16(pass ? "OK" : "plugin_not_found"));
     99     content::TitleWatcher title_watcher(web_contents, expected_title);
    100     title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL"));
    101     title_watcher.AlsoWaitForTitle(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 == 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.
    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 // Verify that the official builds have the known set of plugins.
    249 IN_PROC_BROWSER_TEST_F(ChromePluginTest, InstalledPlugins) {
    250 #if !defined(OFFICIAL_BUILD)
    251   return;
    252 #endif
    253   const char* expected[] = {
    254     "Chrome PDF Viewer",
    255     "Shockwave Flash",
    256     "Native Client",
    257     "Chrome Remote Desktop Viewer",
    258 #if defined(OS_CHROMEOS)
    259     "Google Talk Plugin",
    260     "Google Talk Plugin Video Accelerator",
    261     "Netflix",
    262 #endif
    263   };
    264 
    265   std::vector<content::WebPluginInfo> plugins = GetPlugins();
    266   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) {
    267     size_t j = 0;
    268     for (; j < plugins.size(); ++j) {
    269       if (plugins[j].name == ASCIIToUTF16(expected[i]))
    270         break;
    271     }
    272     ASSERT_TRUE(j != plugins.size()) << "Didn't find " << expected[i];
    273   }
    274 }
    275 
    276 #if defined(OS_WIN)
    277 
    278 namespace {
    279 
    280 BOOL CALLBACK EnumerateChildren(HWND hwnd, LPARAM l_param) {
    281   HWND* child = reinterpret_cast<HWND*>(l_param);
    282   *child = hwnd;
    283   // The first child window is the plugin, then its children. So stop
    284   // enumerating after the first callback.
    285   return FALSE;
    286 }
    287 
    288 }
    289 
    290 // Test that if a background tab loads an NPAPI plugin, they are displayed after
    291 // switching to that page.  http://crbug.com/335900
    292 IN_PROC_BROWSER_TEST_F(ChromePluginTest, WindowedNPAPIPluginHidden) {
    293   browser()->profile()->GetPrefs()->SetBoolean(prefs::kPluginsAlwaysAuthorize,
    294                                                true);
    295 
    296   // First load the page in the background and wait for the NPAPI plugin's
    297   // window to be created.
    298   GURL url = ui_test_utils::GetTestUrl(
    299       base::FilePath(),
    300       base::FilePath().AppendASCII("windowed_npapi_plugin.html"));
    301 
    302   ui_test_utils::NavigateToURLWithDisposition(
    303       browser(), url, NEW_BACKGROUND_TAB,
    304       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    305 
    306   // We create a third window just to trigger the second one to update its
    307   // constrained window list. Normally this would be triggered by the status bar
    308   // animation closing after the user middle clicked a link.
    309   ui_test_utils::NavigateToURLWithDisposition(
    310       browser(), GURL("about:blank"), NEW_BACKGROUND_TAB,
    311       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
    312 
    313   base::string16 expected_title(base::ASCIIToUTF16("created"));
    314   content::WebContents* tab =
    315       browser()->tab_strip_model()->GetWebContentsAt(1);
    316   if (tab->GetTitle() != expected_title) {
    317     content::TitleWatcher title_watcher(tab, expected_title);
    318     EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
    319   }
    320 
    321   // Now activate the tab and verify that the plugin painted.
    322   browser()->tab_strip_model()->ActivateTabAt(1, true);
    323 
    324   base::string16 expected_title2(base::ASCIIToUTF16("shown"));
    325   content::TitleWatcher title_watcher2(tab, expected_title2);
    326   EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle());
    327 
    328   HWND child = NULL;
    329   HWND hwnd = tab->GetView()->GetNativeView()->GetDispatcher()->host()->
    330       GetAcceleratedWidget();
    331   EnumChildWindows(hwnd, EnumerateChildren,reinterpret_cast<LPARAM>(&child));
    332 
    333   RECT region;
    334   int result = GetWindowRgnBox(child, &region);
    335   ASSERT_NE(result, NULLREGION);
    336 }
    337 
    338 #endif
    339