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/base/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     PpapiPluginProcessHost::PluginClient* client) {
    333   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    334 
    335   if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path))
    336     return NULL;
    337 
    338   PpapiPluginProcessHost* plugin_host =
    339       FindPpapiPluginProcess(plugin_path, profile_data_directory);
    340   if (plugin_host)
    341     return plugin_host;
    342 
    343   // Validate that the plugin is actually registered.
    344   PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path);
    345   if (!info)
    346     return NULL;
    347 
    348   // Record when PPAPI Flash process is started for the first time.
    349   static bool counted = false;
    350   if (!counted && info->name == kFlashPluginName) {
    351     counted = true;
    352     UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage",
    353                               START_PPAPI_FLASH_AT_LEAST_ONCE,
    354                               FLASH_USAGE_ENUM_COUNT);
    355   }
    356 
    357   // This plugin isn't loaded by any plugin process, so create a new process.
    358   return PpapiPluginProcessHost::CreatePluginHost(
    359       *info, profile_data_directory,
    360       client->GetResourceContext()->GetHostResolver());
    361 }
    362 
    363 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiBrokerProcess(
    364     int render_process_id,
    365     const base::FilePath& plugin_path) {
    366   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    367 
    368   if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path))
    369     return NULL;
    370 
    371   PpapiPluginProcessHost* plugin_host = FindPpapiBrokerProcess(plugin_path);
    372   if (plugin_host)
    373     return plugin_host;
    374 
    375   // Validate that the plugin is actually registered.
    376   PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path);
    377   if (!info)
    378     return NULL;
    379 
    380   // TODO(ddorwin): Uncomment once out of process is supported.
    381   // DCHECK(info->is_out_of_process);
    382 
    383   // This broker isn't loaded by any broker process, so create a new process.
    384   return PpapiPluginProcessHost::CreateBrokerHost(*info);
    385 }
    386 
    387 void PluginServiceImpl::OpenChannelToNpapiPlugin(
    388     int render_process_id,
    389     int render_view_id,
    390     const GURL& url,
    391     const GURL& page_url,
    392     const std::string& mime_type,
    393     PluginProcessHost::Client* client) {
    394   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    395   DCHECK(!ContainsKey(pending_plugin_clients_, client));
    396   pending_plugin_clients_.insert(client);
    397 
    398   // Make sure plugins are loaded if necessary.
    399   PluginServiceFilterParams params = {
    400     render_process_id,
    401     render_view_id,
    402     page_url,
    403     client->GetResourceContext()
    404   };
    405   GetPlugins(base::Bind(
    406       &PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin,
    407       base::Unretained(this), params, url, mime_type, client));
    408 }
    409 
    410 void PluginServiceImpl::OpenChannelToPpapiPlugin(
    411     int render_process_id,
    412     const base::FilePath& plugin_path,
    413     const base::FilePath& profile_data_directory,
    414     PpapiPluginProcessHost::PluginClient* client) {
    415   PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess(
    416       render_process_id, plugin_path, profile_data_directory, client);
    417   if (plugin_host) {
    418     plugin_host->OpenChannelToPlugin(client);
    419   } else {
    420     // Send error.
    421     client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0);
    422   }
    423 }
    424 
    425 void PluginServiceImpl::OpenChannelToPpapiBroker(
    426     int render_process_id,
    427     const base::FilePath& path,
    428     PpapiPluginProcessHost::BrokerClient* client) {
    429   PpapiPluginProcessHost* plugin_host = FindOrStartPpapiBrokerProcess(
    430       render_process_id, path);
    431   if (plugin_host) {
    432     plugin_host->OpenChannelToPlugin(client);
    433   } else {
    434     // Send error.
    435     client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0);
    436   }
    437 }
    438 
    439 void PluginServiceImpl::CancelOpenChannelToNpapiPlugin(
    440     PluginProcessHost::Client* client) {
    441   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    442   DCHECK(ContainsKey(pending_plugin_clients_, client));
    443   pending_plugin_clients_.erase(client);
    444 }
    445 
    446 void PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin(
    447     const PluginServiceFilterParams& params,
    448     const GURL& url,
    449     const std::string& mime_type,
    450     PluginProcessHost::Client* client,
    451     const std::vector<WebPluginInfo>&) {
    452   GetAllowedPluginForOpenChannelToPlugin(params.render_process_id,
    453       params.render_view_id, url, params.page_url, mime_type, client,
    454       params.resource_context);
    455 }
    456 
    457 void PluginServiceImpl::GetAllowedPluginForOpenChannelToPlugin(
    458     int render_process_id,
    459     int render_view_id,
    460     const GURL& url,
    461     const GURL& page_url,
    462     const std::string& mime_type,
    463     PluginProcessHost::Client* client,
    464     ResourceContext* resource_context) {
    465   WebPluginInfo info;
    466   bool allow_wildcard = true;
    467   bool found = GetPluginInfo(
    468       render_process_id, render_view_id, resource_context,
    469       url, page_url, mime_type, allow_wildcard,
    470       NULL, &info, NULL);
    471   base::FilePath plugin_path;
    472   if (found)
    473     plugin_path = info.path;
    474 
    475   // Now we jump back to the IO thread to finish opening the channel.
    476   BrowserThread::PostTask(
    477       BrowserThread::IO, FROM_HERE,
    478       base::Bind(&PluginServiceImpl::FinishOpenChannelToPlugin,
    479                  base::Unretained(this),
    480                  render_process_id,
    481                  plugin_path,
    482                  client));
    483 }
    484 
    485 void PluginServiceImpl::FinishOpenChannelToPlugin(
    486     int render_process_id,
    487     const base::FilePath& plugin_path,
    488     PluginProcessHost::Client* client) {
    489   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    490 
    491   // Make sure it hasn't been canceled yet.
    492   if (!ContainsKey(pending_plugin_clients_, client))
    493     return;
    494   pending_plugin_clients_.erase(client);
    495 
    496   PluginProcessHost* plugin_host = FindOrStartNpapiPluginProcess(
    497       render_process_id, plugin_path);
    498   if (plugin_host) {
    499     client->OnFoundPluginProcessHost(plugin_host);
    500     plugin_host->OpenChannelToPlugin(client);
    501   } else {
    502     client->OnError();
    503   }
    504 }
    505 
    506 bool PluginServiceImpl::GetPluginInfoArray(
    507     const GURL& url,
    508     const std::string& mime_type,
    509     bool allow_wildcard,
    510     std::vector<WebPluginInfo>* plugins,
    511     std::vector<std::string>* actual_mime_types) {
    512   bool use_stale = false;
    513   PluginList::Singleton()->GetPluginInfoArray(
    514       url, mime_type, allow_wildcard, &use_stale, NPAPIPluginsSupported(),
    515       plugins, actual_mime_types);
    516   return use_stale;
    517 }
    518 
    519 bool PluginServiceImpl::GetPluginInfo(int render_process_id,
    520                                       int render_view_id,
    521                                       ResourceContext* context,
    522                                       const GURL& url,
    523                                       const GURL& page_url,
    524                                       const std::string& mime_type,
    525                                       bool allow_wildcard,
    526                                       bool* is_stale,
    527                                       WebPluginInfo* info,
    528                                       std::string* actual_mime_type) {
    529   std::vector<WebPluginInfo> plugins;
    530   std::vector<std::string> mime_types;
    531   bool stale = GetPluginInfoArray(
    532       url, mime_type, allow_wildcard, &plugins, &mime_types);
    533   if (is_stale)
    534     *is_stale = stale;
    535 
    536   for (size_t i = 0; i < plugins.size(); ++i) {
    537     if (!filter_ || filter_->IsPluginAvailable(render_process_id,
    538                                                render_view_id,
    539                                                context,
    540                                                url,
    541                                                page_url,
    542                                                &plugins[i])) {
    543       *info = plugins[i];
    544       if (actual_mime_type)
    545         *actual_mime_type = mime_types[i];
    546       return true;
    547     }
    548   }
    549   return false;
    550 }
    551 
    552 bool PluginServiceImpl::GetPluginInfoByPath(const base::FilePath& plugin_path,
    553                                             WebPluginInfo* info) {
    554   std::vector<WebPluginInfo> plugins;
    555   PluginList::Singleton()->GetPluginsNoRefresh(&plugins);
    556 
    557   for (std::vector<WebPluginInfo>::iterator it = plugins.begin();
    558        it != plugins.end();
    559        ++it) {
    560     if (it->path == plugin_path) {
    561       *info = *it;
    562       return true;
    563     }
    564   }
    565 
    566   return false;
    567 }
    568 
    569 string16 PluginServiceImpl::GetPluginDisplayNameByPath(
    570     const base::FilePath& path) {
    571   string16 plugin_name = path.LossyDisplayName();
    572   WebPluginInfo info;
    573   if (PluginService::GetInstance()->GetPluginInfoByPath(path, &info) &&
    574       !info.name.empty()) {
    575     plugin_name = info.name;
    576 #if defined(OS_MACOSX)
    577     // Many plugins on the Mac have .plugin in the actual name, which looks
    578     // terrible, so look for that and strip it off if present.
    579     const std::string kPluginExtension = ".plugin";
    580     if (EndsWith(plugin_name, ASCIIToUTF16(kPluginExtension), true))
    581       plugin_name.erase(plugin_name.length() - kPluginExtension.length());
    582 #endif  // OS_MACOSX
    583   }
    584   return plugin_name;
    585 }
    586 
    587 void PluginServiceImpl::GetPlugins(const GetPluginsCallback& callback) {
    588   scoped_refptr<base::MessageLoopProxy> target_loop(
    589       base::MessageLoop::current()->message_loop_proxy());
    590 
    591   if (LoadPluginListInProcess()) {
    592     BrowserThread::GetBlockingPool()->
    593         PostSequencedWorkerTaskWithShutdownBehavior(
    594             plugin_list_token_,
    595             FROM_HERE,
    596             base::Bind(&PluginServiceImpl::GetPluginsInternal,
    597                        base::Unretained(this),
    598                        target_loop, callback),
    599         base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
    600     return;
    601   }
    602 #if defined(OS_POSIX)
    603   std::vector<WebPluginInfo> cached_plugins;
    604   if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins)) {
    605     // Can't assume the caller is reentrant.
    606     target_loop->PostTask(FROM_HERE,
    607         base::Bind(callback, cached_plugins));
    608   } else {
    609     // If we switch back to loading plugins in process, then we need to make
    610     // sure g_thread_init() gets called since plugins may call glib at load.
    611     if (!plugin_loader_.get())
    612       plugin_loader_ = new PluginLoaderPosix;
    613     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    614         base::Bind(&PluginLoaderPosix::LoadPlugins, plugin_loader_,
    615                    target_loop, callback));
    616   }
    617 #else
    618   NOTREACHED();
    619 #endif
    620 }
    621 
    622 void PluginServiceImpl::GetPluginsInternal(
    623      base::MessageLoopProxy* target_loop,
    624      const PluginService::GetPluginsCallback& callback) {
    625   DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
    626       plugin_list_token_));
    627 
    628   std::vector<WebPluginInfo> plugins;
    629   PluginList::Singleton()->GetPlugins(&plugins, NPAPIPluginsSupported());
    630 
    631   target_loop->PostTask(FROM_HERE,
    632       base::Bind(callback, plugins));
    633 }
    634 
    635 void PluginServiceImpl::OnWaitableEventSignaled(
    636     base::WaitableEvent* waitable_event) {
    637 #if defined(OS_WIN)
    638   if (waitable_event == hkcu_event_) {
    639     hkcu_key_.StartWatching();
    640   } else {
    641     hklm_key_.StartWatching();
    642   }
    643 
    644   PluginList::Singleton()->RefreshPlugins();
    645   PurgePluginListCache(NULL, false);
    646 #else
    647   // This event should only get signaled on a Windows machine.
    648   NOTREACHED();
    649 #endif  // defined(OS_WIN)
    650 }
    651 
    652 void PluginServiceImpl::RegisterPepperPlugins() {
    653   ComputePepperPluginList(&ppapi_plugins_);
    654   for (size_t i = 0; i < ppapi_plugins_.size(); ++i) {
    655     RegisterInternalPlugin(ppapi_plugins_[i].ToWebPluginInfo(), true);
    656   }
    657 }
    658 
    659 // There should generally be very few plugins so a brute-force search is fine.
    660 PepperPluginInfo* PluginServiceImpl::GetRegisteredPpapiPluginInfo(
    661     const base::FilePath& plugin_path) {
    662   PepperPluginInfo* info = NULL;
    663   for (size_t i = 0; i < ppapi_plugins_.size(); i++) {
    664     if (ppapi_plugins_[i].path == plugin_path) {
    665       info = &ppapi_plugins_[i];
    666       break;
    667     }
    668   }
    669   if (info)
    670     return info;
    671   // We did not find the plugin in our list. But wait! the plugin can also
    672   // be a latecomer, as it happens with pepper flash. This information
    673   // can be obtained from the PluginList singleton and we can use it to
    674   // construct it and add it to the list. This same deal needs to be done
    675   // in the renderer side in PepperPluginRegistry.
    676   WebPluginInfo webplugin_info;
    677   if (!GetPluginInfoByPath(plugin_path, &webplugin_info))
    678     return NULL;
    679   PepperPluginInfo new_pepper_info;
    680   if (!MakePepperPluginInfo(webplugin_info, &new_pepper_info))
    681     return NULL;
    682   ppapi_plugins_.push_back(new_pepper_info);
    683   return &ppapi_plugins_[ppapi_plugins_.size() - 1];
    684 }
    685 
    686 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
    687 // static
    688 void PluginServiceImpl::RegisterFilePathWatcher(FilePathWatcher* watcher,
    689                                                 const base::FilePath& path) {
    690   bool result = watcher->Watch(path, false,
    691                                base::Bind(&NotifyPluginDirChanged));
    692   DCHECK(result);
    693 }
    694 #endif
    695 
    696 void PluginServiceImpl::SetFilter(PluginServiceFilter* filter) {
    697   filter_ = filter;
    698 }
    699 
    700 PluginServiceFilter* PluginServiceImpl::GetFilter() {
    701   return filter_;
    702 }
    703 
    704 void PluginServiceImpl::ForcePluginShutdown(const base::FilePath& plugin_path) {
    705   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    706     BrowserThread::PostTask(
    707         BrowserThread::IO, FROM_HERE,
    708         base::Bind(&PluginServiceImpl::ForcePluginShutdown,
    709                    base::Unretained(this), plugin_path));
    710     return;
    711   }
    712 
    713   PluginProcessHost* plugin = FindNpapiPluginProcess(plugin_path);
    714   if (plugin)
    715     plugin->ForceShutdown();
    716 }
    717 
    718 static const unsigned int kMaxCrashesPerInterval = 3;
    719 static const unsigned int kCrashesInterval = 120;
    720 
    721 void PluginServiceImpl::RegisterPluginCrash(const base::FilePath& path) {
    722   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    723   std::map<base::FilePath, std::vector<base::Time> >::iterator i =
    724       crash_times_.find(path);
    725   if (i == crash_times_.end()) {
    726     crash_times_[path] = std::vector<base::Time>();
    727     i = crash_times_.find(path);
    728   }
    729   if (i->second.size() == kMaxCrashesPerInterval) {
    730     i->second.erase(i->second.begin());
    731   }
    732   base::Time time = base::Time::Now();
    733   i->second.push_back(time);
    734 }
    735 
    736 bool PluginServiceImpl::IsPluginUnstable(const base::FilePath& path) {
    737   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    738   std::map<base::FilePath, std::vector<base::Time> >::const_iterator i =
    739       crash_times_.find(path);
    740   if (i == crash_times_.end()) {
    741     return false;
    742   }
    743   if (i->second.size() != kMaxCrashesPerInterval) {
    744     return false;
    745   }
    746   base::TimeDelta delta = base::Time::Now() - i->second[0];
    747   return delta.InSeconds() <= kCrashesInterval;
    748 }
    749 
    750 void PluginServiceImpl::RefreshPlugins() {
    751   PluginList::Singleton()->RefreshPlugins();
    752 }
    753 
    754 void PluginServiceImpl::AddExtraPluginPath(const base::FilePath& path) {
    755  if (!NPAPIPluginsSupported()) {
    756     // TODO(jam): remove and just have CHECK once we're sure this doesn't get
    757     // triggered.
    758     DLOG(INFO) << "NPAPI plugins not supported";
    759     return;
    760   }
    761   PluginList::Singleton()->AddExtraPluginPath(path);
    762 }
    763 
    764 void PluginServiceImpl::RemoveExtraPluginPath(const base::FilePath& path) {
    765   PluginList::Singleton()->RemoveExtraPluginPath(path);
    766 }
    767 
    768 void PluginServiceImpl::AddExtraPluginDir(const base::FilePath& path) {
    769   PluginList::Singleton()->AddExtraPluginDir(path);
    770 }
    771 
    772 void PluginServiceImpl::RegisterInternalPlugin(
    773     const WebPluginInfo& info,
    774     bool add_at_beginning) {
    775   if (!NPAPIPluginsSupported() &&
    776       info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
    777     DLOG(INFO) << "Don't register NPAPI plugins when they're not supported";
    778     return;
    779   }
    780   PluginList::Singleton()->RegisterInternalPlugin(info, add_at_beginning);
    781 }
    782 
    783 void PluginServiceImpl::UnregisterInternalPlugin(const base::FilePath& path) {
    784   PluginList::Singleton()->UnregisterInternalPlugin(path);
    785 }
    786 
    787 void PluginServiceImpl::GetInternalPlugins(
    788     std::vector<WebPluginInfo>* plugins) {
    789   PluginList::Singleton()->GetInternalPlugins(plugins);
    790 }
    791 
    792 bool PluginServiceImpl::NPAPIPluginsSupported() {
    793 #if defined(OS_WIN) || defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(USE_AURA))
    794   return true;
    795 #else
    796   return false;
    797 #endif
    798 }
    799 
    800 void PluginServiceImpl::DisablePluginsDiscoveryForTesting() {
    801   PluginList::Singleton()->DisablePluginsDiscovery();
    802 }
    803 
    804 #if defined(OS_MACOSX)
    805 void PluginServiceImpl::AppActivated() {
    806   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    807                           base::Bind(&NotifyPluginsOfActivation));
    808 }
    809 #elif defined(OS_WIN)
    810 
    811 bool GetPluginPropertyFromWindow(
    812     HWND window, const wchar_t* plugin_atom_property,
    813     base::string16* plugin_property) {
    814   ATOM plugin_atom = reinterpret_cast<ATOM>(
    815       GetPropW(window, plugin_atom_property));
    816   if (plugin_atom != 0) {
    817     WCHAR plugin_property_local[MAX_PATH] = {0};
    818     GlobalGetAtomNameW(plugin_atom,
    819                        plugin_property_local,
    820                        ARRAYSIZE(plugin_property_local));
    821     *plugin_property = plugin_property_local;
    822     return true;
    823   }
    824   return false;
    825 }
    826 
    827 bool PluginServiceImpl::GetPluginInfoFromWindow(
    828     HWND window,
    829     base::string16* plugin_name,
    830     base::string16* plugin_version) {
    831   if (!IsPluginWindow(window))
    832     return false;
    833 
    834   GetPluginPropertyFromWindow(
    835           window, kPluginNameAtomProperty, plugin_name);
    836   GetPluginPropertyFromWindow(
    837           window, kPluginVersionAtomProperty, plugin_version);
    838   return true;
    839 }
    840 
    841 bool PluginServiceImpl::IsPluginWindow(HWND window) {
    842   return ui::GetClassName(window) == base::string16(kNativeWindowClassName);
    843 }
    844 #endif
    845 
    846 }  // namespace content
    847