plugin_info_message_filter.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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 content::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_HANDLER( 101 ChromeViewHostMsg_IsInternalPluginRegisteredForMimeType, 102 OnIsInternalPluginRegisteredForMimeType) 103 IPC_MESSAGE_UNHANDLED(return false) 104 IPC_END_MESSAGE_MAP() 105 return true; 106} 107 108void PluginInfoMessageFilter::OnDestruct() const { 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::OnIsInternalPluginRegisteredForMimeType( 171 const std::string& mime_type, 172 bool* is_registered) { 173 std::vector<WebPluginInfo> plugins; 174 PluginService::GetInstance()->GetInternalPlugins(&plugins); 175 for (size_t i = 0; i < plugins.size(); ++i) { 176 const std::vector<content::WebPluginMimeType>& mime_types = 177 plugins[i].mime_types; 178 for (size_t j = 0; j < mime_types.size(); ++j) { 179 if (mime_types[j].mime_type == mime_type) { 180 *is_registered = true; 181 return; 182 } 183 } 184 } 185 186 *is_registered = false; 187} 188 189void PluginInfoMessageFilter::Context::DecidePluginStatus( 190 const GetPluginInfo_Params& params, 191 const WebPluginInfo& plugin, 192 const PluginMetadata* plugin_metadata, 193 ChromeViewHostMsg_GetPluginInfo_Status* status) const { 194#if defined(OS_WIN) 195 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI && 196 base::win::IsMetroProcess()) { 197 status->value = 198 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported; 199 return; 200 } 201#endif 202 203 ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT; 204 bool uses_default_content_setting = true; 205 // Check plug-in content settings. The primary URL is the top origin URL and 206 // the secondary URL is the plug-in URL. 207 GetPluginContentSetting(plugin, params.top_origin_url, params.url, 208 plugin_metadata->identifier(), &plugin_setting, 209 &uses_default_content_setting); 210 DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT); 211 212 PluginMetadata::SecurityStatus plugin_status = 213 plugin_metadata->GetSecurityStatus(plugin); 214#if defined(ENABLE_PLUGIN_INSTALLATION) 215 // Check if the plug-in is outdated. 216 if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE && 217 !allow_outdated_plugins_.GetValue()) { 218 if (allow_outdated_plugins_.IsManaged()) { 219 status->value = 220 ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed; 221 } else { 222 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked; 223 } 224 return; 225 } 226#endif 227 228 // Check if the plug-in requires authorization. 229 if (plugin_status == 230 PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION && 231 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS && 232 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS && 233 !always_authorize_plugins_.GetValue() && 234 plugin_setting != CONTENT_SETTING_BLOCK && 235 uses_default_content_setting) { 236 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 237 return; 238 } 239 240 // Check if the plug-in is crashing too much. 241 if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) && 242 !always_authorize_plugins_.GetValue() && 243 plugin_setting != CONTENT_SETTING_BLOCK && 244 uses_default_content_setting) { 245 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized; 246 return; 247 } 248 249 if (plugin_setting == CONTENT_SETTING_ASK) 250 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay; 251 else if (plugin_setting == CONTENT_SETTING_BLOCK) 252 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kBlocked; 253} 254 255bool PluginInfoMessageFilter::Context::FindEnabledPlugin( 256 int render_view_id, 257 const GURL& url, 258 const GURL& top_origin_url, 259 const std::string& mime_type, 260 ChromeViewHostMsg_GetPluginInfo_Status* status, 261 WebPluginInfo* plugin, 262 std::string* actual_mime_type, 263 scoped_ptr<PluginMetadata>* plugin_metadata) const { 264 bool allow_wildcard = true; 265 std::vector<WebPluginInfo> matching_plugins; 266 std::vector<std::string> mime_types; 267 PluginService::GetInstance()->GetPluginInfoArray( 268 url, mime_type, allow_wildcard, &matching_plugins, &mime_types); 269 if (matching_plugins.empty()) { 270 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound; 271 return false; 272 } 273 274 content::PluginServiceFilter* filter = 275 PluginService::GetInstance()->GetFilter(); 276 size_t i = 0; 277 for (; i < matching_plugins.size(); ++i) { 278 if (!filter || filter->IsPluginAvailable(render_process_id_, 279 render_view_id, 280 resource_context_, 281 url, 282 top_origin_url, 283 &matching_plugins[i])) { 284 break; 285 } 286 } 287 288 // If we broke out of the loop, we have found an enabled plug-in. 289 bool enabled = i < matching_plugins.size(); 290 if (!enabled) { 291 // Otherwise, we only found disabled plug-ins, so we take the first one. 292 i = 0; 293 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled; 294 } 295 296 *plugin = matching_plugins[i]; 297 *actual_mime_type = mime_types[i]; 298 if (plugin_metadata) 299 *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin); 300 301 return enabled; 302} 303 304void PluginInfoMessageFilter::Context::GetPluginContentSetting( 305 const WebPluginInfo& plugin, 306 const GURL& policy_url, 307 const GURL& plugin_url, 308 const std::string& resource, 309 ContentSetting* setting, 310 bool* uses_default_content_setting) const { 311 312 scoped_ptr<base::Value> value; 313 content_settings::SettingInfo info; 314 bool uses_plugin_specific_setting = false; 315 if (ShouldUseJavaScriptSettingForPlugin(plugin)) { 316 value.reset( 317 host_content_settings_map_->GetWebsiteSetting( 318 policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT, 319 std::string(), &info)); 320 } else { 321 value.reset( 322 host_content_settings_map_->GetWebsiteSetting( 323 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource, 324 &info)); 325 if (value.get()) { 326 uses_plugin_specific_setting = true; 327 } else { 328 value.reset(host_content_settings_map_->GetWebsiteSetting( 329 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, std::string(), 330 &info)); 331 } 332 } 333 *setting = content_settings::ValueToContentSetting(value.get()); 334 *uses_default_content_setting = 335 !uses_plugin_specific_setting && 336 info.primary_pattern == ContentSettingsPattern::Wildcard() && 337 info.secondary_pattern == ContentSettingsPattern::Wildcard(); 338} 339 340void PluginInfoMessageFilter::Context::MaybeGrantAccess( 341 const ChromeViewHostMsg_GetPluginInfo_Status& status, 342 const base::FilePath& path) const { 343 if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed || 344 status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) { 345 ChromePluginServiceFilter::GetInstance()->AuthorizePlugin( 346 render_process_id_, path); 347 } 348} 349 350