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/chrome_plugin_service_filter.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/plugins/plugin_metadata.h"
12#include "chrome/browser/plugins/plugin_prefs.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/common/render_messages.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/browser/notification_service.h"
17#include "content/public/browser/plugin_service.h"
18#include "content/public/browser/render_frame_host.h"
19#include "content/public/browser/render_process_host.h"
20#include "content/public/browser/resource_context.h"
21#include "content/public/browser/web_contents.h"
22
23using content::BrowserThread;
24using content::PluginService;
25
26namespace {
27
28void AuthorizeRenderer(content::RenderFrameHost* render_frame_host) {
29  ChromePluginServiceFilter::GetInstance()->AuthorizePlugin(
30      render_frame_host->GetProcess()->GetID(), base::FilePath());
31}
32
33}
34
35// static
36ChromePluginServiceFilter* ChromePluginServiceFilter::GetInstance() {
37  return Singleton<ChromePluginServiceFilter>::get();
38}
39
40void ChromePluginServiceFilter::RegisterResourceContext(
41    PluginPrefs* plugin_prefs,
42    const void* context) {
43  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
44  base::AutoLock lock(lock_);
45  resource_context_map_[context] = plugin_prefs;
46}
47
48void ChromePluginServiceFilter::UnregisterResourceContext(
49    const void* context) {
50  base::AutoLock lock(lock_);
51  resource_context_map_.erase(context);
52}
53
54void ChromePluginServiceFilter::OverridePluginForFrame(
55    int render_process_id,
56    int render_frame_id,
57    const GURL& url,
58    const content::WebPluginInfo& plugin) {
59  base::AutoLock auto_lock(lock_);
60  ProcessDetails* details = GetOrRegisterProcess(render_process_id);
61  OverriddenPlugin overridden_plugin;
62  overridden_plugin.render_frame_id = render_frame_id;
63  overridden_plugin.url = url;
64  overridden_plugin.plugin = plugin;
65  details->overridden_plugins.push_back(overridden_plugin);
66}
67
68void ChromePluginServiceFilter::RestrictPluginToProfileAndOrigin(
69    const base::FilePath& plugin_path,
70    Profile* profile,
71    const GURL& origin) {
72  base::AutoLock auto_lock(lock_);
73  restricted_plugins_[plugin_path] =
74      std::make_pair(PluginPrefs::GetForProfile(profile).get(), origin);
75}
76
77void ChromePluginServiceFilter::UnrestrictPlugin(
78    const base::FilePath& plugin_path) {
79  base::AutoLock auto_lock(lock_);
80  restricted_plugins_.erase(plugin_path);
81}
82
83bool ChromePluginServiceFilter::IsPluginRestricted(
84    const base::FilePath& plugin_path) {
85  base::AutoLock auto_lock(lock_);
86  return restricted_plugins_.find(plugin_path) != restricted_plugins_.end();
87}
88
89bool ChromePluginServiceFilter::IsPluginAvailable(
90    int render_process_id,
91    int render_frame_id,
92    const void* context,
93    const GURL& url,
94    const GURL& policy_url,
95    content::WebPluginInfo* plugin) {
96  base::AutoLock auto_lock(lock_);
97  const ProcessDetails* details = GetProcess(render_process_id);
98
99  // Check whether the plugin is overridden.
100  if (details) {
101    for (size_t i = 0; i < details->overridden_plugins.size(); ++i) {
102      if (details->overridden_plugins[i].render_frame_id == render_frame_id &&
103          (details->overridden_plugins[i].url == url ||
104           details->overridden_plugins[i].url.is_empty())) {
105
106        bool use = details->overridden_plugins[i].plugin.path == plugin->path;
107        if (use)
108          *plugin = details->overridden_plugins[i].plugin;
109        return use;
110      }
111    }
112  }
113
114  // Check whether the plugin is disabled.
115  ResourceContextMap::iterator prefs_it =
116      resource_context_map_.find(context);
117  if (prefs_it == resource_context_map_.end())
118    return false;
119
120  PluginPrefs* plugin_prefs = prefs_it->second.get();
121  if (!plugin_prefs->IsPluginEnabled(*plugin))
122    return false;
123
124  // Check whether the plugin is restricted to a URL.
125  RestrictedPluginMap::const_iterator it =
126      restricted_plugins_.find(plugin->path);
127  if (it != restricted_plugins_.end()) {
128    if (it->second.first != plugin_prefs)
129      return false;
130    const GURL& origin = it->second.second;
131    if (!origin.is_empty() &&
132        (policy_url.scheme() != origin.scheme() ||
133         policy_url.host() != origin.host() ||
134         policy_url.port() != origin.port())) {
135      return false;
136    }
137  }
138
139  return true;
140}
141
142bool ChromePluginServiceFilter::CanLoadPlugin(int render_process_id,
143                                              const base::FilePath& path) {
144  // The browser itself sometimes loads plug-ins to e.g. clear plug-in data.
145  // We always grant the browser permission.
146  if (!render_process_id)
147    return true;
148
149  base::AutoLock auto_lock(lock_);
150  const ProcessDetails* details = GetProcess(render_process_id);
151  if (!details)
152    return false;
153
154  if (details->authorized_plugins.find(path) ==
155          details->authorized_plugins.end() &&
156      details->authorized_plugins.find(base::FilePath()) ==
157          details->authorized_plugins.end()) {
158    return false;
159  }
160
161  return true;
162}
163
164void ChromePluginServiceFilter::AuthorizePlugin(
165    int render_process_id,
166    const base::FilePath& plugin_path) {
167  base::AutoLock auto_lock(lock_);
168  ProcessDetails* details = GetOrRegisterProcess(render_process_id);
169  details->authorized_plugins.insert(plugin_path);
170}
171
172void ChromePluginServiceFilter::AuthorizeAllPlugins(
173    content::WebContents* web_contents,
174    bool load_blocked,
175    const std::string& identifier) {
176  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
177  web_contents->ForEachFrame(base::Bind(&AuthorizeRenderer));
178  if (load_blocked) {
179    web_contents->SendToAllFrames(new ChromeViewMsg_LoadBlockedPlugins(
180        MSG_ROUTING_NONE, identifier));
181  }
182}
183
184ChromePluginServiceFilter::ChromePluginServiceFilter() {
185  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
186  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
187                 content::NotificationService::AllSources());
188  registrar_.Add(this, chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
189                 content::NotificationService::AllSources());
190}
191
192ChromePluginServiceFilter::~ChromePluginServiceFilter() {
193}
194
195void ChromePluginServiceFilter::Observe(
196    int type,
197    const content::NotificationSource& source,
198    const content::NotificationDetails& details) {
199  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200  switch (type) {
201    case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
202      int render_process_id =
203          content::Source<content::RenderProcessHost>(source).ptr()->GetID();
204
205      base::AutoLock auto_lock(lock_);
206      plugin_details_.erase(render_process_id);
207      break;
208    }
209    case chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED: {
210      Profile* profile = content::Source<Profile>(source).ptr();
211      PluginService::GetInstance()->PurgePluginListCache(profile, false);
212      if (profile && profile->HasOffTheRecordProfile()) {
213        PluginService::GetInstance()->PurgePluginListCache(
214            profile->GetOffTheRecordProfile(), false);
215      }
216      break;
217    }
218    default: {
219      NOTREACHED();
220    }
221  }
222}
223
224ChromePluginServiceFilter::ProcessDetails*
225ChromePluginServiceFilter::GetOrRegisterProcess(
226    int render_process_id) {
227  return &plugin_details_[render_process_id];
228}
229
230const ChromePluginServiceFilter::ProcessDetails*
231ChromePluginServiceFilter::GetProcess(
232    int render_process_id) const {
233  std::map<int, ProcessDetails>::const_iterator it =
234      plugin_details_.find(render_process_id);
235  if (it == plugin_details_.end())
236    return NULL;
237  return &it->second;
238}
239
240ChromePluginServiceFilter::OverriddenPlugin::OverriddenPlugin()
241    : render_frame_id(MSG_ROUTING_NONE) {
242}
243
244ChromePluginServiceFilter::OverriddenPlugin::~OverriddenPlugin() {
245}
246
247ChromePluginServiceFilter::ProcessDetails::ProcessDetails() {
248}
249
250ChromePluginServiceFilter::ProcessDetails::~ProcessDetails() {
251}
252
253