1 // Copyright 2014 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 "chrome/browser/plugins/plugin_info_message_filter.h" 6 7 #include "base/at_exit.h" 8 #include "base/bind.h" 9 #include "base/bind_helpers.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/run_loop.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/content_settings/host_content_settings_map.h" 14 #include "chrome/common/pref_names.h" 15 #include "chrome/common/render_messages.h" 16 #include "chrome/test/base/testing_pref_service_syncable.h" 17 #include "chrome/test/base/testing_profile.h" 18 #include "content/public/browser/plugin_service.h" 19 #include "content/public/browser/plugin_service_filter.h" 20 #include "content/public/browser/render_process_host.h" 21 #include "content/public/test/test_browser_thread_bundle.h" 22 #include "testing/gmock/include/gmock/gmock.h" 23 #include "testing/gtest/include/gtest/gtest.h" 24 25 using content::PluginService; 26 27 namespace { 28 29 void PluginsLoaded(const base::Closure& callback, 30 const std::vector<content::WebPluginInfo>& plugins) { 31 callback.Run(); 32 } 33 34 class FakePluginServiceFilter : public content::PluginServiceFilter { 35 public: 36 FakePluginServiceFilter() {} 37 virtual ~FakePluginServiceFilter() {} 38 39 virtual bool IsPluginAvailable(int render_process_id, 40 int render_view_id, 41 const void* context, 42 const GURL& url, 43 const GURL& policy_url, 44 content::WebPluginInfo* plugin) OVERRIDE; 45 46 virtual bool CanLoadPlugin(int render_process_id, 47 const base::FilePath& path) OVERRIDE; 48 49 void set_plugin_enabled(const base::FilePath& plugin_path, bool enabled) { 50 plugin_state_[plugin_path] = enabled; 51 } 52 53 private: 54 std::map<base::FilePath, bool> plugin_state_; 55 }; 56 57 bool FakePluginServiceFilter::IsPluginAvailable( 58 int render_process_id, 59 int render_view_id, 60 const void* context, 61 const GURL& url, 62 const GURL& policy_url, 63 content::WebPluginInfo* plugin) { 64 std::map<base::FilePath, bool>::iterator it = 65 plugin_state_.find(plugin->path); 66 if (it == plugin_state_.end()) { 67 ADD_FAILURE() << "No plug-in state for '" << plugin->path.value() << "'"; 68 return false; 69 } 70 return it->second; 71 } 72 73 bool FakePluginServiceFilter::CanLoadPlugin(int render_process_id, 74 const base::FilePath& path) { 75 return true; 76 } 77 78 } // namespace 79 80 class PluginInfoMessageFilterTest : public ::testing::Test { 81 public: 82 PluginInfoMessageFilterTest() : 83 foo_plugin_path_(FILE_PATH_LITERAL("/path/to/foo")), 84 bar_plugin_path_(FILE_PATH_LITERAL("/path/to/bar")), 85 context_(0, &profile_) { 86 } 87 88 virtual void SetUp() OVERRIDE { 89 content::WebPluginInfo foo_plugin(base::ASCIIToUTF16("Foo Plug-in"), 90 foo_plugin_path_, 91 base::ASCIIToUTF16("1"), 92 base::ASCIIToUTF16("The Foo plug-in.")); 93 content::WebPluginMimeType mime_type; 94 mime_type.mime_type = "foo/bar"; 95 foo_plugin.mime_types.push_back(mime_type); 96 foo_plugin.type = content::WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS; 97 PluginService::GetInstance()->Init(); 98 PluginService::GetInstance()->RegisterInternalPlugin(foo_plugin, false); 99 100 content::WebPluginInfo bar_plugin(base::ASCIIToUTF16("Bar Plug-in"), 101 bar_plugin_path_, 102 base::ASCIIToUTF16("1"), 103 base::ASCIIToUTF16("The Bar plug-in.")); 104 mime_type.mime_type = "foo/bar"; 105 bar_plugin.mime_types.push_back(mime_type); 106 bar_plugin.type = content::WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS; 107 PluginService::GetInstance()->RegisterInternalPlugin(bar_plugin, false); 108 109 PluginService::GetInstance()->SetFilter(&filter_); 110 111 #if !defined(OS_WIN) 112 // Can't go out of process in unit tests. 113 content::RenderProcessHost::SetRunRendererInProcess(true); 114 #endif 115 base::RunLoop run_loop; 116 PluginService::GetInstance()->GetPlugins( 117 base::Bind(&PluginsLoaded, run_loop.QuitClosure())); 118 run_loop.Run(); 119 #if !defined(OS_WIN) 120 content::RenderProcessHost::SetRunRendererInProcess(false); 121 #endif 122 } 123 124 protected: 125 TestingProfile* profile() { 126 return &profile_; 127 } 128 129 PluginInfoMessageFilter::Context* context() { 130 return &context_; 131 } 132 133 void VerifyPluginContentSetting(const GURL& url, 134 const std::string& plugin, 135 ContentSetting expected_setting, 136 bool expected_is_default, 137 bool expected_is_managed) { 138 ContentSetting setting = expected_setting == CONTENT_SETTING_DEFAULT ? 139 CONTENT_SETTING_BLOCK : CONTENT_SETTING_DEFAULT; 140 bool is_default = !expected_is_default; 141 bool is_managed = !expected_is_managed; 142 context()->GetPluginContentSetting( 143 content::WebPluginInfo(), url, url, plugin, 144 &setting, &is_default, &is_managed); 145 EXPECT_EQ(expected_setting, setting); 146 EXPECT_EQ(expected_is_default, is_default); 147 EXPECT_EQ(expected_is_managed, is_managed); 148 } 149 150 base::FilePath foo_plugin_path_; 151 base::FilePath bar_plugin_path_; 152 FakePluginServiceFilter filter_; 153 154 private: 155 base::ShadowingAtExitManager at_exit_manager_; // Destroys the PluginService. 156 content::TestBrowserThreadBundle test_thread_bundle; 157 TestingProfile profile_; 158 PluginInfoMessageFilter::Context context_; 159 }; 160 161 TEST_F(PluginInfoMessageFilterTest, FindEnabledPlugin) { 162 filter_.set_plugin_enabled(foo_plugin_path_, true); 163 filter_.set_plugin_enabled(bar_plugin_path_, true); 164 { 165 ChromeViewHostMsg_GetPluginInfo_Status status; 166 content::WebPluginInfo plugin; 167 std::string actual_mime_type; 168 EXPECT_TRUE(context()->FindEnabledPlugin( 169 0, GURL(), GURL(), "foo/bar", &status, &plugin, &actual_mime_type, 170 NULL)); 171 EXPECT_EQ(ChromeViewHostMsg_GetPluginInfo_Status::kAllowed, status.value); 172 EXPECT_EQ(foo_plugin_path_.value(), plugin.path.value()); 173 } 174 175 filter_.set_plugin_enabled(foo_plugin_path_, false); 176 { 177 ChromeViewHostMsg_GetPluginInfo_Status status; 178 content::WebPluginInfo plugin; 179 std::string actual_mime_type; 180 EXPECT_TRUE(context()->FindEnabledPlugin( 181 0, GURL(), GURL(), "foo/bar", &status, &plugin, &actual_mime_type, 182 NULL)); 183 EXPECT_EQ(ChromeViewHostMsg_GetPluginInfo_Status::kAllowed, status.value); 184 EXPECT_EQ(bar_plugin_path_.value(), plugin.path.value()); 185 } 186 187 filter_.set_plugin_enabled(bar_plugin_path_, false); 188 { 189 ChromeViewHostMsg_GetPluginInfo_Status status; 190 content::WebPluginInfo plugin; 191 std::string actual_mime_type; 192 std::string identifier; 193 base::string16 plugin_name; 194 EXPECT_FALSE(context()->FindEnabledPlugin( 195 0, GURL(), GURL(), "foo/bar", &status, &plugin, &actual_mime_type, 196 NULL)); 197 EXPECT_EQ(ChromeViewHostMsg_GetPluginInfo_Status::kDisabled, status.value); 198 EXPECT_EQ(foo_plugin_path_.value(), plugin.path.value()); 199 } 200 { 201 ChromeViewHostMsg_GetPluginInfo_Status status; 202 content::WebPluginInfo plugin; 203 std::string actual_mime_type; 204 EXPECT_FALSE(context()->FindEnabledPlugin( 205 0, GURL(), GURL(), "baz/blurp", &status, &plugin, &actual_mime_type, 206 NULL)); 207 EXPECT_EQ(ChromeViewHostMsg_GetPluginInfo_Status::kNotFound, status.value); 208 EXPECT_EQ(FILE_PATH_LITERAL(""), plugin.path.value()); 209 } 210 } 211 212 TEST_F(PluginInfoMessageFilterTest, GetPluginContentSetting) { 213 HostContentSettingsMap* map = profile()->GetHostContentSettingsMap(); 214 215 // Block plugins by default. 216 map->SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_PLUGINS, 217 CONTENT_SETTING_BLOCK); 218 219 // Set plugins to click-to-play on example.com and subdomains. 220 ContentSettingsPattern pattern = 221 ContentSettingsPattern::FromString("[*.]example.com"); 222 map->SetContentSetting(pattern, 223 ContentSettingsPattern::Wildcard(), 224 CONTENT_SETTINGS_TYPE_PLUGINS, 225 std::string(), 226 CONTENT_SETTING_ASK); 227 228 // Allow plugin "foo" on all sites. 229 map->SetContentSetting(ContentSettingsPattern::Wildcard(), 230 ContentSettingsPattern::Wildcard(), 231 CONTENT_SETTINGS_TYPE_PLUGINS, 232 "foo", 233 CONTENT_SETTING_ALLOW); 234 235 GURL unmatched_host("https://www.google.com"); 236 GURL host("http://example.com/"); 237 ASSERT_EQ(CONTENT_SETTING_BLOCK, map->GetContentSetting( 238 unmatched_host, unmatched_host, CONTENT_SETTINGS_TYPE_PLUGINS, 239 std::string())); 240 ASSERT_EQ(CONTENT_SETTING_ASK, map->GetContentSetting( 241 host, host, CONTENT_SETTINGS_TYPE_PLUGINS, std::string())); 242 ASSERT_EQ(CONTENT_SETTING_ALLOW, map->GetContentSetting( 243 host, host, CONTENT_SETTINGS_TYPE_PLUGINS, "foo")); 244 ASSERT_EQ(CONTENT_SETTING_DEFAULT, map->GetContentSetting( 245 host, host, CONTENT_SETTINGS_TYPE_PLUGINS, "bar")); 246 247 // "foo" is allowed everywhere. 248 VerifyPluginContentSetting(host, "foo", CONTENT_SETTING_ALLOW, false, false); 249 250 // There is no specific content setting for "bar", so the general setting 251 // for example.com applies. 252 VerifyPluginContentSetting(host, "bar", CONTENT_SETTING_ASK, false, false); 253 254 // Otherwise, use the default. 255 VerifyPluginContentSetting(unmatched_host, "bar", CONTENT_SETTING_BLOCK, 256 true, false); 257 258 // Block plugins via policy. 259 TestingPrefServiceSyncable* prefs = profile()->GetTestingPrefService(); 260 prefs->SetManagedPref(prefs::kManagedDefaultPluginsSetting, 261 new base::FundamentalValue(CONTENT_SETTING_BLOCK)); 262 263 // All plugins should be blocked now. 264 VerifyPluginContentSetting(host, "foo", CONTENT_SETTING_BLOCK, true, true); 265 VerifyPluginContentSetting(host, "bar", CONTENT_SETTING_BLOCK, true, true); 266 VerifyPluginContentSetting(unmatched_host, "bar", CONTENT_SETTING_BLOCK, 267 true, true); 268 } 269