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