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