license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 1 | // 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 4 | |
[email protected] | fd49e2d | 2009-02-20 17:21:30 | [diff] [blame] | 5 | #include "build/build_config.h" |
| 6 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 7 | #include "chrome/browser/plugin_service.h" |
| 8 | |
[email protected] | d48f1e0c | 2009-02-12 20:57:54 | [diff] [blame] | 9 | #include "base/command_line.h" |
[email protected] | d70539de | 2009-06-24 22:17:06 | [diff] [blame] | 10 | #include "base/string_util.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 11 | #include "base/thread.h" |
[email protected] | b547fd4 | 2009-04-23 23:16:27 | [diff] [blame] | 12 | #include "base/waitable_event.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 13 | #include "chrome/browser/browser_process.h" |
| 14 | #include "chrome/browser/chrome_plugin_host.h" |
| 15 | #include "chrome/browser/chrome_thread.h" |
[email protected] | 894bb50 | 2009-05-21 22:39:57 | [diff] [blame] | 16 | #include "chrome/browser/extensions/extensions_service.h" |
[email protected] | 6eaddcc | 2009-02-23 21:03:04 | [diff] [blame] | 17 | #include "chrome/browser/plugin_process_host.h" |
[email protected] | 8c8657d6 | 2009-01-16 18:31:26 | [diff] [blame] | 18 | #include "chrome/browser/renderer_host/render_process_host.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 19 | #include "chrome/common/chrome_plugin_lib.h" |
[email protected] | d48f1e0c | 2009-02-12 20:57:54 | [diff] [blame] | 20 | #include "chrome/common/chrome_switches.h" |
[email protected] | 5b1a0e2 | 2009-05-26 19:00:58 | [diff] [blame] | 21 | #include "chrome/common/extensions/extension.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 22 | #include "chrome/common/logging_chrome.h" |
[email protected] | 894bb50 | 2009-05-21 22:39:57 | [diff] [blame] | 23 | #include "chrome/common/notification_type.h" |
| 24 | #include "chrome/common/notification_service.h" |
[email protected] | b547fd4 | 2009-04-23 23:16:27 | [diff] [blame] | 25 | #include "chrome/common/render_messages.h" |
| 26 | #include "webkit/glue/plugins/plugin_constants_win.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 27 | #include "webkit/glue/plugins/plugin_list.h" |
| 28 | |
| 29 | // static |
| 30 | PluginService* PluginService::GetInstance() { |
| 31 | return Singleton<PluginService>::get(); |
| 32 | } |
| 33 | |
| 34 | PluginService::PluginService() |
| 35 | : main_message_loop_(MessageLoop::current()), |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 36 | resource_dispatcher_host_(NULL), |
[email protected] | d70539de | 2009-06-24 22:17:06 | [diff] [blame] | 37 | ui_locale_(ASCIIToWide(g_browser_process->GetApplicationLocale())) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 38 | // Have the NPAPI plugin list search for Chrome plugins as well. |
| 39 | ChromePluginLib::RegisterPluginsWithNPAPI(); |
[email protected] | d48f1e0c | 2009-02-12 20:57:54 | [diff] [blame] | 40 | // 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] | 35fa6a2 | 2009-08-15 00:04:01 | [diff] [blame] | 43 | if (!path.empty()) { |
| 44 | NPAPI::PluginList::Singleton()->AddExtraPluginPath( |
| 45 | FilePath::FromWStringHack(path)); |
| 46 | } |
[email protected] | b547fd4 | 2009-04-23 23:16:27 | [diff] [blame] | 47 | |
| 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] | 894bb50 | 2009-05-21 22:39:57 | [diff] [blame] | 63 | |
| 64 | registrar_.Add(this, NotificationType::EXTENSIONS_LOADED, |
| 65 | NotificationService::AllSources()); |
| 66 | registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, |
| 67 | NotificationService::AllSources()); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | PluginService::~PluginService() { |
[email protected] | b547fd4 | 2009-04-23 23:16:27 | [diff] [blame] | 71 | #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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 78 | } |
| 79 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 80 | void PluginService::LoadChromePlugins( |
| 81 | ResourceDispatcherHost* resource_dispatcher_host) { |
| 82 | resource_dispatcher_host_ = resource_dispatcher_host; |
| 83 | ChromePluginLib::LoadChromePlugins(GetCPBrowserFuncsForBrowser()); |
| 84 | } |
| 85 | |
[email protected] | f7011fcb | 2009-01-28 21:54:32 | [diff] [blame] | 86 | void PluginService::SetChromePluginDataDir(const FilePath& data_dir) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 87 | chrome_plugin_data_dir_ = data_dir; |
| 88 | } |
| 89 | |
[email protected] | f7011fcb | 2009-01-28 21:54:32 | [diff] [blame] | 90 | const FilePath& PluginService::GetChromePluginDataDir() { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 91 | return chrome_plugin_data_dir_; |
| 92 | } |
| 93 | |
| 94 | const std::wstring& PluginService::GetUILocale() { |
| 95 | return ui_locale_; |
| 96 | } |
| 97 | |
[email protected] | 28ab7f9 | 2009-01-06 21:39:04 | [diff] [blame] | 98 | PluginProcessHost* PluginService::FindPluginProcess( |
| 99 | const FilePath& plugin_path) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 100 | DCHECK(MessageLoop::current() == |
| 101 | ChromeThread::GetMessageLoop(ChromeThread::IO)); |
| 102 | |
[email protected] | 28ab7f9 | 2009-01-06 21:39:04 | [diff] [blame] | 103 | if (plugin_path.value().empty()) { |
| 104 | NOTREACHED() << "should only be called if we have a plugin to load"; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 105 | return NULL; |
| 106 | } |
| 107 | |
[email protected] | 6dffde32 | 2009-02-18 03:47:48 | [diff] [blame] | 108 | for (ChildProcessHost::Iterator iter(ChildProcessInfo::PLUGIN_PROCESS); |
[email protected] | a436d92 | 2009-02-13 23:16:42 | [diff] [blame] | 109 | !iter.Done(); ++iter) { |
| 110 | PluginProcessHost* plugin = static_cast<PluginProcessHost*>(*iter); |
| 111 | if (plugin->info().path == plugin_path) |
| 112 | return plugin; |
| 113 | } |
| 114 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 115 | return NULL; |
| 116 | } |
| 117 | |
| 118 | PluginProcessHost* PluginService::FindOrStartPluginProcess( |
[email protected] | 28ab7f9 | 2009-01-06 21:39:04 | [diff] [blame] | 119 | const FilePath& plugin_path, |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 120 | const std::string& clsid) { |
| 121 | DCHECK(MessageLoop::current() == |
| 122 | ChromeThread::GetMessageLoop(ChromeThread::IO)); |
| 123 | |
[email protected] | 28ab7f9 | 2009-01-06 21:39:04 | [diff] [blame] | 124 | PluginProcessHost *plugin_host = FindPluginProcess(plugin_path); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 125 | if (plugin_host) |
| 126 | return plugin_host; |
| 127 | |
[email protected] | a27a938 | 2009-02-11 23:55:10 | [diff] [blame] | 128 | WebPluginInfo info; |
[email protected] | 35fa6a2 | 2009-08-15 00:04:01 | [diff] [blame] | 129 | if (!NPAPI::PluginList::Singleton()->GetPluginInfoByPath( |
| 130 | plugin_path, &info)) { |
[email protected] | a27a938 | 2009-02-11 23:55:10 | [diff] [blame] | 131 | DCHECK(false); |
| 132 | return NULL; |
| 133 | } |
| 134 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 135 | // This plugin isn't loaded by any plugin process, so create a new process. |
[email protected] | dabe607 | 2009-03-17 00:52:35 | [diff] [blame] | 136 | plugin_host = new PluginProcessHost(); |
[email protected] | a27a938 | 2009-02-11 23:55:10 | [diff] [blame] | 137 | if (!plugin_host->Init(info, clsid, ui_locale_)) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 138 | DCHECK(false); // Init is not expected to fail |
| 139 | delete plugin_host; |
| 140 | return NULL; |
| 141 | } |
[email protected] | a436d92 | 2009-02-13 23:16:42 | [diff] [blame] | 142 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 143 | 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 | |
| 151 | void 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] | 9dd9e838 | 2009-06-05 18:23:21 | [diff] [blame] | 157 | // 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] | 28ab7f9 | 2009-01-06 21:39:04 | [diff] [blame] | 161 | PluginProcessHost* plugin_host = FindOrStartPluginProcess(plugin_path, clsid); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 162 | if (plugin_host) { |
| 163 | plugin_host->OpenChannelToPlugin(renderer_msg_filter, mime_type, reply_msg); |
| 164 | } else { |
| 165 | PluginProcessHost::ReplyToRenderer(renderer_msg_filter, |
[email protected] | d2e884d | 2009-06-22 20:37:52 | [diff] [blame] | 166 | IPC::ChannelHandle(), |
[email protected] | 690a99c | 2009-01-06 16:48:45 | [diff] [blame] | 167 | FilePath(), |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 168 | reply_msg); |
| 169 | } |
| 170 | } |
| 171 | |
[email protected] | 690a99c | 2009-01-06 16:48:45 | [diff] [blame] | 172 | FilePath PluginService::GetPluginPath(const GURL& url, |
[email protected] | 9dd9e838 | 2009-06-05 18:23:21 | [diff] [blame] | 173 | const GURL& policy_url, |
[email protected] | 690a99c | 2009-01-06 16:48:45 | [diff] [blame] | 174 | const std::string& mime_type, |
| 175 | const std::string& clsid, |
| 176 | std::string* actual_mime_type) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 177 | bool allow_wildcard = true; |
| 178 | WebPluginInfo info; |
[email protected] | 9dd9e838 | 2009-06-05 18:23:21 | [diff] [blame] | 179 | 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 187 | } |
| 188 | |
[email protected] | b547fd4 | 2009-04-23 23:16:27 | [diff] [blame] | 189 | void 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] | 35fa6a2 | 2009-08-15 00:04:01 | [diff] [blame] | 197 | NPAPI::PluginList::Singleton()->ResetPluginsLoaded(); |
[email protected] | b547fd4 | 2009-04-23 23:16:27 | [diff] [blame] | 198 | |
[email protected] | 9de09f8 | 2009-08-17 20:13:53 | [diff] [blame^] | 199 | for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); |
| 200 | !it.IsAtEnd(); it.Advance()) { |
| 201 | it.GetCurrentValue()->Send(new ViewMsg_PurgePluginListCache()); |
[email protected] | b547fd4 | 2009-04-23 23:16:27 | [diff] [blame] | 202 | } |
[email protected] | 9de09f8 | 2009-08-17 20:13:53 | [diff] [blame^] | 203 | #endif // defined(OS_WIN) |
[email protected] | b547fd4 | 2009-04-23 23:16:27 | [diff] [blame] | 204 | } |
[email protected] | 894bb50 | 2009-05-21 22:39:57 | [diff] [blame] | 205 | |
| 206 | void 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] | c533bb2 | 2009-06-03 19:06:11 | [diff] [blame] | 215 | |
[email protected] | 894bb50 | 2009-05-21 22:39:57 | [diff] [blame] | 216 | ExtensionList* extensions = Details<ExtensionList>(details).ptr(); |
| 217 | for (ExtensionList::iterator extension = extensions->begin(); |
[email protected] | c533bb2 | 2009-06-03 19:06:11 | [diff] [blame] | 218 | 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] | 35fa6a2 | 2009-08-15 00:04:01 | [diff] [blame] | 221 | NPAPI::PluginList::Singleton()->ResetPluginsLoaded(); |
| 222 | NPAPI::PluginList::Singleton()->AddExtraPluginPath(plugin.path); |
[email protected] | 9dd9e838 | 2009-06-05 18:23:21 | [diff] [blame] | 223 | if (!plugin.is_public) |
| 224 | private_plugins_[plugin.path] = (*extension)->url(); |
[email protected] | c533bb2 | 2009-06-03 19:06:11 | [diff] [blame] | 225 | } |
| 226 | } |
[email protected] | 894bb50 | 2009-05-21 22:39:57 | [diff] [blame] | 227 | 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] | 9dd9e838 | 2009-06-05 18:23:21 | [diff] [blame] | 241 | |
| 242 | bool 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 | } |