Upstream version 9.38.198.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/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"
26 #include "url/gurl.h"
27
28 #include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
29
30 #if defined(ENABLE_EXTENSIONS)
31 #include "chrome/browser/guest_view/web_view/web_view_renderer_state.h"
32 #endif
33
34 #if defined(OS_WIN)
35 #include "base/win/metro.h"
36 #endif
37
38 using content::PluginService;
39 using content::WebPluginInfo;
40
41 namespace {
42
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) {
47     return false;
48   }
49
50   // Treat Native Client invocations like JavaScript.
51   if (plugin.name == base::ASCIIToUTF16(ChromeContentClient::kNaClPluginName))
52     return true;
53
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);
58     return true;
59   }
60 #endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
61
62   return false;
63 }
64
65 #if defined(ENABLE_PEPPER_CDMS)
66
67 enum PluginAvailabilityStatusForUMA {
68   PLUGIN_NOT_REGISTERED,
69   PLUGIN_AVAILABLE,
70   PLUGIN_DISABLED,
71   PLUGIN_AVAILABILITY_STATUS_MAX
72 };
73
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)
79     return;
80
81   UMA_HISTOGRAM_ENUMERATION("Plugin.AvailabilityStatus.WidevineCdm",
82                             status, PLUGIN_AVAILABILITY_STATUS_MAX);
83 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
84 }
85
86 #endif  // defined(ENABLE_PEPPER_CDMS)
87
88 }  // namespace
89
90 PluginInfoMessageFilter::Context::Context(int render_process_id,
91                                           Profile* profile)
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,
97                                profile->GetPrefs());
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));
106 }
107
108 PluginInfoMessageFilter::Context::~Context() {
109 }
110
111 PluginInfoMessageFilter::PluginInfoMessageFilter(
112     int render_process_id,
113     Profile* profile)
114     : BrowserMessageFilter(ChromeMsgStart),
115       context_(render_process_id, profile),
116       weak_ptr_factory_(this) {
117 }
118
119 bool PluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message) {
120   IPC_BEGIN_MESSAGE_MAP(PluginInfoMessageFilter, message)
121     IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_GetPluginInfo,
122                                     OnGetPluginInfo)
123 #if defined(ENABLE_PEPPER_CDMS)
124     IPC_MESSAGE_HANDLER(
125         ChromeViewHostMsg_IsInternalPluginAvailableForMimeType,
126         OnIsInternalPluginAvailableForMimeType)
127 #endif
128     IPC_MESSAGE_UNHANDLED(return false)
129   IPC_END_MESSAGE_MAP()
130   return true;
131 }
132
133 void PluginInfoMessageFilter::OnDestruct() const {
134   const_cast<PluginInfoMessageFilter*>(this)->
135       weak_ptr_factory_.InvalidateWeakPtrs();
136
137   // Destroy on the UI thread because we contain a |PrefMember|.
138   content::BrowserThread::DeleteOnUIThread::Destruct(this);
139 }
140
141 PluginInfoMessageFilter::~PluginInfoMessageFilter() {}
142
143 struct PluginInfoMessageFilter::GetPluginInfo_Params {
144   int render_frame_id;
145   GURL url;
146   GURL top_origin_url;
147   std::string mime_type;
148 };
149
150 void PluginInfoMessageFilter::OnGetPluginInfo(
151     int render_frame_id,
152     const GURL& url,
153     const GURL& top_origin_url,
154     const std::string& mime_type,
155     IPC::Message* reply_msg) {
156   GetPluginInfo_Params params = {
157     render_frame_id,
158     url,
159     top_origin_url,
160     mime_type
161   };
162   PluginService::GetInstance()->GetPlugins(
163       base::Bind(&PluginInfoMessageFilter::PluginsLoaded,
164                  weak_ptr_factory_.GetWeakPtr(),
165                  params, reply_msg));
166 }
167
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,
179                                  &plugin_metadata)) {
180     context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(),
181                                 &output.status);
182   }
183
184   if (plugin_metadata) {
185     output.group_identifier = plugin_metadata->identifier();
186     output.group_name = plugin_metadata->name();
187   }
188
189   context_.MaybeGrantAccess(output.status, output.plugin.path);
190
191   ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output);
192   Send(reply_msg);
193 }
194
195 #if defined(ENABLE_PEPPER_CDMS)
196
197 void PluginInfoMessageFilter::OnIsInternalPluginAvailableForMimeType(
198     const std::string& mime_type,
199     bool* is_available,
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);
204
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 =
209         plugin.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;
214           break;
215         }
216
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);
221         return;
222       }
223     }
224   }
225
226   *is_available = false;
227   SendPluginAvailabilityUMA(
228       mime_type, is_plugin_disabled ? PLUGIN_DISABLED : PLUGIN_NOT_REGISTERED);
229 }
230
231 #endif // defined(ENABLE_PEPPER_CDMS)
232
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 {
238 #if defined(OS_WIN)
239   if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI &&
240       base::win::IsMetroProcess()) {
241     status->value =
242         ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
243     return;
244   }
245 #endif
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_)) {
252       status->value =
253           ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
254       return;
255     }
256 #endif
257   }
258
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);
268
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()) {
276       status->value =
277           ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed;
278     } else {
279       status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked;
280     }
281     return;
282   }
283 #endif
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());
289
290   // Check if the plug-in requires authorization.
291   if (plugin_status ==
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(
301           plugin.path)) {
302     status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
303     return;
304   }
305
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;
312     return;
313   }
314
315   if (plugin_setting == CONTENT_SETTING_ASK) {
316       status->value = ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay;
317   } else if (plugin_setting == CONTENT_SETTING_BLOCK) {
318     status->value =
319         is_managed ? ChromeViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy
320                    : ChromeViewHostMsg_GetPluginInfo_Status::kBlocked;
321   }
322
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
327     // embedder.
328 #if defined(ENABLE_EXTENSIONS)
329     if (extensions::WebViewRendererState::GetInstance()->IsGuest(
330         render_process_id_))
331       status->value = ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
332
333 #endif
334   }
335 }
336
337 bool PluginInfoMessageFilter::Context::FindEnabledPlugin(
338     int render_frame_id,
339     const GURL& url,
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;
353     return false;
354   }
355
356   content::PluginServiceFilter* filter =
357       PluginService::GetInstance()->GetFilter();
358   size_t i = 0;
359   for (; i < matching_plugins.size(); ++i) {
360     if (!filter || filter->IsPluginAvailable(render_process_id_,
361                                              render_frame_id,
362                                              resource_context_,
363                                              url,
364                                              top_origin_url,
365                                              &matching_plugins[i])) {
366       break;
367     }
368   }
369
370   // If we broke out of the loop, we have found an enabled plug-in.
371   bool enabled = i < matching_plugins.size();
372   if (!enabled) {
373     // Otherwise, we only found disabled plug-ins, so we take the first one.
374     i = 0;
375     status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled;
376   }
377
378   *plugin = matching_plugins[i];
379   *actual_mime_type = mime_types[i];
380   if (plugin_metadata)
381     *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin);
382
383   return enabled;
384 }
385
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)) {
398     value.reset(
399         host_content_settings_map_->GetWebsiteSetting(
400             policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT,
401             std::string(), &info));
402   } else {
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,
407             &specific_info));
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));
413
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;
421     } else {
422       value = general_setting.Pass();
423       info = general_info;
424     }
425   }
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;
432 }
433
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);
441   }
442 }
443
444 bool PluginInfoMessageFilter::Context::IsPluginEnabled(
445     const content::WebPluginInfo& plugin) const {
446   return plugin_prefs_->IsPluginEnabled(plugin);
447 }