Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / plugins / plugin_info_message_filter.cc
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/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"
27 #include "url/gurl.h"
28
29 #include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
30
31 #if defined(OS_WIN)
32 #include "base/win/metro.h"
33 #endif
34
35 using content::PluginService;
36 using content::WebPluginInfo;
37
38 namespace {
39
40 // For certain sandboxed Pepper plugins, use the JavaScript Content Settings.
41 bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) {
42   if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
43       plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) {
44     return false;
45   }
46
47   // Treat Native Client invocations like JavaScript.
48   if (plugin.name == base::ASCIIToUTF16(ChromeContentClient::kNaClPluginName))
49     return true;
50
51 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
52   // Treat CDM invocations like JavaScript.
53   if (plugin.name == base::ASCIIToUTF16(kWidevineCdmDisplayName)) {
54     DCHECK(plugin.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS);
55     return true;
56   }
57 #endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
58
59   return false;
60 }
61
62 #if defined(ENABLE_PEPPER_CDMS)
63
64 enum PluginAvailabilityStatusForUMA {
65   PLUGIN_NOT_REGISTERED,
66   PLUGIN_AVAILABLE,
67   PLUGIN_DISABLED,
68   PLUGIN_AVAILABILITY_STATUS_MAX
69 };
70
71 static void SendPluginAvailabilityUMA(const std::string& mime_type,
72                                       PluginAvailabilityStatusForUMA status) {
73 #if defined(WIDEVINE_CDM_AVAILABLE)
74   // Only report results for Widevine CDM.
75   if (mime_type != kWidevineCdmPluginMimeType)
76     return;
77
78   UMA_HISTOGRAM_ENUMERATION("Plugin.AvailabilityStatus.WidevineCdm",
79                             status, PLUGIN_AVAILABILITY_STATUS_MAX);
80 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
81 }
82
83 #endif  // defined(ENABLE_PEPPER_CDMS)
84
85 }  // namespace
86
87 PluginInfoMessageFilter::Context::Context(int render_process_id,
88                                           Profile* profile)
89     : render_process_id_(render_process_id),
90       resource_context_(profile->GetResourceContext()),
91       host_content_settings_map_(profile->GetHostContentSettingsMap()),
92       plugin_prefs_(PluginPrefs::GetForProfile(profile)) {
93   allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated,
94                                profile->GetPrefs());
95   allow_outdated_plugins_.MoveToThread(
96       content::BrowserThread::GetMessageLoopProxyForThread(
97           content::BrowserThread::IO));
98   always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize,
99                                  profile->GetPrefs());
100   always_authorize_plugins_.MoveToThread(
101       content::BrowserThread::GetMessageLoopProxyForThread(
102           content::BrowserThread::IO));
103 }
104
105 PluginInfoMessageFilter::Context::~Context() {
106 }
107
108 PluginInfoMessageFilter::PluginInfoMessageFilter(
109     int render_process_id,
110     Profile* profile)
111     : BrowserMessageFilter(ChromeMsgStart),
112       context_(render_process_id, profile),
113       weak_ptr_factory_(this) {
114 }
115
116 bool PluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message) {
117   IPC_BEGIN_MESSAGE_MAP(PluginInfoMessageFilter, message)
118     IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_GetPluginInfo,
119                                     OnGetPluginInfo)
120 #if defined(ENABLE_PEPPER_CDMS)
121     IPC_MESSAGE_HANDLER(
122         ChromeViewHostMsg_IsInternalPluginAvailableForMimeType,
123         OnIsInternalPluginAvailableForMimeType)
124 #endif
125     IPC_MESSAGE_UNHANDLED(return false)
126   IPC_END_MESSAGE_MAP()
127   return true;
128 }
129
130 void PluginInfoMessageFilter::OnDestruct() const {
131   const_cast<PluginInfoMessageFilter*>(this)->
132       weak_ptr_factory_.InvalidateWeakPtrs();
133
134   // Destroy on the UI thread because we contain a |PrefMember|.
135   content::BrowserThread::DeleteOnUIThread::Destruct(this);
136 }
137
138 PluginInfoMessageFilter::~PluginInfoMessageFilter() {}
139
140 struct PluginInfoMessageFilter::GetPluginInfo_Params {
141   int render_frame_id;
142   GURL url;
143   GURL top_origin_url;
144   std::string mime_type;
145 };
146
147 void PluginInfoMessageFilter::OnGetPluginInfo(
148     int render_frame_id,
149     const GURL& url,
150     const GURL& top_origin_url,
151     const std::string& mime_type,
152     IPC::Message* reply_msg) {
153   GetPluginInfo_Params params = {
154     render_frame_id,
155     url,
156     top_origin_url,
157     mime_type
158   };
159   PluginService::GetInstance()->GetPlugins(
160       base::Bind(&PluginInfoMessageFilter::PluginsLoaded,
161                  weak_ptr_factory_.GetWeakPtr(),
162                  params, reply_msg));
163 }
164
165 void PluginInfoMessageFilter::PluginsLoaded(
166     const GetPluginInfo_Params& params,
167     IPC::Message* reply_msg,
168     const std::vector<WebPluginInfo>& plugins) {
169   ChromeViewHostMsg_GetPluginInfo_Output output;
170   // This also fills in |actual_mime_type|.
171   scoped_ptr<PluginMetadata> plugin_metadata;
172   if (context_.FindEnabledPlugin(params.render_frame_id, params.url,
173                                  params.top_origin_url, params.mime_type,
174                                  &output.status, &output.plugin,
175                                  &output.actual_mime_type,
176                                  &plugin_metadata)) {
177     context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(),
178                                 &output.status);
179   }
180
181   if (plugin_metadata) {
182     output.group_identifier = plugin_metadata->identifier();
183     output.group_name = plugin_metadata->name();
184   }
185
186   context_.MaybeGrantAccess(output.status, output.plugin.path);
187
188   ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output);
189   Send(reply_msg);
190 }
191
192 #if defined(ENABLE_PEPPER_CDMS)
193 void PluginInfoMessageFilter::OnIsInternalPluginAvailableForMimeType(
194     const std::string& mime_type,
195     bool* is_available,
196     std::vector<base::string16>* additional_param_names,
197     std::vector<base::string16>* additional_param_values) {
198   std::vector<WebPluginInfo> plugins;
199   PluginService::GetInstance()->GetInternalPlugins(&plugins);
200
201   bool is_plugin_disabled = false;
202   for (size_t i = 0; i < plugins.size(); ++i) {
203     const WebPluginInfo& plugin = plugins[i];
204     const std::vector<content::WebPluginMimeType>& mime_types =
205         plugin.mime_types;
206     for (size_t j = 0; j < mime_types.size(); ++j) {
207       if (mime_types[j].mime_type == mime_type) {
208         if (!context_.IsPluginEnabled(plugin)) {
209           is_plugin_disabled = true;
210           break;
211         }
212
213         *is_available = true;
214         *additional_param_names = mime_types[j].additional_param_names;
215         *additional_param_values = mime_types[j].additional_param_values;
216         SendPluginAvailabilityUMA(mime_type, PLUGIN_AVAILABLE);
217         return;
218       }
219     }
220   }
221
222   *is_available = false;
223   SendPluginAvailabilityUMA(
224       mime_type, is_plugin_disabled ? PLUGIN_DISABLED : PLUGIN_NOT_REGISTERED);
225 }
226
227 #endif // defined(ENABLE_PEPPER_CDMS)
228
229 void PluginInfoMessageFilter::Context::DecidePluginStatus(
230     const GetPluginInfo_Params& params,
231     const WebPluginInfo& plugin,
232     const PluginMetadata* plugin_metadata,
233     ChromeViewHostMsg_GetPluginInfo_Status* status) const {
234 #if defined(OS_WIN)
235   if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI &&
236       base::win::IsMetroProcess()) {
237     status->value =
238         ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
239     return;
240   }
241 #endif
242   if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
243     CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
244     // NPAPI plugins are not supported inside <webview> guests.
245     if (ExtensionRendererState::GetInstance()->IsWebViewRenderer(
246             render_process_id_)) {
247       status->value =
248           ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
249       return;
250     }
251   }
252
253   ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT;
254   bool uses_default_content_setting = true;
255   bool is_managed = false;
256   // Check plug-in content settings. The primary URL is the top origin URL and
257   // the secondary URL is the plug-in URL.
258   GetPluginContentSetting(plugin, params.top_origin_url, params.url,
259                           plugin_metadata->identifier(), &plugin_setting,
260                           &uses_default_content_setting, &is_managed);
261   DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT);
262
263   PluginMetadata::SecurityStatus plugin_status =
264       plugin_metadata->GetSecurityStatus(plugin);
265 #if defined(ENABLE_PLUGIN_INSTALLATION)
266   // Check if the plug-in is outdated.
267   if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE &&
268       !allow_outdated_plugins_.GetValue()) {
269     if (allow_outdated_plugins_.IsManaged()) {
270       status->value =
271           ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed;
272     } else {
273       status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked;
274     }
275     return;
276   }
277 #endif
278   // Check if the plug-in or its group is enabled by policy.
279   PluginPrefs::PolicyStatus plugin_policy =
280       plugin_prefs_->PolicyStatusForPlugin(plugin.name);
281   PluginPrefs::PolicyStatus group_policy =
282       plugin_prefs_->PolicyStatusForPlugin(plugin_metadata->name());
283
284   // Check if the plug-in requires authorization.
285   if (plugin_status ==
286           PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION &&
287       plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
288       plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS &&
289       !always_authorize_plugins_.GetValue() &&
290       plugin_setting != CONTENT_SETTING_BLOCK &&
291       uses_default_content_setting &&
292       plugin_policy != PluginPrefs::POLICY_ENABLED &&
293       group_policy != PluginPrefs::POLICY_ENABLED &&
294       !ChromePluginServiceFilter::GetInstance()->IsPluginRestricted(
295           plugin.path)) {
296     status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
297     return;
298   }
299
300   // Check if the plug-in is crashing too much.
301   if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) &&
302       !always_authorize_plugins_.GetValue() &&
303       plugin_setting != CONTENT_SETTING_BLOCK &&
304       uses_default_content_setting) {
305     status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
306     return;
307   }
308
309   if (plugin_setting == CONTENT_SETTING_ASK) {
310       status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay;
311   } else if (plugin_setting == CONTENT_SETTING_BLOCK) {
312     status->value =
313         is_managed ? ChromeViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy
314                    : ChromeViewHostMsg_GetPluginInfo_Status::kBlocked;
315   }
316
317   if (status->value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed) {
318     // Allow an embedder of <webview> to block a plugin from being loaded inside
319     // the guest. In order to do this, set the status to 'Unauthorized' here,
320     // and update the status as appropriate depending on the response from the
321     // embedder.
322     if (ExtensionRendererState::GetInstance()->IsWebViewRenderer(
323             render_process_id_)) {
324       status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
325     }
326   }
327 }
328
329 bool PluginInfoMessageFilter::Context::FindEnabledPlugin(
330     int render_frame_id,
331     const GURL& url,
332     const GURL& top_origin_url,
333     const std::string& mime_type,
334     ChromeViewHostMsg_GetPluginInfo_Status* status,
335     WebPluginInfo* plugin,
336     std::string* actual_mime_type,
337     scoped_ptr<PluginMetadata>* plugin_metadata) const {
338   bool allow_wildcard = true;
339   std::vector<WebPluginInfo> matching_plugins;
340   std::vector<std::string> mime_types;
341   PluginService::GetInstance()->GetPluginInfoArray(
342       url, mime_type, allow_wildcard, &matching_plugins, &mime_types);
343   if (matching_plugins.empty()) {
344     status->value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound;
345     return false;
346   }
347
348   content::PluginServiceFilter* filter =
349       PluginService::GetInstance()->GetFilter();
350   size_t i = 0;
351   for (; i < matching_plugins.size(); ++i) {
352     if (!filter || filter->IsPluginAvailable(render_process_id_,
353                                              render_frame_id,
354                                              resource_context_,
355                                              url,
356                                              top_origin_url,
357                                              &matching_plugins[i])) {
358       break;
359     }
360   }
361
362   // If we broke out of the loop, we have found an enabled plug-in.
363   bool enabled = i < matching_plugins.size();
364   if (!enabled) {
365     // Otherwise, we only found disabled plug-ins, so we take the first one.
366     i = 0;
367     status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled;
368   }
369
370   *plugin = matching_plugins[i];
371   *actual_mime_type = mime_types[i];
372   if (plugin_metadata)
373     *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin);
374
375   return enabled;
376 }
377
378 void PluginInfoMessageFilter::Context::GetPluginContentSetting(
379     const WebPluginInfo& plugin,
380     const GURL& policy_url,
381     const GURL& plugin_url,
382     const std::string& resource,
383     ContentSetting* setting,
384     bool* uses_default_content_setting,
385     bool* is_managed) const {
386   scoped_ptr<base::Value> value;
387   content_settings::SettingInfo info;
388   bool uses_plugin_specific_setting = false;
389   if (ShouldUseJavaScriptSettingForPlugin(plugin)) {
390     value.reset(
391         host_content_settings_map_->GetWebsiteSetting(
392             policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT,
393             std::string(), &info));
394   } else {
395     content_settings::SettingInfo specific_info;
396     scoped_ptr<base::Value> specific_setting(
397         host_content_settings_map_->GetWebsiteSetting(
398             policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource,
399             &specific_info));
400     content_settings::SettingInfo general_info;
401     scoped_ptr<base::Value> general_setting(
402         host_content_settings_map_->GetWebsiteSetting(
403             policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS,
404             std::string(), &general_info));
405
406     // If there is a plugin-specific setting, we use it, unless the general
407     // setting was set by policy, in which case it takes precedence.
408     uses_plugin_specific_setting = specific_setting &&
409         (general_info.source != content_settings::SETTING_SOURCE_POLICY);
410     if (uses_plugin_specific_setting) {
411       value = specific_setting.Pass();
412       info = specific_info;
413     } else {
414       value = general_setting.Pass();
415       info = general_info;
416     }
417   }
418   *setting = content_settings::ValueToContentSetting(value.get());
419   *uses_default_content_setting =
420       !uses_plugin_specific_setting &&
421       info.primary_pattern == ContentSettingsPattern::Wildcard() &&
422       info.secondary_pattern == ContentSettingsPattern::Wildcard();
423   *is_managed = info.source == content_settings::SETTING_SOURCE_POLICY;
424 }
425
426 void PluginInfoMessageFilter::Context::MaybeGrantAccess(
427     const ChromeViewHostMsg_GetPluginInfo_Status& status,
428     const base::FilePath& path) const {
429   if (status.value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed ||
430       status.value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay) {
431     ChromePluginServiceFilter::GetInstance()->AuthorizePlugin(
432         render_process_id_, path);
433   }
434 }
435
436 bool PluginInfoMessageFilter::Context::IsPluginEnabled(
437     const content::WebPluginInfo& plugin) const {
438   return plugin_prefs_->IsPluginEnabled(plugin);
439 }