blob: c3e1b1475f16e5a767d08e50205c8f040666c4c5 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commit09911bf2008-07-26 23:55:294
[email protected]fd49e2d2009-02-20 17:21:305#include "build/build_config.h"
6
initial.commit09911bf2008-07-26 23:55:297#include "chrome/browser/plugin_service.h"
8
[email protected]d48f1e0c2009-02-12 20:57:549#include "base/command_line.h"
[email protected]d70539de2009-06-24 22:17:0610#include "base/string_util.h"
initial.commit09911bf2008-07-26 23:55:2911#include "base/thread.h"
[email protected]b547fd42009-04-23 23:16:2712#include "base/waitable_event.h"
initial.commit09911bf2008-07-26 23:55:2913#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chrome_plugin_host.h"
15#include "chrome/browser/chrome_thread.h"
[email protected]894bb502009-05-21 22:39:5716#include "chrome/browser/extensions/extensions_service.h"
[email protected]6eaddcc2009-02-23 21:03:0417#include "chrome/browser/plugin_process_host.h"
[email protected]8c8657d62009-01-16 18:31:2618#include "chrome/browser/renderer_host/render_process_host.h"
initial.commit09911bf2008-07-26 23:55:2919#include "chrome/common/chrome_plugin_lib.h"
[email protected]d48f1e0c2009-02-12 20:57:5420#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5821#include "chrome/common/extensions/extension.h"
initial.commit09911bf2008-07-26 23:55:2922#include "chrome/common/logging_chrome.h"
[email protected]894bb502009-05-21 22:39:5723#include "chrome/common/notification_type.h"
24#include "chrome/common/notification_service.h"
[email protected]b547fd42009-04-23 23:16:2725#include "chrome/common/render_messages.h"
26#include "webkit/glue/plugins/plugin_constants_win.h"
initial.commit09911bf2008-07-26 23:55:2927#include "webkit/glue/plugins/plugin_list.h"
28
29// static
30PluginService* PluginService::GetInstance() {
31 return Singleton<PluginService>::get();
32}
33
34PluginService::PluginService()
35 : main_message_loop_(MessageLoop::current()),
initial.commit09911bf2008-07-26 23:55:2936 resource_dispatcher_host_(NULL),
[email protected]d70539de2009-06-24 22:17:0637 ui_locale_(ASCIIToWide(g_browser_process->GetApplicationLocale())) {
initial.commit09911bf2008-07-26 23:55:2938 // Have the NPAPI plugin list search for Chrome plugins as well.
39 ChromePluginLib::RegisterPluginsWithNPAPI();
[email protected]d48f1e0c2009-02-12 20:57:5440 // Load the one specified on the command line as well.
41 const CommandLine* command_line = CommandLine::ForCurrentProcess();
42 std::wstring path = command_line->GetSwitchValue(switches::kLoadPlugin);
[email protected]35fa6a22009-08-15 00:04:0143 if (!path.empty()) {
44 NPAPI::PluginList::Singleton()->AddExtraPluginPath(
45 FilePath::FromWStringHack(path));
46 }
[email protected]b547fd42009-04-23 23:16:2747
48#if defined(OS_WIN)
49 hkcu_key_.Create(
50 HKEY_CURRENT_USER, kRegistryMozillaPlugins, KEY_NOTIFY);
51 hklm_key_.Create(
52 HKEY_LOCAL_MACHINE, kRegistryMozillaPlugins, KEY_NOTIFY);
53 if (hkcu_key_.StartWatching()) {
54 hkcu_event_.reset(new base::WaitableEvent(hkcu_key_.watch_event()));
55 hkcu_watcher_.StartWatching(hkcu_event_.get(), this);
56 }
57
58 if (hklm_key_.StartWatching()) {
59 hklm_event_.reset(new base::WaitableEvent(hklm_key_.watch_event()));
60 hklm_watcher_.StartWatching(hklm_event_.get(), this);
61 }
62#endif
[email protected]894bb502009-05-21 22:39:5763
64 registrar_.Add(this, NotificationType::EXTENSIONS_LOADED,
65 NotificationService::AllSources());
66 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
67 NotificationService::AllSources());
initial.commit09911bf2008-07-26 23:55:2968}
69
70PluginService::~PluginService() {
[email protected]b547fd42009-04-23 23:16:2771#if defined(OS_WIN)
72 // Release the events since they're owned by RegKey, not WaitableEvent.
73 hkcu_watcher_.StopWatching();
74 hklm_watcher_.StopWatching();
75 hkcu_event_->Release();
76 hklm_event_->Release();
77#endif
initial.commit09911bf2008-07-26 23:55:2978}
79
initial.commit09911bf2008-07-26 23:55:2980void PluginService::LoadChromePlugins(
81 ResourceDispatcherHost* resource_dispatcher_host) {
82 resource_dispatcher_host_ = resource_dispatcher_host;
83 ChromePluginLib::LoadChromePlugins(GetCPBrowserFuncsForBrowser());
84}
85
[email protected]f7011fcb2009-01-28 21:54:3286void PluginService::SetChromePluginDataDir(const FilePath& data_dir) {
initial.commit09911bf2008-07-26 23:55:2987 chrome_plugin_data_dir_ = data_dir;
88}
89
[email protected]f7011fcb2009-01-28 21:54:3290const FilePath& PluginService::GetChromePluginDataDir() {
initial.commit09911bf2008-07-26 23:55:2991 return chrome_plugin_data_dir_;
92}
93
94const std::wstring& PluginService::GetUILocale() {
95 return ui_locale_;
96}
97
[email protected]28ab7f92009-01-06 21:39:0498PluginProcessHost* PluginService::FindPluginProcess(
99 const FilePath& plugin_path) {
initial.commit09911bf2008-07-26 23:55:29100 DCHECK(MessageLoop::current() ==
101 ChromeThread::GetMessageLoop(ChromeThread::IO));
102
[email protected]28ab7f92009-01-06 21:39:04103 if (plugin_path.value().empty()) {
104 NOTREACHED() << "should only be called if we have a plugin to load";
initial.commit09911bf2008-07-26 23:55:29105 return NULL;
106 }
107
[email protected]6dffde322009-02-18 03:47:48108 for (ChildProcessHost::Iterator iter(ChildProcessInfo::PLUGIN_PROCESS);
[email protected]a436d922009-02-13 23:16:42109 !iter.Done(); ++iter) {
110 PluginProcessHost* plugin = static_cast<PluginProcessHost*>(*iter);
111 if (plugin->info().path == plugin_path)
112 return plugin;
113 }
114
initial.commit09911bf2008-07-26 23:55:29115 return NULL;
116}
117
118PluginProcessHost* PluginService::FindOrStartPluginProcess(
[email protected]28ab7f92009-01-06 21:39:04119 const FilePath& plugin_path,
initial.commit09911bf2008-07-26 23:55:29120 const std::string& clsid) {
121 DCHECK(MessageLoop::current() ==
122 ChromeThread::GetMessageLoop(ChromeThread::IO));
123
[email protected]28ab7f92009-01-06 21:39:04124 PluginProcessHost *plugin_host = FindPluginProcess(plugin_path);
initial.commit09911bf2008-07-26 23:55:29125 if (plugin_host)
126 return plugin_host;
127
[email protected]a27a9382009-02-11 23:55:10128 WebPluginInfo info;
[email protected]35fa6a22009-08-15 00:04:01129 if (!NPAPI::PluginList::Singleton()->GetPluginInfoByPath(
130 plugin_path, &info)) {
[email protected]a27a9382009-02-11 23:55:10131 DCHECK(false);
132 return NULL;
133 }
134
initial.commit09911bf2008-07-26 23:55:29135 // This plugin isn't loaded by any plugin process, so create a new process.
[email protected]dabe6072009-03-17 00:52:35136 plugin_host = new PluginProcessHost();
[email protected]a27a9382009-02-11 23:55:10137 if (!plugin_host->Init(info, clsid, ui_locale_)) {
initial.commit09911bf2008-07-26 23:55:29138 DCHECK(false); // Init is not expected to fail
139 delete plugin_host;
140 return NULL;
141 }
[email protected]a436d922009-02-13 23:16:42142
initial.commit09911bf2008-07-26 23:55:29143 return plugin_host;
144
145 // TODO(jabdelmalek): adding a new channel means we can have one less
146 // renderer process (since each child process uses one handle in the
147 // IPC thread and main thread's WaitForMultipleObjects call). Limit the
148 // number of plugin processes.
149}
150
151void PluginService::OpenChannelToPlugin(
152 ResourceMessageFilter* renderer_msg_filter, const GURL& url,
153 const std::string& mime_type, const std::string& clsid,
154 const std::wstring& locale, IPC::Message* reply_msg) {
155 DCHECK(MessageLoop::current() ==
156 ChromeThread::GetMessageLoop(ChromeThread::IO));
[email protected]9dd9e8382009-06-05 18:23:21157 // We don't need a policy URL here because that was already checked by a
158 // previous call to GetPluginPath.
159 GURL policy_url;
160 FilePath plugin_path = GetPluginPath(url, policy_url, mime_type, clsid, NULL);
[email protected]28ab7f92009-01-06 21:39:04161 PluginProcessHost* plugin_host = FindOrStartPluginProcess(plugin_path, clsid);
initial.commit09911bf2008-07-26 23:55:29162 if (plugin_host) {
163 plugin_host->OpenChannelToPlugin(renderer_msg_filter, mime_type, reply_msg);
164 } else {
165 PluginProcessHost::ReplyToRenderer(renderer_msg_filter,
[email protected]d2e884d2009-06-22 20:37:52166 IPC::ChannelHandle(),
[email protected]690a99c2009-01-06 16:48:45167 FilePath(),
initial.commit09911bf2008-07-26 23:55:29168 reply_msg);
169 }
170}
171
[email protected]690a99c2009-01-06 16:48:45172FilePath PluginService::GetPluginPath(const GURL& url,
[email protected]9dd9e8382009-06-05 18:23:21173 const GURL& policy_url,
[email protected]690a99c2009-01-06 16:48:45174 const std::string& mime_type,
175 const std::string& clsid,
176 std::string* actual_mime_type) {
initial.commit09911bf2008-07-26 23:55:29177 bool allow_wildcard = true;
178 WebPluginInfo info;
[email protected]9dd9e8382009-06-05 18:23:21179 if (NPAPI::PluginList::Singleton()->GetPluginInfo(url, mime_type, clsid,
180 allow_wildcard, &info,
181 actual_mime_type) &&
182 PluginAllowedForURL(info.path, policy_url)) {
183 return info.path;
184 }
185
186 return FilePath();
initial.commit09911bf2008-07-26 23:55:29187}
188
[email protected]b547fd42009-04-23 23:16:27189void PluginService::OnWaitableEventSignaled(base::WaitableEvent* waitable_event) {
190#if defined(OS_WIN)
191 if (waitable_event == hkcu_event_.get()) {
192 hkcu_key_.StartWatching();
193 } else {
194 hklm_key_.StartWatching();
195 }
196
[email protected]35fa6a22009-08-15 00:04:01197 NPAPI::PluginList::Singleton()->ResetPluginsLoaded();
[email protected]b547fd42009-04-23 23:16:27198
[email protected]9de09f82009-08-17 20:13:53199 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
200 !it.IsAtEnd(); it.Advance()) {
201 it.GetCurrentValue()->Send(new ViewMsg_PurgePluginListCache());
[email protected]b547fd42009-04-23 23:16:27202 }
[email protected]9de09f82009-08-17 20:13:53203#endif // defined(OS_WIN)
[email protected]b547fd42009-04-23 23:16:27204}
[email protected]894bb502009-05-21 22:39:57205
206void PluginService::Observe(NotificationType type,
207 const NotificationSource& source,
208 const NotificationDetails& details) {
209 switch (type.value) {
210 case NotificationType::EXTENSIONS_LOADED: {
211 // TODO(mpcomplete): We also need to force a renderer to refresh its
212 // cache of the plugin list when we inject user scripts, since it could
213 // have a stale version by the time extensions are loaded.
214 // See: http://code.google.com/p/chromium/issues/detail?id=12306
[email protected]c533bb22009-06-03 19:06:11215
[email protected]894bb502009-05-21 22:39:57216 ExtensionList* extensions = Details<ExtensionList>(details).ptr();
217 for (ExtensionList::iterator extension = extensions->begin();
[email protected]c533bb22009-06-03 19:06:11218 extension != extensions->end(); ++extension) {
219 for (size_t i = 0; i < (*extension)->plugins().size(); ++i ) {
220 const Extension::PluginInfo& plugin = (*extension)->plugins()[i];
[email protected]35fa6a22009-08-15 00:04:01221 NPAPI::PluginList::Singleton()->ResetPluginsLoaded();
222 NPAPI::PluginList::Singleton()->AddExtraPluginPath(plugin.path);
[email protected]9dd9e8382009-06-05 18:23:21223 if (!plugin.is_public)
224 private_plugins_[plugin.path] = (*extension)->url();
[email protected]c533bb22009-06-03 19:06:11225 }
226 }
[email protected]894bb502009-05-21 22:39:57227 break;
228 }
229
230 case NotificationType::EXTENSION_UNLOADED: {
231 // TODO(aa): Implement this. Also, will it be possible to delete the
232 // extension folder if this isn't unloaded?
233 // See: http://code.google.com/p/chromium/issues/detail?id=12306
234 break;
235 }
236
237 default:
238 DCHECK(false);
239 }
240}
[email protected]9dd9e8382009-06-05 18:23:21241
242bool PluginService::PluginAllowedForURL(const FilePath& plugin_path,
243 const GURL& url) {
244 if (url.is_empty())
245 return true; // Caller wants all plugins.
246
247 PrivatePluginMap::iterator it = private_plugins_.find(plugin_path);
248 if (it == private_plugins_.end())
249 return true; // This plugin is not private, so it's allowed everywhere.
250
251 // We do a dumb compare of scheme and host, rather than using the domain
252 // service, since we only care about this for extensions.
253 const GURL& required_url = it->second;
254 return (url.scheme() == required_url.scheme() &&
255 url.host() == required_url.host());
256}