- add sources.
[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 chrome::ChromeContentClient;
36 using content::PluginService;
37 using content::WebPluginInfo;
38
39 namespace {
40
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) {
45     return false;
46   }
47
48   // Treat Native Client invocations like JavaScript.
49   if (plugin.name == ASCIIToUTF16(ChromeContentClient::kNaClPluginName))
50     return true;
51
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);
56     return true;
57   }
58 #endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
59
60   return false;
61 }
62
63 }  // namespace
64
65 PluginInfoMessageFilter::Context::Context(int render_process_id,
66                                           Profile* profile)
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,
72                                profile->GetPrefs());
73   allow_outdated_plugins_.MoveToThread(
74       content::BrowserThread::GetMessageLoopProxyForThread(
75           content::BrowserThread::IO));
76   always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize,
77                                  profile->GetPrefs());
78   always_authorize_plugins_.MoveToThread(
79       content::BrowserThread::GetMessageLoopProxyForThread(
80           content::BrowserThread::IO));
81 }
82
83 PluginInfoMessageFilter::Context::Context()
84     : render_process_id_(0),
85       resource_context_(NULL),
86       host_content_settings_map_(NULL) {
87 }
88
89 PluginInfoMessageFilter::Context::~Context() {
90 }
91
92 PluginInfoMessageFilter::PluginInfoMessageFilter(
93     int render_process_id,
94     Profile* profile)
95     : context_(render_process_id, profile),
96       weak_ptr_factory_(this) {
97 }
98
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,
103                                     OnGetPluginInfo)
104     IPC_MESSAGE_HANDLER(
105         ChromeViewHostMsg_IsInternalPluginRegisteredForMimeType,
106         OnIsInternalPluginRegisteredForMimeType)
107     IPC_MESSAGE_UNHANDLED(return false)
108   IPC_END_MESSAGE_MAP()
109   return true;
110 }
111
112 void PluginInfoMessageFilter::OnDestruct() const {
113   const_cast<PluginInfoMessageFilter*>(this)->
114       weak_ptr_factory_.InvalidateWeakPtrs();
115
116   // Destroy on the UI thread because we contain a |PrefMember|.
117   content::BrowserThread::DeleteOnUIThread::Destruct(this);
118 }
119
120 PluginInfoMessageFilter::~PluginInfoMessageFilter() {}
121
122 struct PluginInfoMessageFilter::GetPluginInfo_Params {
123   int render_view_id;
124   GURL url;
125   GURL top_origin_url;
126   std::string mime_type;
127 };
128
129 void PluginInfoMessageFilter::OnGetPluginInfo(
130     int render_view_id,
131     const GURL& url,
132     const GURL& top_origin_url,
133     const std::string& mime_type,
134     IPC::Message* reply_msg) {
135   GetPluginInfo_Params params = {
136     render_view_id,
137     url,
138     top_origin_url,
139     mime_type
140   };
141   PluginService::GetInstance()->GetPlugins(
142       base::Bind(&PluginInfoMessageFilter::PluginsLoaded,
143                  weak_ptr_factory_.GetWeakPtr(),
144                  params, reply_msg));
145 }
146
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,
158                                  &plugin_metadata)) {
159     context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(),
160                                 &output.status);
161   }
162
163   if (plugin_metadata) {
164     output.group_identifier = plugin_metadata->identifier();
165     output.group_name = plugin_metadata->name();
166   }
167
168   context_.MaybeGrantAccess(output.status, output.plugin.path);
169
170   ChromeViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output);
171   Send(reply_msg);
172 }
173
174 void PluginInfoMessageFilter::OnIsInternalPluginRegisteredForMimeType(
175     const std::string& mime_type,
176     bool* is_registered,
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;
189         return;
190       }
191     }
192   }
193
194   *is_registered = false;
195 }
196
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 {
202 #if defined(OS_WIN)
203   if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI &&
204       base::win::IsMetroProcess()) {
205     status->value =
206         ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
207     return;
208   }
209 #endif
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)) {
216       status->value =
217           ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
218       return;
219     }
220   }
221
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);
230
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()) {
238       status->value =
239           ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed;
240     } else {
241       status->value = ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked;
242     }
243     return;
244   }
245 #endif
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());
251
252   // Check if the plug-in requires authorization.
253   if (plugin_status ==
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(
263           plugin.path)) {
264     status->value = ChromeViewHostMsg_GetPluginInfo_Status::kBlocked;
265     return;
266   }
267
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;
274     return;
275   }
276
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;
281
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
286     // embedder.
287     ExtensionRendererState::WebViewInfo info;
288     if (ExtensionRendererState::GetInstance()->GetWebViewInfo(
289             render_process_id_, params.render_view_id, &info)) {
290       status->value =
291           ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized;
292     }
293   }
294 }
295
296 bool PluginInfoMessageFilter::Context::FindEnabledPlugin(
297     int render_view_id,
298     const GURL& url,
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;
312     return false;
313   }
314
315   content::PluginServiceFilter* filter =
316       PluginService::GetInstance()->GetFilter();
317   size_t i = 0;
318   for (; i < matching_plugins.size(); ++i) {
319     if (!filter || filter->IsPluginAvailable(render_process_id_,
320                                              render_view_id,
321                                              resource_context_,
322                                              url,
323                                              top_origin_url,
324                                              &matching_plugins[i])) {
325       break;
326     }
327   }
328
329   // If we broke out of the loop, we have found an enabled plug-in.
330   bool enabled = i < matching_plugins.size();
331   if (!enabled) {
332     // Otherwise, we only found disabled plug-ins, so we take the first one.
333     i = 0;
334     status->value = ChromeViewHostMsg_GetPluginInfo_Status::kDisabled;
335   }
336
337   *plugin = matching_plugins[i];
338   *actual_mime_type = mime_types[i];
339   if (plugin_metadata)
340     *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin);
341
342   return enabled;
343 }
344
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)) {
356     value.reset(
357         host_content_settings_map_->GetWebsiteSetting(
358             policy_url, policy_url, CONTENT_SETTINGS_TYPE_JAVASCRIPT,
359             std::string(), &info));
360   } else {
361     value.reset(
362         host_content_settings_map_->GetWebsiteSetting(
363             policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, resource,
364             &info));
365     if (value.get()) {
366       uses_plugin_specific_setting = true;
367     } else {
368       value.reset(host_content_settings_map_->GetWebsiteSetting(
369           policy_url, plugin_url, CONTENT_SETTINGS_TYPE_PLUGINS, std::string(),
370           &info));
371     }
372   }
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();
378 }
379
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);
387   }
388 }
389