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