Home | History | Annotate | Download | only in browser
      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 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
    127 PluginService* PluginService::GetInstance() {
    128   return PluginServiceImpl::GetInstance();
    129 }
    130 
    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
    142 PluginServiceImpl* PluginServiceImpl::GetInstance() {
    143   return Singleton<PluginServiceImpl>::get();
    144 }
    145 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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)
    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 
    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 
    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.
    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
    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 
    706 void PluginServiceImpl::SetFilter(PluginServiceFilter* filter) {
    707   filter_ = filter;
    708 }
    709 
    710 PluginServiceFilter* PluginServiceImpl::GetFilter() {
    711   return filter_;
    712 }
    713 
    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 
    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 
    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 
    760 void PluginServiceImpl::RefreshPlugins() {
    761   PluginList::Singleton()->RefreshPlugins();
    762 }
    763 
    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 
    774 void PluginServiceImpl::RemoveExtraPluginPath(const base::FilePath& path) {
    775   PluginList::Singleton()->RemoveExtraPluginPath(path);
    776 }
    777 
    778 void PluginServiceImpl::AddExtraPluginDir(const base::FilePath& path) {
    779   PluginList::Singleton()->AddExtraPluginDir(path);
    780 }
    781 
    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 
    793 void PluginServiceImpl::UnregisterInternalPlugin(const base::FilePath& path) {
    794   PluginList::Singleton()->UnregisterInternalPlugin(path);
    795 }
    796 
    797 void PluginServiceImpl::GetInternalPlugins(
    798     std::vector<WebPluginInfo>* plugins) {
    799   PluginList::Singleton()->GetInternalPlugins(plugins);
    800 }
    801 
    802 bool PluginServiceImpl::NPAPIPluginsSupported() {
    803 #if defined(OS_WIN) || defined(OS_MACOSX)
    804   return true;
    805 #else
    806   return false;
    807 #endif
    808 }
    809 
    810 void PluginServiceImpl::DisablePluginsDiscoveryForTesting() {
    811   PluginList::Singleton()->DisablePluginsDiscovery();
    812 }
    813 
    814 #if defined(OS_MACOSX)
    815 void PluginServiceImpl::AppActivated() {
    816   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    817                           base::Bind(&NotifyPluginsOfActivation));
    818 }
    819 #elif defined(OS_WIN)
    820 
    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 
    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 
    856 bool PluginServiceImpl::IsPluginWindow(HWND window) {
    857   return gfx::GetClassName(window) == base::string16(kNativeWindowClassName);
    858 }
    859 #endif
    860 
    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