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/extensions/extension_renderer_state.h"
15 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
16 #include "chrome/browser/plugins/plugin_finder.h"
17 #include "chrome/browser/plugins/plugin_metadata.h"
18 #include "chrome/browser/plugins/plugin_prefs.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/chrome_content_client.h"
21 #include "chrome/common/content_settings.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/common/render_messages.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/plugin_service.h"
26 #include "content/public/browser/plugin_service_filter.h"
29 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
32 #include "base/win/metro.h"
35 using chrome::ChromeContentClient;
36 using content::PluginService;
37 using content::WebPluginInfo;
41 // For certain sandboxed Pepper plugins, use the JavaScript Content Settings.
42 bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) {
43 if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
44 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) {
48 // Treat Native Client invocations like JavaScript.
49 if (plugin.name == ASCIIToUTF16(ChromeContentClient::kNaClPluginName))
52 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
53 // Treat CDM invocations like JavaScript.
54 if (plugin.name == ASCIIToUTF16(kWidevineCdmDisplayName)) {
55 DCHECK(plugin.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS);
58 #endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
65 PluginInfoMessageFilter::Context::Context(int render_process_id,
67 : render_process_id_(render_process_id),
68 resource_context_(profile->GetResourceContext()),
69 host_content_settings_map_(profile->GetHostContentSettingsMap()),
70 plugin_prefs_(PluginPrefs::GetForProfile(profile)) {
71 allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated,
73 allow_outdated_plugins_.MoveToThread(
74 content::BrowserThread::GetMessageLoopProxyForThread(
75 content::BrowserThread::IO));
76 always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize,
78 always_authorize_plugins_.MoveToThread(
79 content::BrowserThread::GetMessageLoopProxyForThread(
80 content::BrowserThread::IO));
83 PluginInfoMessageFilter::Context::Context()
84 : render_process_id_(0),
85 resource_context_(NULL),
86 host_content_settings_map_(NULL) {
89 PluginInfoMessageFilter::Context::~Context() {
92 PluginInfoMessageFilter::PluginInfoMessageFilter(
93 int render_process_id,
95 : context_(render_process_id, profile),
96 weak_ptr_factory_(this) {
99 bool PluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message,
100 bool* message_was_ok) {
101 IPC_BEGIN_MESSAGE_MAP_EX(PluginInfoMessageFilter, message, *message_was_ok)
102 IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_GetPluginInfo,
105 ChromeViewHostMsg_IsInternalPluginRegisteredForMimeType,
106 OnIsInternalPluginRegisteredForMimeType)
107 IPC_MESSAGE_UNHANDLED(return false)
108 IPC_END_MESSAGE_MAP()
112 void PluginInfoMessageFilter::OnDestruct() const {
113 const_cast<PluginInfoMessageFilter*>(this)->
114 weak_ptr_factory_.InvalidateWeakPtrs();
116 // Destroy on the UI thread because we contain a |PrefMember|.
117 content::BrowserThread::DeleteOnUIThread::Destruct(this);
120 PluginInfoMessageFilter::~PluginInfoMessageFilter() {}
122 struct PluginInfoMessageFilter::GetPluginInfo_Params {
126 std::string mime_type;
129 void PluginInfoMessageFilter::OnGetPluginInfo(
132 const GURL& top_origin_url,
133 const std::string& mime_type,
134 IPC::Message* reply_msg) {
135 GetPluginInfo_Params params = {
141 PluginService::GetInstance()->GetPlugins(
142 base::Bind(&PluginInfoMessageFilter::PluginsLoaded,
143 weak_ptr_factory_.GetWeakPtr(),
147 void PluginInfoMessageFilter::PluginsLoaded(
148 const GetPluginInfo_Params& params,
149 IPC::Message* reply_msg,
150 const std::vector<WebPluginInfo>& plugins) {
151 ChromeViewHostMsg_GetPluginInfo_Output output;
152 // This also fills in |actual_mime_type|.
153 scoped_ptr<PluginMetadata> plugin_metadata;
154 if (context_.FindEnabledPlugin(params.render_view_id, params.url,
155 params.top_origin_url, params.mime_type,
156 &output.status, &output.plugin,
157 &output.actual_mime_type,
159 context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(),
163 if (plugin_metadata) {
164 output.group_identifier = plugin_metadata->identifier();
165 output.group_name = plugin_metadata->name();
168 context_.MaybeGrantAccess(output.status, output.plugin.path);
170 ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output);
174 void PluginInfoMessageFilter::OnIsInternalPluginRegisteredForMimeType(
175 const std::string& mime_type,
177 std::vector<base::string16>* additional_param_names,
178 std::vector<base::string16>* additional_param_values) {
179 std::vector<WebPluginInfo> plugins;
180 PluginService::GetInstance()->GetInternalPlugins(&plugins);
181 for (size_t i = 0; i < plugins.size(); ++i) {
182 const std::vector<content::WebPluginMimeType>& mime_types =
183 plugins[i].mime_types;
184 for (size_t j = 0; j < mime_types.size(); ++j) {
185 if (mime_types[j].mime_type == mime_type) {
186 *is_registered = true;
187 *additional_param_names = mime_types[j].additional_param_names;
188 *additional_param_values = mime_types[j].additional_param_values;
194 *is_registered = false;
197 void PluginInfoMessageFilter::Context::DecidePluginStatus(
198 const GetPluginInfo_Params& params,
199 const WebPluginInfo& plugin,
200 const PluginMetadata* plugin_metadata,
201 ChromeViewHostMsg_GetPluginInfo_Status* status) const {
203 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI &&
204 base::win::IsMetroProcess()) {
206 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
210 if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
211 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
212 // NPAPI plugins are not supported inside <webview> guests.
213 ExtensionRendererState::WebViewInfo info;
214 if (ExtensionRendererState::GetInstance()->GetWebViewInfo(
215 render_process_id_, params.render_view_id, &info)) {
217 ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
222 ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT;
223 bool uses_default_content_setting = true;
224 // Check plug-in content settings. The primary URL is the top origin URL and
225 // the secondary URL is the plug-in URL.
226 GetPluginContentSetting(plugin, params.top_origin_url, params.url,
227 plugin_metadata->identifier(), &plugin_setting,
228 &uses_default_content_setting);
229 DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT);
231 PluginMetadata::SecurityStatus plugin_status =
232 plugin_metadata->GetSecurityStatus(plugin);
233 #if defined(ENABLE_PLUGIN_INSTALLATION)
234 // Check if the plug-in is outdated.
235 if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE &&
236 !allow_outdated_plugins_.GetValue()) {
237 if (allow_outdated_plugins_.IsManaged()) {
239 ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed;
241 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked;
246 // Check if the plug-in or its group is enabled by policy.
247 PluginPrefs::PolicyStatus plugin_policy =
248 plugin_prefs_->PolicyStatusForPlugin(plugin.name);
249 PluginPrefs::PolicyStatus group_policy =
250 plugin_prefs_->PolicyStatusForPlugin(plugin_metadata->name());
252 // Check if the plug-in requires authorization.
254 PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION &&
255 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
256 plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS &&
257 !always_authorize_plugins_.GetValue() &&
258 plugin_setting != CONTENT_SETTING_BLOCK &&
259 uses_default_content_setting &&
260 plugin_policy != PluginPrefs::POLICY_ENABLED &&
261 group_policy != PluginPrefs::POLICY_ENABLED &&
262 !ChromePluginServiceFilter::GetInstance()->IsPluginRestricted(
264 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kBlocked;
268 // Check if the plug-in is crashing too much.
269 if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) &&
270 !always_authorize_plugins_.GetValue() &&
271 plugin_setting != CONTENT_SETTING_BLOCK &&
272 uses_default_content_setting) {
273 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
277 if (plugin_setting == CONTENT_SETTING_ASK)
278 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay;
279 else if (plugin_setting == CONTENT_SETTING_BLOCK)
280 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kBlocked;
282 if (status->value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed) {
283 // Allow an embedder of <webview> to block a plugin from being loaded inside
284 // the guest. In order to do this, set the status to 'Unauthorized' here,
285 // and update the status as appropriate depending on the response from the
287 ExtensionRendererState::WebViewInfo info;
288 if (ExtensionRendererState::GetInstance()->GetWebViewInfo(
289 render_process_id_, params.render_view_id, &info)) {
291 ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
296 bool PluginInfoMessageFilter::Context::FindEnabledPlugin(
299 const GURL& top_origin_url,
300 const std::string& mime_type,
301 ChromeViewHostMsg_GetPluginInfo_Status* status,
302 WebPluginInfo* plugin,
303 std::string* actual_mime_type,
304 scoped_ptr<PluginMetadata>* plugin_metadata) const {
305 bool allow_wildcard = true;
306 std::vector<WebPluginInfo> matching_plugins;
307 std::vector<std::string> mime_types;
308 PluginService::GetInstance()->GetPluginInfoArray(
309 url, mime_type, allow_wildcard, &matching_plugins, &mime_types);
310 if (matching_plugins.empty()) {
311 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound;
315 content::PluginServiceFilter* filter =
316 PluginService::GetInstance()->GetFilter();
318 for (; i < matching_plugins.size(); ++i) {
319 if (!filter || filter->IsPluginAvailable(render_process_id_,
324 &matching_plugins[i])) {
329 // If we broke out of the loop, we have found an enabled plug-in.
330 bool enabled = i < matching_plugins.size();
332 // Otherwise, we only found disabled plug-ins, so we take the first one.
334 status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled;
337 *plugin = matching_plugins[i];
338 *actual_mime_type = mime_types[i];
340 *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin);
345 void PluginInfoMessageFilter::Context::GetPluginContentSetting(
346 const WebPluginInfo& plugin,
347 const GURL& policy_url,
348 const GURL& plugin_url,
349 const std::string& resource,
350 ContentSetting* setting,
351 bool* uses_default_content_setting) const {
352 scoped_ptr<base::Value> value;
353 content_settings::SettingInfo info;
354 bool uses_plugin_specific_setting = false;
355 if (ShouldUseJavaScriptSettingForPlugin(plugin)) {
357 host_content_settings_map_->GetWebsiteSetting(
358 policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT,
359 std::string(), &info));
362 host_content_settings_map_->GetWebsiteSetting(
363 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource,
366 uses_plugin_specific_setting = true;
368 value.reset(host_content_settings_map_->GetWebsiteSetting(
369 policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, std::string(),
373 *setting = content_settings::ValueToContentSetting(value.get());
374 *uses_default_content_setting =
375 !uses_plugin_specific_setting &&
376 info.primary_pattern == ContentSettingsPattern::Wildcard() &&
377 info.secondary_pattern == ContentSettingsPattern::Wildcard();
380 void PluginInfoMessageFilter::Context::MaybeGrantAccess(
381 const ChromeViewHostMsg_GetPluginInfo_Status& status,
382 const base::FilePath& path) const {
383 if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed ||
384 status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) {
385 ChromePluginServiceFilter::GetInstance()->AuthorizePlugin(
386 render_process_id_, path);