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_loader_posix.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "base/metrics/histogram.h"
     11 #include "content/browser/utility_process_host_impl.h"
     12 #include "content/common/child_process_host_impl.h"
     13 #include "content/common/plugin_list.h"
     14 #include "content/common/utility_messages.h"
     15 #include "content/public/browser/browser_thread.h"
     16 #include "content/public/browser/plugin_service.h"
     17 #include "content/public/browser/user_metrics.h"
     18 
     19 namespace content {
     20 
     21 PluginLoaderPosix::PluginLoaderPosix()
     22     : next_load_index_(0) {
     23 }
     24 
     25 void PluginLoaderPosix::GetPlugins(
     26     const PluginService::GetPluginsCallback& callback) {
     27   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     28 
     29   std::vector<WebPluginInfo> cached_plugins;
     30   if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins)) {
     31     // Can't assume the caller is reentrant.
     32     base::MessageLoop::current()->PostTask(FROM_HERE,
     33         base::Bind(callback, cached_plugins));
     34     return;
     35   }
     36 
     37   if (callbacks_.empty()) {
     38     callbacks_.push_back(callback);
     39 
     40     PluginList::Singleton()->PrepareForPluginLoading();
     41 
     42     BrowserThread::PostTask(BrowserThread::FILE,
     43                             FROM_HERE,
     44                             base::Bind(&PluginLoaderPosix::GetPluginsToLoad,
     45                                        make_scoped_refptr(this)));
     46   } else {
     47     // If we are currently loading plugins, the plugin list might have been
     48     // invalidated in the mean time, or might get invalidated before we finish.
     49     // We'll wait until we have finished the current run, then try to get them
     50     // again from the plugin list. If it has indeed been invalidated, it will
     51     // restart plugin loading, otherwise it will immediately run the callback.
     52     callbacks_.push_back(base::Bind(&PluginLoaderPosix::GetPluginsWrapper,
     53                                     make_scoped_refptr(this), callback));
     54   }
     55 }
     56 
     57 bool PluginLoaderPosix::OnMessageReceived(const IPC::Message& message) {
     58   bool handled = true;
     59   IPC_BEGIN_MESSAGE_MAP(PluginLoaderPosix, message)
     60     IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadedPlugin, OnPluginLoaded)
     61     IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadPluginFailed, OnPluginLoadFailed)
     62     IPC_MESSAGE_UNHANDLED(handled = false)
     63   IPC_END_MESSAGE_MAP()
     64   return handled;
     65 }
     66 
     67 void PluginLoaderPosix::OnProcessCrashed(int exit_code) {
     68   RecordAction(
     69       base::UserMetricsAction("PluginLoaderPosix.UtilityProcessCrashed"));
     70 
     71   if (next_load_index_ == canonical_list_.size()) {
     72     // How this case occurs is unknown. See crbug.com/111935.
     73     canonical_list_.clear();
     74   } else {
     75     canonical_list_.erase(canonical_list_.begin(),
     76                           canonical_list_.begin() + next_load_index_ + 1);
     77   }
     78 
     79   next_load_index_ = 0;
     80 
     81   LoadPluginsInternal();
     82 }
     83 
     84 bool PluginLoaderPosix::Send(IPC::Message* message) {
     85   if (process_host_.get())
     86     return process_host_->Send(message);
     87   return false;
     88 }
     89 
     90 PluginLoaderPosix::~PluginLoaderPosix() {
     91 }
     92 
     93 void PluginLoaderPosix::GetPluginsToLoad() {
     94   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     95 
     96   base::TimeTicks start_time(base::TimeTicks::Now());
     97 
     98   loaded_plugins_.clear();
     99   next_load_index_ = 0;
    100 
    101   canonical_list_.clear();
    102   PluginList::Singleton()->GetPluginPathsToLoad(
    103       &canonical_list_,
    104       PluginService::GetInstance()->NPAPIPluginsSupported());
    105 
    106   internal_plugins_.clear();
    107   PluginList::Singleton()->GetInternalPlugins(&internal_plugins_);
    108 
    109   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    110       base::Bind(&PluginLoaderPosix::LoadPluginsInternal,
    111                  make_scoped_refptr(this)));
    112 
    113   LOCAL_HISTOGRAM_TIMES("PluginLoaderPosix.GetPluginList",
    114                         (base::TimeTicks::Now() - start_time) *
    115                             base::Time::kMicrosecondsPerMillisecond);
    116 }
    117 
    118 void PluginLoaderPosix::LoadPluginsInternal() {
    119   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    120 
    121   // Check if the list is empty or all plugins have already been loaded before
    122   // forking.
    123   if (MaybeRunPendingCallbacks())
    124     return;
    125 
    126   RecordAction(
    127       base::UserMetricsAction("PluginLoaderPosix.LaunchUtilityProcess"));
    128 
    129   if (load_start_time_.is_null())
    130     load_start_time_ = base::TimeTicks::Now();
    131 
    132   UtilityProcessHostImpl* host = new UtilityProcessHostImpl(
    133       this,
    134       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get());
    135   process_host_ = host->AsWeakPtr();
    136   process_host_->DisableSandbox();
    137 #if defined(OS_MACOSX)
    138   host->set_child_flags(ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION);
    139 #endif
    140 
    141   process_host_->Send(new UtilityMsg_LoadPlugins(canonical_list_));
    142 }
    143 
    144 void PluginLoaderPosix::GetPluginsWrapper(
    145     const PluginService::GetPluginsCallback& callback,
    146     const std::vector<WebPluginInfo>& plugins_unused) {
    147   // We are being called after plugin loading has finished, but we don't know
    148   // whether the plugin list has been invalidated in the mean time
    149   // (and therefore |plugins| might already be stale). So we simply ignore it
    150   // and call regular GetPlugins() instead.
    151   GetPlugins(callback);
    152 }
    153 
    154 void PluginLoaderPosix::OnPluginLoaded(uint32 index,
    155                                        const WebPluginInfo& plugin) {
    156   if (index != next_load_index_) {
    157     LOG(ERROR) << "Received unexpected plugin load message for "
    158                << plugin.path.value() << "; index=" << index;
    159     return;
    160   }
    161 
    162   if (!MaybeAddInternalPlugin(plugin.path))
    163     loaded_plugins_.push_back(plugin);
    164 
    165   ++next_load_index_;
    166 
    167   MaybeRunPendingCallbacks();
    168 }
    169 
    170 void PluginLoaderPosix::OnPluginLoadFailed(uint32 index,
    171                                            const base::FilePath& plugin_path) {
    172   if (index != next_load_index_) {
    173     LOG(ERROR) << "Received unexpected plugin load failure message for "
    174                << plugin_path.value() << "; index=" << index;
    175     return;
    176   }
    177 
    178   ++next_load_index_;
    179 
    180   MaybeAddInternalPlugin(plugin_path);
    181   MaybeRunPendingCallbacks();
    182 }
    183 
    184 bool PluginLoaderPosix::MaybeAddInternalPlugin(
    185     const base::FilePath& plugin_path) {
    186   for (std::vector<WebPluginInfo>::iterator it = internal_plugins_.begin();
    187        it != internal_plugins_.end();
    188        ++it) {
    189     if (it->path == plugin_path) {
    190       loaded_plugins_.push_back(*it);
    191       internal_plugins_.erase(it);
    192       return true;
    193     }
    194   }
    195   return false;
    196 }
    197 
    198 bool PluginLoaderPosix::MaybeRunPendingCallbacks() {
    199   if (next_load_index_ < canonical_list_.size())
    200     return false;
    201 
    202   PluginList::Singleton()->SetPlugins(loaded_plugins_);
    203 
    204   for (std::vector<PluginService::GetPluginsCallback>::iterator it =
    205            callbacks_.begin();
    206        it != callbacks_.end(); ++it) {
    207     base::MessageLoop::current()->PostTask(FROM_HERE,
    208                                            base::Bind(*it, loaded_plugins_));
    209   }
    210   callbacks_.clear();
    211 
    212   LOCAL_HISTOGRAM_TIMES("PluginLoaderPosix.LoadDone",
    213                         (base::TimeTicks::Now() - load_start_time_) *
    214                             base::Time::kMicrosecondsPerMillisecond);
    215   load_start_time_ = base::TimeTicks();
    216 
    217   return true;
    218 }
    219 
    220 }  // namespace content
    221