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.
5 #include "chrome/browser/plugins/plugin_info_message_filter.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/plugins/plugin_prefs.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/chrome_content_client.h"
20 #include "chrome/common/content_settings.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/common/render_messages.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/plugin_service.h"
25 #include "content/public/browser/plugin_service_filter.h"
28 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
30 #if defined(ENABLE_EXTENSIONS)
31 #include "chrome/browser/guest_view/web_view/web_view_renderer_state.h"
35 #include "base/win/metro.h"
38 using content::PluginService;
39 using content::WebPluginInfo;
43 // For certain sandboxed Pepper plugins, use the JavaScript Content Settings.
44 bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) {
45 if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
46 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) {
50 // Treat Native Client invocations like JavaScript.
51 if (plugin.name == base::ASCIIToUTF16(ChromeContentClient::kNaClPluginName))
54 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
55 // Treat CDM invocations like JavaScript.
56 if (plugin.name == base::ASCIIToUTF16(kWidevineCdmDisplayName)) {
57 DCHECK(plugin.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS);
60 #endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
65 #if defined(ENABLE_PEPPER_CDMS)
67 enum PluginAvailabilityStatusForUMA {
68 PLUGIN_NOT_REGISTERED,
71 PLUGIN_AVAILABILITY_STATUS_MAX
74 static void SendPluginAvailabilityUMA(const std::string& mime_type,
75 PluginAvailabilityStatusForUMA status) {
76 #if defined(WIDEVINE_CDM_AVAILABLE)
77 // Only report results for Widevine CDM.
78 if (mime_type != kWidevineCdmPluginMimeType)
81 UMA_HISTOGRAM_ENUMERATION("Plugin.AvailabilityStatus.WidevineCdm",
82 status, PLUGIN_AVAILABILITY_STATUS_MAX);
83 #endif // defined(WIDEVINE_CDM_AVAILABLE)
86 #endif // defined(ENABLE_PEPPER_CDMS)
90 PluginInfoMessageFilter::Context::Context(int render_process_id,
92 : render_process_id_(render_process_id),
93 resource_context_(profile->GetResourceContext()),
94 host_content_settings_map_(profile->GetHostContentSettingsMap()),
95 plugin_prefs_(PluginPrefs::GetForProfile(profile)) {
96 allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated,
98 allow_outdated_plugins_.MoveToThread(
99 content::BrowserThread::GetMessageLoopProxyForThread(
100 content::BrowserThread::IO));
101 always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize,
102 profile->GetPrefs());
103 always_authorize_plugins_.MoveToThread(
104 content::BrowserThread::GetMessageLoopProxyForThread(
105 content::BrowserThread::IO));
108 PluginInfoMessageFilter::Context::~Context() {
111 PluginInfoMessageFilter::PluginInfoMessageFilter(
112 int render_process_id,
114 : BrowserMessageFilter(ChromeMsgStart),
115 context_(render_process_id, profile),
116 weak_ptr_factory_(this) {
119 bool PluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message) {
120 IPC_BEGIN_MESSAGE_MAP(PluginInfoMessageFilter, message)
121 IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_GetPluginInfo,
123 #if defined(ENABLE_PEPPER_CDMS)
125 ChromeViewHostMsg_IsInternalPluginAvailableForMimeType,
126 OnIsInternalPluginAvailableForMimeType)
128 IPC_MESSAGE_UNHANDLED(return false)
129 IPC_END_MESSAGE_MAP()
133 void PluginInfoMessageFilter::OnDestruct() const {
134 const_cast<PluginInfoMessageFilter*>(this)->
135 weak_ptr_factory_.InvalidateWeakPtrs();
137 // Destroy on the UI thread because we contain a |PrefMember|.
138 content::BrowserThread::DeleteOnUIThread::Destruct(this);
141 PluginInfoMessageFilter::~PluginInfoMessageFilter() {}
143 struct PluginInfoMessageFilter::GetPluginInfo_Params {
147 std::string mime_type;
150 void PluginInfoMessageFilter::OnGetPluginInfo(
153 const GURL& top_origin_url,
154 const std::string& mime_type,
155 IPC::Message* reply_msg) {
156 GetPluginInfo_Params params = {
162 PluginService::GetInstance()->GetPlugins(
163 base::Bind(&PluginInfoMessageFilter::PluginsLoaded,
164 weak_ptr_factory_.GetWeakPtr(),
168 void PluginInfoMessageFilter::PluginsLoaded(
169 const GetPluginInfo_Params& params,
170 IPC::Message* reply_msg,
171 const std::vector<WebPluginInfo>& plugins) {
172 ChromeViewHostMsg_GetPluginInfo_Output output;
173 // This also fills in |actual_mime_type|.
174 scoped_ptr<PluginMetadata> plugin_metadata;
175 if (context_.FindEnabledPlugin(params.render_frame_id, params.url,
176 params.top_origin_url, params.mime_type,
177 &output.status, &output.plugin,
178 &output.actual_mime_type,
180 context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(),
184 if (plugin_metadata) {
185 output.group_identifier = plugin_metadata->identifier();
186 output.group_name = plugin_metadata->name();
189 context_.MaybeGrantAccess(output.status, output.plugin.path);
191 ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output);
195 #if defined(ENABLE_PEPPER_CDMS)
197 void PluginInfoMessageFilter::OnIsInternalPluginAvailableForMimeType(
198 const std::string& mime_type,
200 std::vector<base::string16>* additional_param_names,
201 std::vector<base::string16>* additional_param_values) {
202 std::vector<WebPluginInfo> plugins;
203 PluginService::GetInstance()->GetInternalPlugins(&plugins);
205 bool is_plugin_disabled = false;
206 for (size_t i = 0; i < plugins.size(); ++i) {
207 const WebPluginInfo& plugin = plugins[i];
208 const std::vector<content::WebPluginMimeType>& mime_types =
210 for (size_t j = 0; j < mime_types.size(); ++j) {
211 if (mime_types[j].mime_type == mime_type) {
212 if (!context_.IsPluginEnabled(plugin)) {
213 is_plugin_disabled = true;
217 *is_available = true;
218 *additional_param_names = mime_types[j].additional_param_names;
219 *additional_param_values = mime_types[j].additional_param_values;
220 SendPluginAvailabilityUMA(mime_type, PLUGIN_AVAILABLE);
226 *is_available = false;
227 SendPluginAvailabilityUMA(
228 mime_type, is_plugin_disabled ? PLUGIN_DISABLED : PLUGIN_NOT_REGISTERED);
231 #endif // defined(ENABLE_PEPPER_CDMS)
233 void PluginInfoMessageFilter::Context::DecidePluginStatus(
234 const GetPluginInfo_Params& params,
235 const WebPluginInfo& plugin,
236 const PluginMetadata* plugin_metadata,
237 ChromeViewHostMsg_GetPluginInfo_Status* status) const {
239 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI &&
240 base::win::IsMetroProcess()) {
242 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
246 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
247 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
248 // NPAPI plugins are not supported inside <webview> guests.
249 #if defined(ENABLE_EXTENSIONS)
250 if (extensions::WebViewRendererState::GetInstance()->IsGuest(
251 render_process_id_)) {
253 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
259 ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT;
260 bool uses_default_content_setting = true;
261 bool is_managed = false;
262 // Check plug-in content settings. The primary URL is the top origin URL and
263 // the secondary URL is the plug-in URL.
264 GetPluginContentSetting(plugin, params.top_origin_url, params.url,
265 plugin_metadata->identifier(), &plugin_setting,
266 &uses_default_content_setting, &is_managed);
267 DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT);
269 PluginMetadata::SecurityStatus plugin_status =
270 plugin_metadata->GetSecurityStatus(plugin);
271 #if defined(ENABLE_PLUGIN_INSTALLATION)
272 // Check if the plug-in is outdated.
273 if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE &&
274 !allow_outdated_plugins_.GetValue()) {
275 if (allow_outdated_plugins_.IsManaged()) {
277 ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed;
279 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked;
284 // Check if the plug-in or its group is enabled by policy.
285 PluginPrefs::PolicyStatus plugin_policy =
286 plugin_prefs_->PolicyStatusForPlugin(plugin.name);
287 PluginPrefs::PolicyStatus group_policy =
288 plugin_prefs_->PolicyStatusForPlugin(plugin_metadata->name());
290 // Check if the plug-in requires authorization.
292 PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION &&
293 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
294 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS &&
295 !always_authorize_plugins_.GetValue() &&
296 plugin_setting != CONTENT_SETTING_BLOCK &&
297 uses_default_content_setting &&
298 plugin_policy != PluginPrefs::POLICY_ENABLED &&
299 group_policy != PluginPrefs::POLICY_ENABLED &&
300 !ChromePluginServiceFilter::GetInstance()->IsPluginRestricted(
302 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
306 // Check if the plug-in is crashing too much.
307 if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) &&
308 !always_authorize_plugins_.GetValue() &&
309 plugin_setting != CONTENT_SETTING_BLOCK &&
310 uses_default_content_setting) {
311 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
315 if (plugin_setting == CONTENT_SETTING_ASK) {
316 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay;
317 } else if (plugin_setting == CONTENT_SETTING_BLOCK) {
319 is_managed ? ChromeViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy
320 : ChromeViewHostMsg_GetPluginInfo_Status::kBlocked;
323 if (status->value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed) {
324 // Allow an embedder of <webview> to block a plugin from being loaded inside
325 // the guest. In order to do this, set the status to 'Unauthorized' here,
326 // and update the status as appropriate depending on the response from the
328 #if defined(ENABLE_EXTENSIONS)
329 if (extensions::WebViewRendererState::GetInstance()->IsGuest(
331 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
337 bool PluginInfoMessageFilter::Context::FindEnabledPlugin(
340 const GURL& top_origin_url,
341 const std::string& mime_type,
342 ChromeViewHostMsg_GetPluginInfo_Status* status,
343 WebPluginInfo* plugin,
344 std::string* actual_mime_type,
345 scoped_ptr<PluginMetadata>* plugin_metadata) const {
346 bool allow_wildcard = true;
347 std::vector<WebPluginInfo> matching_plugins;
348 std::vector<std::string> mime_types;
349 PluginService::GetInstance()->GetPluginInfoArray(
350 url, mime_type, allow_wildcard, &matching_plugins, &mime_types);
351 if (matching_plugins.empty()) {
352 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound;
356 content::PluginServiceFilter* filter =
357 PluginService::GetInstance()->GetFilter();
359 for (; i < matching_plugins.size(); ++i) {
360 if (!filter || filter->IsPluginAvailable(render_process_id_,
365 &matching_plugins[i])) {
370 // If we broke out of the loop, we have found an enabled plug-in.
371 bool enabled = i < matching_plugins.size();
373 // Otherwise, we only found disabled plug-ins, so we take the first one.
375 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled;
378 *plugin = matching_plugins[i];
379 *actual_mime_type = mime_types[i];
381 *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin);
386 void PluginInfoMessageFilter::Context::GetPluginContentSetting(
387 const WebPluginInfo& plugin,
388 const GURL& policy_url,
389 const GURL& plugin_url,
390 const std::string& resource,
391 ContentSetting* setting,
392 bool* uses_default_content_setting,
393 bool* is_managed) const {
394 scoped_ptr<base::Value> value;
395 content_settings::SettingInfo info;
396 bool uses_plugin_specific_setting = false;
397 if (ShouldUseJavaScriptSettingForPlugin(plugin)) {
399 host_content_settings_map_->GetWebsiteSetting(
400 policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT,
401 std::string(), &info));
403 content_settings::SettingInfo specific_info;
404 scoped_ptr<base::Value> specific_setting(
405 host_content_settings_map_->GetWebsiteSetting(
406 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource,
408 content_settings::SettingInfo general_info;
409 scoped_ptr<base::Value> general_setting(
410 host_content_settings_map_->GetWebsiteSetting(
411 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS,
412 std::string(), &general_info));
414 // If there is a plugin-specific setting, we use it, unless the general
415 // setting was set by policy, in which case it takes precedence.
416 uses_plugin_specific_setting = specific_setting &&
417 (general_info.source != content_settings::SETTING_SOURCE_POLICY);
418 if (uses_plugin_specific_setting) {
419 value = specific_setting.Pass();
420 info = specific_info;
422 value = general_setting.Pass();
426 *setting = content_settings::ValueToContentSetting(value.get());
427 *uses_default_content_setting =
428 !uses_plugin_specific_setting &&
429 info.primary_pattern == ContentSettingsPattern::Wildcard() &&
430 info.secondary_pattern == ContentSettingsPattern::Wildcard();
431 *is_managed = info.source == content_settings::SETTING_SOURCE_POLICY;
434 void PluginInfoMessageFilter::Context::MaybeGrantAccess(
435 const ChromeViewHostMsg_GetPluginInfo_Status& status,
436 const base::FilePath& path) const {
437 if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed ||
438 status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) {
439 ChromePluginServiceFilter::GetInstance()->AuthorizePlugin(
440 render_process_id_, path);
444 bool PluginInfoMessageFilter::Context::IsPluginEnabled(
445 const content::WebPluginInfo& plugin) const {
446 return plugin_prefs_->IsPluginEnabled(plugin);