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