plugin_info_message_filter.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/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(kWidevineCdmPluginName)) { 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_(ALLOW_THIS_IN_INITIALIZER_LIST(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_.DetachFromThread(); 109 const_cast<PluginInfoMessageFilter*>(this)-> 110 weak_ptr_factory_.InvalidateWeakPtrs(); 111 112 // Destroy on the UI thread because we contain a |PrefMember|. 113 content::BrowserThread::DeleteOnUIThread::Destruct(this); 114} 115 116PluginInfoMessageFilter::~PluginInfoMessageFilter() {} 117 118struct PluginInfoMessageFilter::GetPluginInfo_Params { 119 int render_view_id; 120 GURL url; 121 GURL top_origin_url; 122 std::string mime_type; 123}; 124 125void PluginInfoMessageFilter::OnGetPluginInfo( 126 int render_view_id, 127 const GURL& url, 128 const GURL& top_origin_url, 129 const std::string& mime_type, 130 IPC::Message* reply_msg) { 131 GetPluginInfo_Params params = { 132 render_view_id, 133 url, 134 top_origin_url, 135 mime_type 136 }; 137 PluginService::GetInstance()->GetPlugins( 138 base::Bind(&PluginInfoMessageFilter::PluginsLoaded, 139 weak_ptr_factory_.GetWeakPtr(), 140 params, reply_msg)); 141} 142 143void PluginInfoMessageFilter::PluginsLoaded( 144 const GetPluginInfo_Params& params, 145 IPC::Message* reply_msg, 146 const std::vector<WebPluginInfo>& plugins) { 147 ChromeViewHostMsg_GetPluginInfo_Output output; 148 // This also fills in |actual_mime_type|. 149 scoped_ptr<PluginMetadata> plugin_metadata; 150 if (context_.FindEnabledPlugin(params.render_view_id, params.url, 151 params.top_origin_url, params.mime_type, 152 &output.status, &output.plugin, 153 &output.actual_mime_type, 154 &plugin_metadata)) { 155 context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(), 156 &output.status); 157 } 158 159 if (plugin_metadata) { 160 output.group_identifier = plugin_metadata->identifier(); 161 output.group_name = plugin_metadata->name(); 162 } 163 164 context_.MaybeGrantAccess(output.status, output.plugin.path); 165 166 ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output); 167 Send(reply_msg); 168} 169 170void PluginInfoMessageFilter::Context::DecidePluginStatus( 171 const GetPluginInfo_Params& params, 172 const WebPluginInfo& plugin, 173 const PluginMetadata* plugin_metadata, 174 ChromeViewHostMsg_GetPluginInfo_Status* status) const { 175#if defined(OS_WIN) 176 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI && 177 base::win::IsMetroProcess()) { 178 status->value = 179 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported; 180 return; 181 } 182#endif 183 184 ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT; 185 bool uses_default_content_setting = true; 186 // Check plug-in content settings. The primary URL is the top origin URL and 187 // the secondary URL is the plug-in URL. 188 GetPluginContentSetting(plugin, params.top_origin_url, params.url, 189 plugin_metadata->identifier(), &plugin_setting, 190 &uses_default_content_setting); 191 DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT); 192 193 PluginMetadata::SecurityStatus plugin_status = 194 plugin_metadata->GetSecurityStatus(plugin); 195#if defined(ENABLE_PLUGIN_INSTALLATION) 196 // Check if the plug-in is outdated. 197 if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE && 198 !allow_outdated_plugins_.GetValue()) { 199 if (allow_outdated_plugins_.IsManaged()) { 200 status->value = 201 ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed; 202 } else { 203 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked; 204 } 205 return; 206 } 207#endif 208 209 // Check if the plug-in requires authorization. 210 if (plugin_status == 211 PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION && 212 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS && 213 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS && 214 !always_authorize_plugins_.GetValue() && 215 plugin_setting != CONTENT_SETTING_BLOCK && 216 uses_default_content_setting) { 217 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 218 return; 219 } 220 221 // Check if the plug-in is crashing too much. 222 if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) && 223 !always_authorize_plugins_.GetValue() && 224 plugin_setting != CONTENT_SETTING_BLOCK && 225 uses_default_content_setting) { 226 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 227 return; 228 } 229 230 if (plugin_setting == CONTENT_SETTING_ASK) 231 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay; 232 else if (plugin_setting == CONTENT_SETTING_BLOCK) 233 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kBlocked; 234} 235 236bool PluginInfoMessageFilter::Context::FindEnabledPlugin( 237 int render_view_id, 238 const GURL& url, 239 const GURL& top_origin_url, 240 const std::string& mime_type, 241 ChromeViewHostMsg_GetPluginInfo_Status* status, 242 WebPluginInfo* plugin, 243 std::string* actual_mime_type, 244 scoped_ptr<PluginMetadata>* plugin_metadata) const { 245 bool allow_wildcard = true; 246 std::vector<WebPluginInfo> matching_plugins; 247 std::vector<std::string> mime_types; 248 PluginService::GetInstance()->GetPluginInfoArray( 249 url, mime_type, allow_wildcard, &matching_plugins, &mime_types); 250 if (matching_plugins.empty()) { 251 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; 252 return false; 253 } 254 255 content::PluginServiceFilter* filter = 256 PluginService::GetInstance()->GetFilter(); 257 size_t i = 0; 258 for (; i < matching_plugins.size(); ++i) { 259 if (!filter || filter->IsPluginAvailable(render_process_id_, 260 render_view_id, 261 resource_context_, 262 url, 263 top_origin_url, 264 &matching_plugins[i])) { 265 break; 266 } 267 } 268 269 // If we broke out of the loop, we have found an enabled plug-in. 270 bool enabled = i < matching_plugins.size(); 271 if (!enabled) { 272 // Otherwise, we only found disabled plug-ins, so we take the first one. 273 i = 0; 274 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled; 275 } 276 277 *plugin = matching_plugins[i]; 278 *actual_mime_type = mime_types[i]; 279 if (plugin_metadata) 280 *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin); 281 282 return enabled; 283} 284 285void PluginInfoMessageFilter::Context::GetPluginContentSetting( 286 const WebPluginInfo& plugin, 287 const GURL& policy_url, 288 const GURL& plugin_url, 289 const std::string& resource, 290 ContentSetting* setting, 291 bool* uses_default_content_setting) const { 292 293 scoped_ptr<base::Value> value; 294 content_settings::SettingInfo info; 295 bool uses_plugin_specific_setting = false; 296 if (ShouldUseJavaScriptSettingForPlugin(plugin)) { 297 value.reset( 298 host_content_settings_map_->GetWebsiteSetting( 299 policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT, 300 std::string(), &info)); 301 } else { 302 value.reset( 303 host_content_settings_map_->GetWebsiteSetting( 304 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource, 305 &info)); 306 if (value.get()) { 307 uses_plugin_specific_setting = true; 308 } else { 309 value.reset(host_content_settings_map_->GetWebsiteSetting( 310 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, std::string(), 311 &info)); 312 } 313 } 314 *setting = content_settings::ValueToContentSetting(value.get()); 315 *uses_default_content_setting = 316 !uses_plugin_specific_setting && 317 info.primary_pattern == ContentSettingsPattern::Wildcard() && 318 info.secondary_pattern == ContentSettingsPattern::Wildcard(); 319} 320 321void PluginInfoMessageFilter::Context::MaybeGrantAccess( 322 const ChromeViewHostMsg_GetPluginInfo_Status& status, 323 const base::FilePath& path) const { 324 if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed || 325 status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) { 326 ChromePluginServiceFilter::GetInstance()->AuthorizePlugin( 327 render_process_id_, path); 328 } 329} 330 331