• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "content/browser/plugin_service_impl.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/files/file_path.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/metrics/histogram.h"
14 #include "base/path_service.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/threading/thread.h"
19 #include "content/browser/ppapi_plugin_process_host.h"
20 #include "content/browser/renderer_host/render_process_host_impl.h"
21 #include "content/browser/renderer_host/render_view_host_impl.h"
22 #include "content/common/pepper_plugin_list.h"
23 #include "content/common/plugin_list.h"
24 #include "content/common/view_messages.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/content_browser_client.h"
27 #include "content/public/browser/plugin_service_filter.h"
28 #include "content/public/browser/resource_context.h"
29 #include "content/public/common/content_constants.h"
30 #include "content/public/common/content_switches.h"
31 #include "content/public/common/process_type.h"
32 #include "content/public/common/webplugininfo.h"
33 
34 #if defined(OS_WIN)
35 #include "content/common/plugin_constants_win.h"
36 #include "ui/gfx/win/hwnd_util.h"
37 #endif
38 
39 #if defined(OS_POSIX)
40 #include "content/browser/plugin_loader_posix.h"
41 #endif
42 
43 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
44 using ::base::FilePathWatcher;
45 #endif
46 
47 namespace content {
48 namespace {
49 
50 // This enum is used to collect Flash usage data.
51 enum FlashUsage {
52   // Number of browser processes that have started at least one NPAPI Flash
53   // process during their lifetime.
54   START_NPAPI_FLASH_AT_LEAST_ONCE,
55   // Number of browser processes that have started at least one PPAPI Flash
56   // process during their lifetime.
57   START_PPAPI_FLASH_AT_LEAST_ONCE,
58   // Total number of browser processes.
59   TOTAL_BROWSER_PROCESSES,
60   FLASH_USAGE_ENUM_COUNT
61 };
62 
LoadPluginListInProcess()63 bool LoadPluginListInProcess() {
64 #if defined(OS_WIN)
65   return true;
66 #else
67   // If on POSIX, we don't want to load the list of NPAPI plugins in-process as
68   // that causes instability.
69 
70   // Can't load the plugins on the utility thread when in single process mode
71   // since that requires GTK which can only be used on the main thread.
72   if (RenderProcessHost::run_renderer_in_process())
73     return true;
74 
75   return !PluginService::GetInstance()->NPAPIPluginsSupported();
76 #endif
77 }
78 
79 // Callback set on the PluginList to assert that plugin loading happens on the
80 // correct thread.
WillLoadPluginsCallback(base::SequencedWorkerPool::SequenceToken token)81 void WillLoadPluginsCallback(
82     base::SequencedWorkerPool::SequenceToken token) {
83   if (LoadPluginListInProcess()) {
84     CHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
85         token));
86   } else {
87     CHECK(false) << "Plugin loading should happen out-of-process.";
88   }
89 }
90 
91 #if defined(OS_MACOSX)
NotifyPluginsOfActivation()92 void NotifyPluginsOfActivation() {
93   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
94 
95   for (PluginProcessHostIterator iter; !iter.Done(); ++iter)
96     iter->OnAppActivation();
97 }
98 #endif
99 
100 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
NotifyPluginDirChanged(const base::FilePath & path,bool error)101 void NotifyPluginDirChanged(const base::FilePath& path, bool error) {
102   if (error) {
103     // TODO(pastarmovj): Add some sensible error handling. Maybe silently
104     // stopping the watcher would be enough. Or possibly restart it.
105     NOTREACHED();
106     return;
107   }
108   VLOG(1) << "Watched path changed: " << path.value();
109   // Make the plugin list update itself
110   PluginList::Singleton()->RefreshPlugins();
111   BrowserThread::PostTask(
112       BrowserThread::UI, FROM_HERE,
113       base::Bind(&PluginService::PurgePluginListCache,
114                  static_cast<BrowserContext*>(NULL), false));
115 }
116 #endif
117 
ForwardCallback(base::MessageLoopProxy * target_loop,const PluginService::GetPluginsCallback & callback,const std::vector<WebPluginInfo> & plugins)118 void ForwardCallback(base::MessageLoopProxy* target_loop,
119                      const PluginService::GetPluginsCallback& callback,
120                      const std::vector<WebPluginInfo>& plugins) {
121   target_loop->PostTask(FROM_HERE, base::Bind(callback, plugins));
122 }
123 
124 }  // namespace
125 
126 // static
GetInstance()127 PluginService* PluginService::GetInstance() {
128   return PluginServiceImpl::GetInstance();
129 }
130 
PurgePluginListCache(BrowserContext * browser_context,bool reload_pages)131 void PluginService::PurgePluginListCache(BrowserContext* browser_context,
132                                          bool reload_pages) {
133   for (RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator();
134        !it.IsAtEnd(); it.Advance()) {
135     RenderProcessHost* host = it.GetCurrentValue();
136     if (!browser_context || host->GetBrowserContext() == browser_context)
137       host->Send(new ViewMsg_PurgePluginListCache(reload_pages));
138   }
139 }
140 
141 // static
GetInstance()142 PluginServiceImpl* PluginServiceImpl::GetInstance() {
143   return Singleton<PluginServiceImpl>::get();
144 }
145 
PluginServiceImpl()146 PluginServiceImpl::PluginServiceImpl()
147     : filter_(NULL) {
148   // Collect the total number of browser processes (which create
149   // PluginServiceImpl objects, to be precise). The number is used to normalize
150   // the number of processes which start at least one NPAPI/PPAPI Flash process.
151   static bool counted = false;
152   if (!counted) {
153     counted = true;
154     UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage", TOTAL_BROWSER_PROCESSES,
155                               FLASH_USAGE_ENUM_COUNT);
156   }
157 }
158 
~PluginServiceImpl()159 PluginServiceImpl::~PluginServiceImpl() {
160 #if defined(OS_WIN)
161   // Release the events since they're owned by RegKey, not WaitableEvent.
162   hkcu_watcher_.StopWatching();
163   hklm_watcher_.StopWatching();
164   if (hkcu_event_)
165     hkcu_event_->Release();
166   if (hklm_event_)
167     hklm_event_->Release();
168 #endif
169   // Make sure no plugin channel requests have been leaked.
170   DCHECK(pending_plugin_clients_.empty());
171 }
172 
Init()173 void PluginServiceImpl::Init() {
174   plugin_list_token_ = BrowserThread::GetBlockingPool()->GetSequenceToken();
175   PluginList::Singleton()->set_will_load_plugins_callback(
176       base::Bind(&WillLoadPluginsCallback, plugin_list_token_));
177 
178   RegisterPepperPlugins();
179 
180   // Load any specified on the command line as well.
181   const base::CommandLine* command_line =
182       base::CommandLine::ForCurrentProcess();
183   base::FilePath path =
184       command_line->GetSwitchValuePath(switches::kLoadPlugin);
185   if (!path.empty())
186     AddExtraPluginPath(path);
187   path = command_line->GetSwitchValuePath(switches::kExtraPluginDir);
188   if (!path.empty())
189     PluginList::Singleton()->AddExtraPluginDir(path);
190 
191   if (command_line->HasSwitch(switches::kDisablePluginsDiscovery))
192     PluginList::Singleton()->DisablePluginsDiscovery();
193 }
194 
StartWatchingPlugins()195 void PluginServiceImpl::StartWatchingPlugins() {
196   // Start watching for changes in the plugin list. This means watching
197   // for changes in the Windows registry keys and on both Windows and POSIX
198   // watch for changes in the paths that are expected to contain plugins.
199 #if defined(OS_WIN)
200   if (hkcu_key_.Create(HKEY_CURRENT_USER,
201                        kRegistryMozillaPlugins,
202                        KEY_NOTIFY) == ERROR_SUCCESS) {
203     if (hkcu_key_.StartWatching() == ERROR_SUCCESS) {
204       hkcu_event_.reset(new base::WaitableEvent(hkcu_key_.watch_event()));
205       base::WaitableEventWatcher::EventCallback callback =
206             base::Bind(&PluginServiceImpl::OnWaitableEventSignaled,
207                        base::Unretained(this));
208       hkcu_watcher_.StartWatching(hkcu_event_.get(), callback);
209     }
210   }
211   if (hklm_key_.Create(HKEY_LOCAL_MACHINE,
212                        kRegistryMozillaPlugins,
213                        KEY_NOTIFY) == ERROR_SUCCESS) {
214     if (hklm_key_.StartWatching() == ERROR_SUCCESS) {
215       hklm_event_.reset(new base::WaitableEvent(hklm_key_.watch_event()));
216       base::WaitableEventWatcher::EventCallback callback =
217             base::Bind(&PluginServiceImpl::OnWaitableEventSignaled,
218                        base::Unretained(this));
219       hklm_watcher_.StartWatching(hklm_event_.get(), callback);
220     }
221   }
222 #endif
223 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
224 // On ChromeOS the user can't install plugins anyway and on Windows all
225 // important plugins register themselves in the registry so no need to do that.
226 
227   // Get the list of all paths for registering the FilePathWatchers
228   // that will track and if needed reload the list of plugins on runtime.
229   std::vector<base::FilePath> plugin_dirs;
230   PluginList::Singleton()->GetPluginDirectories(&plugin_dirs);
231 
232   for (size_t i = 0; i < plugin_dirs.size(); ++i) {
233     // FilePathWatcher can not handle non-absolute paths under windows.
234     // We don't watch for file changes in windows now but if this should ever
235     // be extended to Windows these lines might save some time of debugging.
236 #if defined(OS_WIN)
237     if (!plugin_dirs[i].IsAbsolute())
238       continue;
239 #endif
240     FilePathWatcher* watcher = new FilePathWatcher();
241     VLOG(1) << "Watching for changes in: " << plugin_dirs[i].value();
242     BrowserThread::PostTask(
243         BrowserThread::FILE, FROM_HERE,
244         base::Bind(&PluginServiceImpl::RegisterFilePathWatcher, watcher,
245                    plugin_dirs[i]));
246     file_watchers_.push_back(watcher);
247   }
248 #endif
249 }
250 
FindNpapiPluginProcess(const base::FilePath & plugin_path)251 PluginProcessHost* PluginServiceImpl::FindNpapiPluginProcess(
252     const base::FilePath& plugin_path) {
253   for (PluginProcessHostIterator iter; !iter.Done(); ++iter) {
254     if (iter->info().path == plugin_path)
255       return *iter;
256   }
257 
258   return NULL;
259 }
260 
FindPpapiPluginProcess(const base::FilePath & plugin_path,const base::FilePath & profile_data_directory)261 PpapiPluginProcessHost* PluginServiceImpl::FindPpapiPluginProcess(
262     const base::FilePath& plugin_path,
263     const base::FilePath& profile_data_directory) {
264   for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) {
265     if (iter->plugin_path() == plugin_path &&
266         iter->profile_data_directory() == profile_data_directory) {
267       return *iter;
268     }
269   }
270   return NULL;
271 }
272 
FindPpapiBrokerProcess(const base::FilePath & broker_path)273 PpapiPluginProcessHost* PluginServiceImpl::FindPpapiBrokerProcess(
274     const base::FilePath& broker_path) {
275   for (PpapiBrokerProcessHostIterator iter; !iter.Done(); ++iter) {
276     if (iter->plugin_path() == broker_path)
277       return *iter;
278   }
279 
280   return NULL;
281 }
282 
FindOrStartNpapiPluginProcess(int render_process_id,const base::FilePath & plugin_path)283 PluginProcessHost* PluginServiceImpl::FindOrStartNpapiPluginProcess(
284     int render_process_id,
285     const base::FilePath& plugin_path) {
286   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
287 
288   if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path))
289     return NULL;
290 
291   PluginProcessHost* plugin_host = FindNpapiPluginProcess(plugin_path);
292   if (plugin_host)
293     return plugin_host;
294 
295   WebPluginInfo info;
296   if (!GetPluginInfoByPath(plugin_path, &info)) {
297     return NULL;
298   }
299 
300   // Record when NPAPI Flash process is started for the first time.
301   static bool counted = false;
302   if (!counted && base::UTF16ToUTF8(info.name) == kFlashPluginName) {
303     counted = true;
304     UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage",
305                               START_NPAPI_FLASH_AT_LEAST_ONCE,
306                               FLASH_USAGE_ENUM_COUNT);
307   }
308 #if defined(OS_CHROMEOS)
309   // TODO(ihf): Move to an earlier place once crbug.com/314301 is fixed. For now
310   // we still want Plugin.FlashUsage recorded if we end up here.
311   LOG(WARNING) << "Refusing to start npapi plugin on ChromeOS.";
312   return NULL;
313 #endif
314   // This plugin isn't loaded by any plugin process, so create a new process.
315   scoped_ptr<PluginProcessHost> new_host(new PluginProcessHost());
316   if (!new_host->Init(info)) {
317     NOTREACHED();  // Init is not expected to fail.
318     return NULL;
319   }
320   return new_host.release();
321 }
322 
FindOrStartPpapiPluginProcess(int render_process_id,const base::FilePath & plugin_path,const base::FilePath & profile_data_directory)323 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess(
324     int render_process_id,
325     const base::FilePath& plugin_path,
326     const base::FilePath& profile_data_directory) {
327   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
328 
329   if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path)) {
330     VLOG(1) << "Unable to load ppapi plugin: " << plugin_path.MaybeAsASCII();
331     return NULL;
332   }
333 
334   PpapiPluginProcessHost* plugin_host =
335       FindPpapiPluginProcess(plugin_path, profile_data_directory);
336   if (plugin_host)
337     return plugin_host;
338 
339   // Validate that the plugin is actually registered.
340   PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path);
341   if (!info) {
342     VLOG(1) << "Unable to find ppapi plugin registration for: "
343             << plugin_path.MaybeAsASCII();
344     return NULL;
345   }
346 
347   // Record when PPAPI Flash process is started for the first time.
348   static bool counted = false;
349   if (!counted && info->name == kFlashPluginName) {
350     counted = true;
351     UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage",
352                               START_PPAPI_FLASH_AT_LEAST_ONCE,
353                               FLASH_USAGE_ENUM_COUNT);
354   }
355 
356   // This plugin isn't loaded by any plugin process, so create a new process.
357   plugin_host = PpapiPluginProcessHost::CreatePluginHost(
358       *info, profile_data_directory);
359   if (!plugin_host) {
360     VLOG(1) << "Unable to create ppapi plugin process for: "
361             << plugin_path.MaybeAsASCII();
362   }
363 
364   return plugin_host;
365 }
366 
FindOrStartPpapiBrokerProcess(int render_process_id,const base::FilePath & plugin_path)367 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiBrokerProcess(
368     int render_process_id,
369     const base::FilePath& plugin_path) {
370   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
371 
372   if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path))
373     return NULL;
374 
375   PpapiPluginProcessHost* plugin_host = FindPpapiBrokerProcess(plugin_path);
376   if (plugin_host)
377     return plugin_host;
378 
379   // Validate that the plugin is actually registered.
380   PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path);
381   if (!info)
382     return NULL;
383 
384   // TODO(ddorwin): Uncomment once out of process is supported.
385   // DCHECK(info->is_out_of_process);
386 
387   // This broker isn't loaded by any broker process, so create a new process.
388   return PpapiPluginProcessHost::CreateBrokerHost(*info);
389 }
390 
OpenChannelToNpapiPlugin(int render_process_id,int render_frame_id,const GURL & url,const GURL & page_url,const std::string & mime_type,PluginProcessHost::Client * client)391 void PluginServiceImpl::OpenChannelToNpapiPlugin(
392     int render_process_id,
393     int render_frame_id,
394     const GURL& url,
395     const GURL& page_url,
396     const std::string& mime_type,
397     PluginProcessHost::Client* client) {
398   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
399   DCHECK(!ContainsKey(pending_plugin_clients_, client));
400   pending_plugin_clients_.insert(client);
401 
402   // Make sure plugins are loaded if necessary.
403   PluginServiceFilterParams params = {
404     render_process_id,
405     render_frame_id,
406     page_url,
407     client->GetResourceContext()
408   };
409   GetPlugins(base::Bind(
410       &PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin,
411       base::Unretained(this), params, url, mime_type, client));
412 }
413 
OpenChannelToPpapiPlugin(int render_process_id,const base::FilePath & plugin_path,const base::FilePath & profile_data_directory,PpapiPluginProcessHost::PluginClient * client)414 void PluginServiceImpl::OpenChannelToPpapiPlugin(
415     int render_process_id,
416     const base::FilePath& plugin_path,
417     const base::FilePath& profile_data_directory,
418     PpapiPluginProcessHost::PluginClient* client) {
419   PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess(
420       render_process_id, plugin_path, profile_data_directory);
421   if (plugin_host) {
422     plugin_host->OpenChannelToPlugin(client);
423   } else {
424     // Send error.
425     client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0);
426   }
427 }
428 
OpenChannelToPpapiBroker(int render_process_id,const base::FilePath & path,PpapiPluginProcessHost::BrokerClient * client)429 void PluginServiceImpl::OpenChannelToPpapiBroker(
430     int render_process_id,
431     const base::FilePath& path,
432     PpapiPluginProcessHost::BrokerClient* client) {
433   PpapiPluginProcessHost* plugin_host = FindOrStartPpapiBrokerProcess(
434       render_process_id, path);
435   if (plugin_host) {
436     plugin_host->OpenChannelToPlugin(client);
437   } else {
438     // Send error.
439     client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0);
440   }
441 }
442 
CancelOpenChannelToNpapiPlugin(PluginProcessHost::Client * client)443 void PluginServiceImpl::CancelOpenChannelToNpapiPlugin(
444     PluginProcessHost::Client* client) {
445   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
446   DCHECK(ContainsKey(pending_plugin_clients_, client));
447   pending_plugin_clients_.erase(client);
448 }
449 
ForwardGetAllowedPluginForOpenChannelToPlugin(const PluginServiceFilterParams & params,const GURL & url,const std::string & mime_type,PluginProcessHost::Client * client,const std::vector<WebPluginInfo> &)450 void PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin(
451     const PluginServiceFilterParams& params,
452     const GURL& url,
453     const std::string& mime_type,
454     PluginProcessHost::Client* client,
455     const std::vector<WebPluginInfo>&) {
456   GetAllowedPluginForOpenChannelToPlugin(
457       params.render_process_id, params.render_frame_id, url, params.page_url,
458       mime_type, client, params.resource_context);
459 }
460 
GetAllowedPluginForOpenChannelToPlugin(int render_process_id,int render_frame_id,const GURL & url,const GURL & page_url,const std::string & mime_type,PluginProcessHost::Client * client,ResourceContext * resource_context)461 void PluginServiceImpl::GetAllowedPluginForOpenChannelToPlugin(
462     int render_process_id,
463     int render_frame_id,
464     const GURL& url,
465     const GURL& page_url,
466     const std::string& mime_type,
467     PluginProcessHost::Client* client,
468     ResourceContext* resource_context) {
469   WebPluginInfo info;
470   bool allow_wildcard = true;
471   bool found = GetPluginInfo(
472       render_process_id, render_frame_id, resource_context,
473       url, page_url, mime_type, allow_wildcard,
474       NULL, &info, NULL);
475   base::FilePath plugin_path;
476   if (found)
477     plugin_path = info.path;
478 
479   // Now we jump back to the IO thread to finish opening the channel.
480   BrowserThread::PostTask(
481       BrowserThread::IO, FROM_HERE,
482       base::Bind(&PluginServiceImpl::FinishOpenChannelToPlugin,
483                  base::Unretained(this),
484                  render_process_id,
485                  plugin_path,
486                  client));
487 }
488 
FinishOpenChannelToPlugin(int render_process_id,const base::FilePath & plugin_path,PluginProcessHost::Client * client)489 void PluginServiceImpl::FinishOpenChannelToPlugin(
490     int render_process_id,
491     const base::FilePath& plugin_path,
492     PluginProcessHost::Client* client) {
493   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
494 
495   // Make sure it hasn't been canceled yet.
496   if (!ContainsKey(pending_plugin_clients_, client))
497     return;
498   pending_plugin_clients_.erase(client);
499 
500   PluginProcessHost* plugin_host = FindOrStartNpapiPluginProcess(
501       render_process_id, plugin_path);
502   if (plugin_host) {
503     client->OnFoundPluginProcessHost(plugin_host);
504     plugin_host->OpenChannelToPlugin(client);
505   } else {
506     client->OnError();
507   }
508 }
509 
GetPluginInfoArray(const GURL & url,const std::string & mime_type,bool allow_wildcard,std::vector<WebPluginInfo> * plugins,std::vector<std::string> * actual_mime_types)510 bool PluginServiceImpl::GetPluginInfoArray(
511     const GURL& url,
512     const std::string& mime_type,
513     bool allow_wildcard,
514     std::vector<WebPluginInfo>* plugins,
515     std::vector<std::string>* actual_mime_types) {
516   bool use_stale = false;
517   PluginList::Singleton()->GetPluginInfoArray(
518       url, mime_type, allow_wildcard, &use_stale, NPAPIPluginsSupported(),
519       plugins, actual_mime_types);
520   return use_stale;
521 }
522 
GetPluginInfo(int render_process_id,int render_frame_id,ResourceContext * context,const GURL & url,const GURL & page_url,const std::string & mime_type,bool allow_wildcard,bool * is_stale,WebPluginInfo * info,std::string * actual_mime_type)523 bool PluginServiceImpl::GetPluginInfo(int render_process_id,
524                                       int render_frame_id,
525                                       ResourceContext* context,
526                                       const GURL& url,
527                                       const GURL& page_url,
528                                       const std::string& mime_type,
529                                       bool allow_wildcard,
530                                       bool* is_stale,
531                                       WebPluginInfo* info,
532                                       std::string* actual_mime_type) {
533   std::vector<WebPluginInfo> plugins;
534   std::vector<std::string> mime_types;
535   bool stale = GetPluginInfoArray(
536       url, mime_type, allow_wildcard, &plugins, &mime_types);
537   if (is_stale)
538     *is_stale = stale;
539 
540   for (size_t i = 0; i < plugins.size(); ++i) {
541     if (!filter_ || filter_->IsPluginAvailable(render_process_id,
542                                                render_frame_id,
543                                                context,
544                                                url,
545                                                page_url,
546                                                &plugins[i])) {
547       *info = plugins[i];
548       if (actual_mime_type)
549         *actual_mime_type = mime_types[i];
550       return true;
551     }
552   }
553   return false;
554 }
555 
GetPluginInfoByPath(const base::FilePath & plugin_path,WebPluginInfo * info)556 bool PluginServiceImpl::GetPluginInfoByPath(const base::FilePath& plugin_path,
557                                             WebPluginInfo* info) {
558   std::vector<WebPluginInfo> plugins;
559   PluginList::Singleton()->GetPluginsNoRefresh(&plugins);
560 
561   for (std::vector<WebPluginInfo>::iterator it = plugins.begin();
562        it != plugins.end();
563        ++it) {
564     if (it->path == plugin_path) {
565       *info = *it;
566       return true;
567     }
568   }
569 
570   return false;
571 }
572 
GetPluginDisplayNameByPath(const base::FilePath & path)573 base::string16 PluginServiceImpl::GetPluginDisplayNameByPath(
574     const base::FilePath& path) {
575   base::string16 plugin_name = path.LossyDisplayName();
576   WebPluginInfo info;
577   if (PluginService::GetInstance()->GetPluginInfoByPath(path, &info) &&
578       !info.name.empty()) {
579     plugin_name = info.name;
580 #if defined(OS_MACOSX)
581     // Many plugins on the Mac have .plugin in the actual name, which looks
582     // terrible, so look for that and strip it off if present.
583     const std::string kPluginExtension = ".plugin";
584     if (EndsWith(plugin_name, base::ASCIIToUTF16(kPluginExtension), true))
585       plugin_name.erase(plugin_name.length() - kPluginExtension.length());
586 #endif  // OS_MACOSX
587   }
588   return plugin_name;
589 }
590 
GetPlugins(const GetPluginsCallback & callback)591 void PluginServiceImpl::GetPlugins(const GetPluginsCallback& callback) {
592   scoped_refptr<base::MessageLoopProxy> target_loop(
593       base::MessageLoop::current()->message_loop_proxy());
594 
595   if (LoadPluginListInProcess()) {
596     BrowserThread::GetBlockingPool()->
597         PostSequencedWorkerTaskWithShutdownBehavior(
598             plugin_list_token_,
599             FROM_HERE,
600             base::Bind(&PluginServiceImpl::GetPluginsInternal,
601                        base::Unretained(this),
602                        target_loop, callback),
603         base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
604     return;
605   }
606 #if defined(OS_POSIX)
607   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
608       base::Bind(&PluginServiceImpl::GetPluginsOnIOThread,
609                  base::Unretained(this), target_loop, callback));
610 #else
611   NOTREACHED();
612 #endif
613 }
614 
GetPluginsInternal(base::MessageLoopProxy * target_loop,const PluginService::GetPluginsCallback & callback)615 void PluginServiceImpl::GetPluginsInternal(
616      base::MessageLoopProxy* target_loop,
617      const PluginService::GetPluginsCallback& callback) {
618   DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
619       plugin_list_token_));
620 
621   std::vector<WebPluginInfo> plugins;
622   PluginList::Singleton()->GetPlugins(&plugins, NPAPIPluginsSupported());
623 
624   target_loop->PostTask(FROM_HERE,
625       base::Bind(callback, plugins));
626 }
627 
628 #if defined(OS_POSIX)
GetPluginsOnIOThread(base::MessageLoopProxy * target_loop,const GetPluginsCallback & callback)629 void PluginServiceImpl::GetPluginsOnIOThread(
630     base::MessageLoopProxy* target_loop,
631     const GetPluginsCallback& callback) {
632   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
633 
634   // If we switch back to loading plugins in process, then we need to make
635   // sure g_thread_init() gets called since plugins may call glib at load.
636 
637   if (!plugin_loader_.get())
638     plugin_loader_ = new PluginLoaderPosix;
639 
640   plugin_loader_->GetPlugins(
641       base::Bind(&ForwardCallback, make_scoped_refptr(target_loop), callback));
642 }
643 #endif
644 
OnWaitableEventSignaled(base::WaitableEvent * waitable_event)645 void PluginServiceImpl::OnWaitableEventSignaled(
646     base::WaitableEvent* waitable_event) {
647 #if defined(OS_WIN)
648   if (waitable_event == hkcu_event_) {
649     hkcu_key_.StartWatching();
650   } else {
651     hklm_key_.StartWatching();
652   }
653 
654   PluginList::Singleton()->RefreshPlugins();
655   PurgePluginListCache(NULL, false);
656 #else
657   // This event should only get signaled on a Windows machine.
658   NOTREACHED();
659 #endif  // defined(OS_WIN)
660 }
661 
RegisterPepperPlugins()662 void PluginServiceImpl::RegisterPepperPlugins() {
663   ComputePepperPluginList(&ppapi_plugins_);
664   for (size_t i = 0; i < ppapi_plugins_.size(); ++i) {
665     RegisterInternalPlugin(ppapi_plugins_[i].ToWebPluginInfo(), true);
666   }
667 }
668 
669 // There should generally be very few plugins so a brute-force search is fine.
GetRegisteredPpapiPluginInfo(const base::FilePath & plugin_path)670 PepperPluginInfo* PluginServiceImpl::GetRegisteredPpapiPluginInfo(
671     const base::FilePath& plugin_path) {
672   PepperPluginInfo* info = NULL;
673   for (size_t i = 0; i < ppapi_plugins_.size(); ++i) {
674     if (ppapi_plugins_[i].path == plugin_path) {
675       info = &ppapi_plugins_[i];
676       break;
677     }
678   }
679   if (info)
680     return info;
681   // We did not find the plugin in our list. But wait! the plugin can also
682   // be a latecomer, as it happens with pepper flash. This information
683   // can be obtained from the PluginList singleton and we can use it to
684   // construct it and add it to the list. This same deal needs to be done
685   // in the renderer side in PepperPluginRegistry.
686   WebPluginInfo webplugin_info;
687   if (!GetPluginInfoByPath(plugin_path, &webplugin_info))
688     return NULL;
689   PepperPluginInfo new_pepper_info;
690   if (!MakePepperPluginInfo(webplugin_info, &new_pepper_info))
691     return NULL;
692   ppapi_plugins_.push_back(new_pepper_info);
693   return &ppapi_plugins_[ppapi_plugins_.size() - 1];
694 }
695 
696 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
697 // static
RegisterFilePathWatcher(FilePathWatcher * watcher,const base::FilePath & path)698 void PluginServiceImpl::RegisterFilePathWatcher(FilePathWatcher* watcher,
699                                                 const base::FilePath& path) {
700   bool result = watcher->Watch(path, false,
701                                base::Bind(&NotifyPluginDirChanged));
702   DCHECK(result);
703 }
704 #endif
705 
SetFilter(PluginServiceFilter * filter)706 void PluginServiceImpl::SetFilter(PluginServiceFilter* filter) {
707   filter_ = filter;
708 }
709 
GetFilter()710 PluginServiceFilter* PluginServiceImpl::GetFilter() {
711   return filter_;
712 }
713 
ForcePluginShutdown(const base::FilePath & plugin_path)714 void PluginServiceImpl::ForcePluginShutdown(const base::FilePath& plugin_path) {
715   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
716     BrowserThread::PostTask(
717         BrowserThread::IO, FROM_HERE,
718         base::Bind(&PluginServiceImpl::ForcePluginShutdown,
719                    base::Unretained(this), plugin_path));
720     return;
721   }
722 
723   PluginProcessHost* plugin = FindNpapiPluginProcess(plugin_path);
724   if (plugin)
725     plugin->ForceShutdown();
726 }
727 
728 static const unsigned int kMaxCrashesPerInterval = 3;
729 static const unsigned int kCrashesInterval = 120;
730 
RegisterPluginCrash(const base::FilePath & path)731 void PluginServiceImpl::RegisterPluginCrash(const base::FilePath& path) {
732   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
733   std::map<base::FilePath, std::vector<base::Time> >::iterator i =
734       crash_times_.find(path);
735   if (i == crash_times_.end()) {
736     crash_times_[path] = std::vector<base::Time>();
737     i = crash_times_.find(path);
738   }
739   if (i->second.size() == kMaxCrashesPerInterval) {
740     i->second.erase(i->second.begin());
741   }
742   base::Time time = base::Time::Now();
743   i->second.push_back(time);
744 }
745 
IsPluginUnstable(const base::FilePath & path)746 bool PluginServiceImpl::IsPluginUnstable(const base::FilePath& path) {
747   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
748   std::map<base::FilePath, std::vector<base::Time> >::const_iterator i =
749       crash_times_.find(path);
750   if (i == crash_times_.end()) {
751     return false;
752   }
753   if (i->second.size() != kMaxCrashesPerInterval) {
754     return false;
755   }
756   base::TimeDelta delta = base::Time::Now() - i->second[0];
757   return delta.InSeconds() <= kCrashesInterval;
758 }
759 
RefreshPlugins()760 void PluginServiceImpl::RefreshPlugins() {
761   PluginList::Singleton()->RefreshPlugins();
762 }
763 
AddExtraPluginPath(const base::FilePath & path)764 void PluginServiceImpl::AddExtraPluginPath(const base::FilePath& path) {
765   if (!NPAPIPluginsSupported()) {
766     // TODO(jam): remove and just have CHECK once we're sure this doesn't get
767     // triggered.
768     DVLOG(0) << "NPAPI plugins not supported";
769     return;
770   }
771   PluginList::Singleton()->AddExtraPluginPath(path);
772 }
773 
RemoveExtraPluginPath(const base::FilePath & path)774 void PluginServiceImpl::RemoveExtraPluginPath(const base::FilePath& path) {
775   PluginList::Singleton()->RemoveExtraPluginPath(path);
776 }
777 
AddExtraPluginDir(const base::FilePath & path)778 void PluginServiceImpl::AddExtraPluginDir(const base::FilePath& path) {
779   PluginList::Singleton()->AddExtraPluginDir(path);
780 }
781 
RegisterInternalPlugin(const WebPluginInfo & info,bool add_at_beginning)782 void PluginServiceImpl::RegisterInternalPlugin(
783     const WebPluginInfo& info,
784     bool add_at_beginning) {
785   if (!NPAPIPluginsSupported() &&
786       info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
787     DVLOG(0) << "Don't register NPAPI plugins when they're not supported";
788     return;
789   }
790   PluginList::Singleton()->RegisterInternalPlugin(info, add_at_beginning);
791 }
792 
UnregisterInternalPlugin(const base::FilePath & path)793 void PluginServiceImpl::UnregisterInternalPlugin(const base::FilePath& path) {
794   PluginList::Singleton()->UnregisterInternalPlugin(path);
795 }
796 
GetInternalPlugins(std::vector<WebPluginInfo> * plugins)797 void PluginServiceImpl::GetInternalPlugins(
798     std::vector<WebPluginInfo>* plugins) {
799   PluginList::Singleton()->GetInternalPlugins(plugins);
800 }
801 
NPAPIPluginsSupported()802 bool PluginServiceImpl::NPAPIPluginsSupported() {
803 #if defined(OS_WIN) || defined(OS_MACOSX)
804   return true;
805 #else
806   return false;
807 #endif
808 }
809 
DisablePluginsDiscoveryForTesting()810 void PluginServiceImpl::DisablePluginsDiscoveryForTesting() {
811   PluginList::Singleton()->DisablePluginsDiscovery();
812 }
813 
814 #if defined(OS_MACOSX)
AppActivated()815 void PluginServiceImpl::AppActivated() {
816   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
817                           base::Bind(&NotifyPluginsOfActivation));
818 }
819 #elif defined(OS_WIN)
820 
GetPluginPropertyFromWindow(HWND window,const wchar_t * plugin_atom_property,base::string16 * plugin_property)821 bool GetPluginPropertyFromWindow(
822     HWND window, const wchar_t* plugin_atom_property,
823     base::string16* plugin_property) {
824   ATOM plugin_atom = reinterpret_cast<ATOM>(
825       GetPropW(window, plugin_atom_property));
826   if (plugin_atom != 0) {
827     WCHAR plugin_property_local[MAX_PATH] = {0};
828     GlobalGetAtomNameW(plugin_atom,
829                        plugin_property_local,
830                        ARRAYSIZE(plugin_property_local));
831     *plugin_property = plugin_property_local;
832     return true;
833   }
834   return false;
835 }
836 
GetPluginInfoFromWindow(HWND window,base::string16 * plugin_name,base::string16 * plugin_version)837 bool PluginServiceImpl::GetPluginInfoFromWindow(
838     HWND window,
839     base::string16* plugin_name,
840     base::string16* plugin_version) {
841   if (!IsPluginWindow(window))
842     return false;
843 
844 
845   DWORD process_id = 0;
846   GetWindowThreadProcessId(window, &process_id);
847   WebPluginInfo info;
848   if (!PluginProcessHost::GetWebPluginInfoFromPluginPid(process_id, &info))
849     return false;
850 
851   *plugin_name = info.name;
852   *plugin_version = info.version;
853   return true;
854 }
855 
IsPluginWindow(HWND window)856 bool PluginServiceImpl::IsPluginWindow(HWND window) {
857   return gfx::GetClassName(window) == base::string16(kNativeWindowClassName);
858 }
859 #endif
860 
PpapiDevChannelSupported(BrowserContext * browser_context,const GURL & document_url)861 bool PluginServiceImpl::PpapiDevChannelSupported(
862     BrowserContext* browser_context,
863     const GURL& document_url) {
864   return content::GetContentClient()->browser()->
865       IsPluginAllowedToUseDevChannelAPIs(browser_context, document_url);
866 }
867 
868 }  // namespace content
869