blob: f5eabe4f9057d2ae6878ae8e98c64fb5f9202306 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/plugins/plugin_info_host_impl.h"
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/plugins/plugin_metadata.h"
#include "chrome/browser/plugins/plugin_utils.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/plugin_service_filter.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_constants.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
using content::PluginService;
using testing::Eq;
namespace {
void PluginsLoaded(const base::Closure& callback,
const std::vector<content::WebPluginInfo>& plugins) {
callback.Run();
}
class FakePluginServiceFilter : public content::PluginServiceFilter {
public:
FakePluginServiceFilter() {}
~FakePluginServiceFilter() override {}
bool IsPluginAvailable(int render_process_id,
int render_view_id,
const void* context,
const GURL& url,
const url::Origin& main_frame_origin,
content::WebPluginInfo* plugin) override;
bool CanLoadPlugin(int render_process_id,
const base::FilePath& path) override;
void set_plugin_enabled(const base::FilePath& plugin_path, bool enabled) {
plugin_state_[plugin_path] = enabled;
}
private:
std::map<base::FilePath, bool> plugin_state_;
};
bool FakePluginServiceFilter::IsPluginAvailable(
int render_process_id,
int render_view_id,
const void* context,
const GURL& url,
const url::Origin& main_frame_origin,
content::WebPluginInfo* plugin) {
auto it = plugin_state_.find(plugin->path);
if (it == plugin_state_.end()) {
ADD_FAILURE() << "No plugin state for '" << plugin->path.value() << "'";
return false;
}
return it->second;
}
bool FakePluginServiceFilter::CanLoadPlugin(int render_process_id,
const base::FilePath& path) {
return true;
}
} // namespace
class PluginInfoHostImplTest : public ::testing::Test {
public:
PluginInfoHostImplTest()
: foo_plugin_path_(FILE_PATH_LITERAL("/path/to/foo")),
bar_plugin_path_(FILE_PATH_LITERAL("/path/to/bar")),
fake_flash_path_(FILE_PATH_LITERAL("/path/to/fake/flash")),
context_(0, &profile_),
host_content_settings_map_(
HostContentSettingsMapFactory::GetForProfile(&profile_)) {}
void SetUp() override {
content::WebPluginInfo foo_plugin(base::ASCIIToUTF16("Foo Plugin"),
foo_plugin_path_, base::ASCIIToUTF16("1"),
base::ASCIIToUTF16("The Foo plugin."));
content::WebPluginMimeType mime_type;
mime_type.mime_type = "foo/bar";
foo_plugin.mime_types.push_back(mime_type);
foo_plugin.type = content::WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS;
PluginService::GetInstance()->Init();
PluginService::GetInstance()->RegisterInternalPlugin(foo_plugin, false);
content::WebPluginInfo bar_plugin(base::ASCIIToUTF16("Bar Plugin"),
bar_plugin_path_, base::ASCIIToUTF16("1"),
base::ASCIIToUTF16("The Bar plugin."));
mime_type.mime_type = "foo/bar";
bar_plugin.mime_types.push_back(mime_type);
bar_plugin.type = content::WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS;
PluginService::GetInstance()->RegisterInternalPlugin(bar_plugin, false);
content::WebPluginInfo fake_flash(
base::ASCIIToUTF16(content::kFlashPluginName), fake_flash_path_,
base::ASCIIToUTF16("100.0"),
base::ASCIIToUTF16("Fake Flash Description."));
mime_type.mime_type = content::kFlashPluginSwfMimeType;
fake_flash.mime_types.push_back(mime_type);
fake_flash.type = content::WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS;
PluginService::GetInstance()->RegisterInternalPlugin(fake_flash, false);
PluginService::GetInstance()->SetFilter(&filter_);
#if !defined(OS_WIN)
// Can't go out of process in unit tests.
content::RenderProcessHost::SetRunRendererInProcess(true);
#endif
base::RunLoop run_loop;
PluginService::GetInstance()->GetPlugins(
base::BindOnce(&PluginsLoaded, run_loop.QuitClosure()));
run_loop.Run();
#if !defined(OS_WIN)
content::RenderProcessHost::SetRunRendererInProcess(false);
#endif
}
protected:
TestingProfile* profile() { return &profile_; }
PluginInfoHostImpl::Context* context() { return &context_; }
HostContentSettingsMap* host_content_settings_map() {
return host_content_settings_map_;
}
void VerifyPluginContentSetting(const GURL& url,
const std::string& plugin,
ContentSetting expected_setting,
bool expected_is_default,
bool expected_is_managed) {
ContentSetting setting = expected_setting == CONTENT_SETTING_DEFAULT
? CONTENT_SETTING_BLOCK
: CONTENT_SETTING_DEFAULT;
bool is_default = !expected_is_default;
bool is_managed = !expected_is_managed;
// Pass in a fake Flash plugin info.
content::WebPluginInfo plugin_info(
base::ASCIIToUTF16(content::kFlashPluginName), base::FilePath(),
base::ASCIIToUTF16("1"), base::ASCIIToUTF16("Fake Flash"));
PluginUtils::GetPluginContentSetting(
host_content_settings_map_, plugin_info, url::Origin::Create(url), url,
plugin, &setting, &is_default, &is_managed);
EXPECT_EQ(expected_setting, setting);
EXPECT_EQ(expected_is_default, is_default);
EXPECT_EQ(expected_is_managed, is_managed);
}
base::FilePath foo_plugin_path_;
base::FilePath bar_plugin_path_;
base::FilePath fake_flash_path_;
FakePluginServiceFilter filter_;
private:
base::ShadowingAtExitManager at_exit_manager_; // Destroys the PluginService.
content::TestBrowserThreadBundle test_thread_bundle;
TestingProfile profile_;
PluginInfoHostImpl::Context context_;
HostContentSettingsMap* host_content_settings_map_;
};
TEST_F(PluginInfoHostImplTest, FindEnabledPlugin) {
filter_.set_plugin_enabled(foo_plugin_path_, true);
filter_.set_plugin_enabled(bar_plugin_path_, true);
{
chrome::mojom::PluginStatus status;
content::WebPluginInfo plugin;
std::string actual_mime_type;
EXPECT_TRUE(context()->FindEnabledPlugin(0, GURL(), url::Origin(),
"foo/bar", &status, &plugin,
&actual_mime_type, NULL));
EXPECT_EQ(chrome::mojom::PluginStatus::kAllowed, status);
EXPECT_EQ(foo_plugin_path_.value(), plugin.path.value());
}
filter_.set_plugin_enabled(foo_plugin_path_, false);
{
chrome::mojom::PluginStatus status;
content::WebPluginInfo plugin;
std::string actual_mime_type;
EXPECT_TRUE(context()->FindEnabledPlugin(0, GURL(), url::Origin(),
"foo/bar", &status, &plugin,
&actual_mime_type, NULL));
EXPECT_EQ(chrome::mojom::PluginStatus::kAllowed, status);
EXPECT_EQ(bar_plugin_path_.value(), plugin.path.value());
}
filter_.set_plugin_enabled(bar_plugin_path_, false);
{
chrome::mojom::PluginStatus status;
content::WebPluginInfo plugin;
std::string actual_mime_type;
std::string identifier;
base::string16 plugin_name;
EXPECT_FALSE(context()->FindEnabledPlugin(0, GURL(), url::Origin(),
"foo/bar", &status, &plugin,
&actual_mime_type, NULL));
EXPECT_EQ(chrome::mojom::PluginStatus::kDisabled, status);
EXPECT_EQ(foo_plugin_path_.value(), plugin.path.value());
}
{
chrome::mojom::PluginStatus status;
content::WebPluginInfo plugin;
std::string actual_mime_type;
EXPECT_FALSE(context()->FindEnabledPlugin(0, GURL(), url::Origin(),
"baz/blurp", &status, &plugin,
&actual_mime_type, NULL));
EXPECT_EQ(chrome::mojom::PluginStatus::kNotFound, status);
EXPECT_EQ(FILE_PATH_LITERAL(""), plugin.path.value());
}
}
TEST_F(PluginInfoHostImplTest, PreferHtmlOverPlugins) {
// The HTML5 By Default feature hides Flash using the plugin filter.
filter_.set_plugin_enabled(fake_flash_path_, false);
// Make a real HTTP origin, as all Flash content from non-HTTP and non-FILE
// origins are blocked.
url::Origin main_frame_origin =
url::Origin::Create(GURL("http://example.com"));
chrome::mojom::PluginStatus status;
content::WebPluginInfo plugin;
std::string actual_mime_type;
EXPECT_TRUE(context()->FindEnabledPlugin(
0, GURL(), main_frame_origin, content::kFlashPluginSwfMimeType, &status,
&plugin, &actual_mime_type, NULL));
EXPECT_EQ(chrome::mojom::PluginStatus::kFlashHiddenPreferHtml, status);
PluginMetadata::SecurityStatus security_status =
PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
context()->DecidePluginStatus(GURL(), main_frame_origin, plugin,
security_status, content::kFlashPluginName,
&status);
EXPECT_EQ(chrome::mojom::PluginStatus::kFlashHiddenPreferHtml, status);
// Now block plugins.
host_content_settings_map()->SetDefaultContentSetting(
CONTENT_SETTINGS_TYPE_PLUGINS, CONTENT_SETTING_BLOCK);
context()->DecidePluginStatus(GURL(), main_frame_origin, plugin,
security_status, content::kFlashPluginName,
&status);
EXPECT_EQ(chrome::mojom::PluginStatus::kBlockedNoLoading, status);
}
TEST_F(PluginInfoHostImplTest, RunAllFlashInAllowMode) {
filter_.set_plugin_enabled(fake_flash_path_, true);
// Make a real HTTP origin, as all Flash content from non-HTTP and non-FILE
// origins are blocked.
url::Origin main_frame_origin =
url::Origin::Create(GURL("http://example.com"));
chrome::mojom::PluginStatus status;
content::WebPluginInfo plugin;
std::string actual_mime_type;
ASSERT_TRUE(context()->FindEnabledPlugin(
0, GURL(), main_frame_origin, content::kFlashPluginSwfMimeType, &status,
&plugin, &actual_mime_type, nullptr));
ASSERT_THAT(status, Eq(chrome::mojom::PluginStatus::kAllowed));
host_content_settings_map()->SetContentSettingDefaultScope(
main_frame_origin.GetURL(), GURL(), CONTENT_SETTINGS_TYPE_PLUGINS,
std::string(), CONTENT_SETTING_ALLOW);
ASSERT_FALSE(
profile()->GetPrefs()->GetBoolean(prefs::kRunAllFlashInAllowMode));
PluginMetadata::SecurityStatus security_status =
PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
context()->DecidePluginStatus(GURL(), main_frame_origin, plugin,
security_status, content::kFlashPluginName,
&status);
EXPECT_THAT(status, Eq(chrome::mojom::PluginStatus::kPlayImportantContent));
// Reset the status to allowed.
status = chrome::mojom::PluginStatus::kAllowed;
profile()->GetPrefs()->SetBoolean(prefs::kRunAllFlashInAllowMode, true);
context()->DecidePluginStatus(GURL(), main_frame_origin, plugin,
security_status, content::kFlashPluginName,
&status);
EXPECT_THAT(status, Eq(chrome::mojom::PluginStatus::kAllowed));
}
TEST_F(PluginInfoHostImplTest, PluginsAllowedInWhitelistedSchemes) {
VerifyPluginContentSetting(GURL("http://example.com"), "foo",
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, true,
false);
VerifyPluginContentSetting(GURL("https://example.com"), "foo",
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, true,
false);
VerifyPluginContentSetting(GURL("file://foobar/"), "foo",
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, true,
false);
VerifyPluginContentSetting(GURL("chrome-extension://extension-id"), "foo",
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, true,
false);
VerifyPluginContentSetting(GURL("unknown-scheme://foobar"), "foo",
CONTENT_SETTING_BLOCK, true, false);
}
TEST_F(PluginInfoHostImplTest, GetPluginContentSetting) {
HostContentSettingsMap* map = host_content_settings_map();
{
bool is_managed = false;
EXPECT_EQ(
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT,
PluginUtils::UnsafeGetRawDefaultFlashContentSetting(map, &is_managed));
EXPECT_FALSE(is_managed);
}
// Block plugins by default.
map->SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_PLUGINS,
CONTENT_SETTING_BLOCK);
{
bool is_managed = false;
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
PluginUtils::UnsafeGetRawDefaultFlashContentSetting(map, &is_managed));
EXPECT_FALSE(is_managed);
}
// Set plugins to Plugin Power Saver on example.com and subdomains.
GURL host("http://example.com/");
map->SetContentSettingDefaultScope(
host, GURL(), CONTENT_SETTINGS_TYPE_PLUGINS, std::string(),
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT);
GURL unmatched_host("https://www.google.com");
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map->GetContentSetting(unmatched_host, unmatched_host,
CONTENT_SETTINGS_TYPE_PLUGINS, std::string()));
EXPECT_EQ(CONTENT_SETTING_DETECT_IMPORTANT_CONTENT,
map->GetContentSetting(host, host, CONTENT_SETTINGS_TYPE_PLUGINS,
std::string()));
VerifyPluginContentSetting(host, std::string(),
CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, false,
false);
VerifyPluginContentSetting(unmatched_host, std::string(),
CONTENT_SETTING_BLOCK, false, false);
// Block plugins via policy.
sync_preferences::TestingPrefServiceSyncable* prefs =
profile()->GetTestingPrefService();
prefs->SetManagedPref(prefs::kManagedDefaultPluginsSetting,
std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
// All plugins should be blocked now.
VerifyPluginContentSetting(host, std::string(), CONTENT_SETTING_BLOCK, true,
true);
VerifyPluginContentSetting(unmatched_host, std::string(),
CONTENT_SETTING_BLOCK, true, true);
{
bool is_managed = false;
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
PluginUtils::UnsafeGetRawDefaultFlashContentSetting(map, &is_managed));
EXPECT_TRUE(is_managed);
}
}