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