plugin_info_message_filter.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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 "chrome/browser/plugins/plugin_info_message_filter.h" 6 7#include "base/bind.h" 8#include "base/memory/scoped_ptr.h" 9#include "base/metrics/histogram.h" 10#include "base/prefs/pref_service.h" 11#include "base/strings/utf_string_conversions.h" 12#include "chrome/browser/content_settings/content_settings_utils.h" 13#include "chrome/browser/content_settings/host_content_settings_map.h" 14#include "chrome/browser/plugins/chrome_plugin_service_filter.h" 15#include "chrome/browser/plugins/plugin_finder.h" 16#include "chrome/browser/plugins/plugin_metadata.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/common/chrome_content_client.h" 19#include "chrome/common/content_settings.h" 20#include "chrome/common/pref_names.h" 21#include "chrome/common/render_messages.h" 22#include "content/public/browser/browser_thread.h" 23#include "content/public/browser/plugin_service.h" 24#include "content/public/browser/plugin_service_filter.h" 25#include "googleurl/src/gurl.h" 26#include "webkit/plugins/npapi/plugin_list.h" 27 28#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. 29 30#if defined(OS_WIN) 31#include "base/win/metro.h" 32#endif 33 34using content::PluginService; 35using webkit::WebPluginInfo; 36 37namespace { 38 39// For certain sandboxed Pepper plugins, use the JavaScript Content Settings. 40bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) { 41 if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS && 42 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) { 43 return false; 44 } 45 46 // Treat Native Client invocations like JavaScript. 47 if (plugin.name == ASCIIToUTF16(chrome::ChromeContentClient::kNaClPluginName)) 48 return true; 49 50#if defined(WIDEVINE_CDM_AVAILABLE) 51 // Treat CDM invocations like JavaScript. 52 if (plugin.name == ASCIIToUTF16(kWidevineCdmDisplayName)) { 53 DCHECK(plugin.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS); 54 return true; 55 } 56#endif // WIDEVINE_CDM_AVAILABLE 57 58 return false; 59} 60 61} // namespace 62 63PluginInfoMessageFilter::Context::Context(int render_process_id, 64 Profile* profile) 65 : render_process_id_(render_process_id), 66 resource_context_(profile->GetResourceContext()), 67 host_content_settings_map_(profile->GetHostContentSettingsMap()) { 68 allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated, 69 profile->GetPrefs()); 70 allow_outdated_plugins_.MoveToThread( 71 content::BrowserThread::GetMessageLoopProxyForThread( 72 content::BrowserThread::IO)); 73 always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize, 74 profile->GetPrefs()); 75 always_authorize_plugins_.MoveToThread( 76 content::BrowserThread::GetMessageLoopProxyForThread( 77 content::BrowserThread::IO)); 78} 79 80PluginInfoMessageFilter::Context::Context() 81 : render_process_id_(0), 82 resource_context_(NULL), 83 host_content_settings_map_(NULL) { 84} 85 86PluginInfoMessageFilter::Context::~Context() { 87} 88 89PluginInfoMessageFilter::PluginInfoMessageFilter( 90 int render_process_id, 91 Profile* profile) 92 : context_(render_process_id, profile), 93 weak_ptr_factory_(this) { 94} 95 96bool PluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message, 97 bool* message_was_ok) { 98 IPC_BEGIN_MESSAGE_MAP_EX(PluginInfoMessageFilter, message, *message_was_ok) 99 IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_GetPluginInfo, 100 OnGetPluginInfo) 101 IPC_MESSAGE_UNHANDLED(return false) 102 IPC_END_MESSAGE_MAP() 103 return true; 104} 105 106void PluginInfoMessageFilter::OnDestruct() const { 107 const_cast<PluginInfoMessageFilter*>(this)-> 108 weak_ptr_factory_.InvalidateWeakPtrs(); 109 110 // Destroy on the UI thread because we contain a |PrefMember|. 111 content::BrowserThread::DeleteOnUIThread::Destruct(this); 112} 113 114PluginInfoMessageFilter::~PluginInfoMessageFilter() {} 115 116struct PluginInfoMessageFilter::GetPluginInfo_Params { 117 int render_view_id; 118 GURL url; 119 GURL top_origin_url; 120 std::string mime_type; 121}; 122 123void PluginInfoMessageFilter::OnGetPluginInfo( 124 int render_view_id, 125 const GURL& url, 126 const GURL& top_origin_url, 127 const std::string& mime_type, 128 IPC::Message* reply_msg) { 129 GetPluginInfo_Params params = { 130 render_view_id, 131 url, 132 top_origin_url, 133 mime_type 134 }; 135 PluginService::GetInstance()->GetPlugins( 136 base::Bind(&PluginInfoMessageFilter::PluginsLoaded, 137 weak_ptr_factory_.GetWeakPtr(), 138 params, reply_msg)); 139} 140 141void PluginInfoMessageFilter::PluginsLoaded( 142 const GetPluginInfo_Params& params, 143 IPC::Message* reply_msg, 144 const std::vector<WebPluginInfo>& plugins) { 145 ChromeViewHostMsg_GetPluginInfo_Output output; 146 // This also fills in |actual_mime_type|. 147 scoped_ptr<PluginMetadata> plugin_metadata; 148 if (context_.FindEnabledPlugin(params.render_view_id, params.url, 149 params.top_origin_url, params.mime_type, 150 &output.status, &output.plugin, 151 &output.actual_mime_type, 152 &plugin_metadata)) { 153 context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(), 154 &output.status); 155 } 156 157 if (plugin_metadata) { 158 output.group_identifier = plugin_metadata->identifier(); 159 output.group_name = plugin_metadata->name(); 160 } 161 162 context_.MaybeGrantAccess(output.status, output.plugin.path); 163 164 ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output); 165 Send(reply_msg); 166} 167 168void PluginInfoMessageFilter::Context::DecidePluginStatus( 169 const GetPluginInfo_Params& params, 170 const WebPluginInfo& plugin, 171 const PluginMetadata* plugin_metadata, 172 ChromeViewHostMsg_GetPluginInfo_Status* status) const { 173#if defined(OS_WIN) 174 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI && 175 base::win::IsMetroProcess()) { 176 status->value = 177 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported; 178 return; 179 } 180#endif 181 182 ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT; 183 bool uses_default_content_setting = true; 184 // Check plug-in content settings. The primary URL is the top origin URL and 185 // the secondary URL is the plug-in URL. 186 GetPluginContentSetting(plugin, params.top_origin_url, params.url, 187 plugin_metadata->identifier(), &plugin_setting, 188 &uses_default_content_setting); 189 DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT); 190 191 PluginMetadata::SecurityStatus plugin_status = 192 plugin_metadata->GetSecurityStatus(plugin); 193#if defined(ENABLE_PLUGIN_INSTALLATION) 194 // Check if the plug-in is outdated. 195 if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE && 196 !allow_outdated_plugins_.GetValue()) { 197 if (allow_outdated_plugins_.IsManaged()) { 198 status->value = 199 ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed; 200 } else { 201 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked; 202 } 203 return; 204 } 205#endif 206 207 // Check if the plug-in requires authorization. 208 if (plugin_status == 209 PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION && 210 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS && 211 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS && 212 !always_authorize_plugins_.GetValue() && 213 plugin_setting != CONTENT_SETTING_BLOCK && 214 uses_default_content_setting) { 215 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 216 return; 217 } 218 219 // Check if the plug-in is crashing too much. 220 if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) && 221 !always_authorize_plugins_.GetValue() && 222 plugin_setting != CONTENT_SETTING_BLOCK && 223 uses_default_content_setting) { 224 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 225 return; 226 } 227 228 if (plugin_setting == CONTENT_SETTING_ASK) 229 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay; 230 else if (plugin_setting == CONTENT_SETTING_BLOCK) 231 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kBlocked; 232} 233 234bool PluginInfoMessageFilter::Context::FindEnabledPlugin( 235 int render_view_id, 236 const GURL& url, 237 const GURL& top_origin_url, 238 const std::string& mime_type, 239 ChromeViewHostMsg_GetPluginInfo_Status* status, 240 WebPluginInfo* plugin, 241 std::string* actual_mime_type, 242 scoped_ptr<PluginMetadata>* plugin_metadata) const { 243 bool allow_wildcard = true; 244 std::vector<WebPluginInfo> matching_plugins; 245 std::vector<std::string> mime_types; 246 PluginService::GetInstance()->GetPluginInfoArray( 247 url, mime_type, allow_wildcard, &matching_plugins, &mime_types); 248 if (matching_plugins.empty()) { 249 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; 250 return false; 251 } 252 253 content::PluginServiceFilter* filter = 254 PluginService::GetInstance()->GetFilter(); 255 size_t i = 0; 256 for (; i < matching_plugins.size(); ++i) { 257 if (!filter || filter->IsPluginAvailable(render_process_id_, 258 render_view_id, 259 resource_context_, 260 url, 261 top_origin_url, 262 &matching_plugins[i])) { 263 break; 264 } 265 } 266 267 // If we broke out of the loop, we have found an enabled plug-in. 268 bool enabled = i < matching_plugins.size(); 269 if (!enabled) { 270 // Otherwise, we only found disabled plug-ins, so we take the first one. 271 i = 0; 272 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled; 273 } 274 275 *plugin = matching_plugins[i]; 276 *actual_mime_type = mime_types[i]; 277 if (plugin_metadata) 278 *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin); 279 280 return enabled; 281} 282 283void PluginInfoMessageFilter::Context::GetPluginContentSetting( 284 const WebPluginInfo& plugin, 285 const GURL& policy_url, 286 const GURL& plugin_url, 287 const std::string& resource, 288 ContentSetting* setting, 289 bool* uses_default_content_setting) const { 290 291 scoped_ptr<base::Value> value; 292 content_settings::SettingInfo info; 293 bool uses_plugin_specific_setting = false; 294 if (ShouldUseJavaScriptSettingForPlugin(plugin)) { 295 value.reset( 296 host_content_settings_map_->GetWebsiteSetting( 297 policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT, 298 std::string(), &info)); 299 } else { 300 value.reset( 301 host_content_settings_map_->GetWebsiteSetting( 302 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource, 303 &info)); 304 if (value.get()) { 305 uses_plugin_specific_setting = true; 306 } else { 307 value.reset(host_content_settings_map_->GetWebsiteSetting( 308 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, std::string(), 309 &info)); 310 } 311 } 312 *setting = content_settings::ValueToContentSetting(value.get()); 313 *uses_default_content_setting = 314 !uses_plugin_specific_setting && 315 info.primary_pattern == ContentSettingsPattern::Wildcard() && 316 info.secondary_pattern == ContentSettingsPattern::Wildcard(); 317} 318 319void PluginInfoMessageFilter::Context::MaybeGrantAccess( 320 const ChromeViewHostMsg_GetPluginInfo_Status& status, 321 const base::FilePath& path) const { 322 if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed || 323 status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) { 324 ChromePluginServiceFilter::GetInstance()->AuthorizePlugin( 325 render_process_id_, path); 326 } 327} 328 329