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 
     18 namespace content {
     19 
     20 PluginLoaderPosix::PluginLoaderPosix()
     21     : next_load_index_(0) {
     22 }
     23 
     24 void PluginLoaderPosix::LoadPlugins(
     25     scoped_refptr<base::MessageLoopProxy> target_loop,
     26     const PluginService::GetPluginsCallback& callback) {
     27   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     28 
     29   callbacks_.push_back(PendingCallback(target_loop, callback));
     30 
     31   if (callbacks_.size() == 1) {
     32     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
     33         base::Bind(&PluginLoaderPosix::GetPluginsToLoad, this));
     34   }
     35 }
     36 
     37 bool PluginLoaderPosix::OnMessageReceived(const IPC::Message& message) {
     38   bool handled = true;
     39   IPC_BEGIN_MESSAGE_MAP(PluginLoaderPosix, message)
     40     IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadedPlugin, OnPluginLoaded)
     41     IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadPluginFailed, OnPluginLoadFailed)
     42     IPC_MESSAGE_UNHANDLED(handled = false)
     43   IPC_END_MESSAGE_MAP()
     44   return handled;
     45 }
     46 
     47 void PluginLoaderPosix::OnProcessCrashed(int exit_code) {
     48   if (next_load_index_ == canonical_list_.size()) {
     49     // How this case occurs is unknown. See crbug.com/111935.
     50     canonical_list_.clear();
     51   } else {
     52     canonical_list_.erase(canonical_list_.begin(),
     53                           canonical_list_.begin() + next_load_index_ + 1);
     54   }
     55 
     56   next_load_index_ = 0;
     57 
     58   LoadPluginsInternal();
     59 }
     60 
     61 bool PluginLoaderPosix::Send(IPC::Message* message) {
     62   if (process_host_.get())
     63     return process_host_->Send(message);
     64   return false;
     65 }
     66 
     67 PluginLoaderPosix::~PluginLoaderPosix() {
     68 }
     69 
     70 void PluginLoaderPosix::GetPluginsToLoad() {
     71   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     72 
     73   base::TimeTicks start_time(base::TimeTicks::Now());
     74 
     75   loaded_plugins_.clear();
     76   next_load_index_ = 0;
     77 
     78   canonical_list_.clear();
     79   PluginList::Singleton()->GetPluginPathsToLoad(
     80       &canonical_list_,
     81       PluginService::GetInstance()->NPAPIPluginsSupported());
     82 
     83   internal_plugins_.clear();
     84   PluginList::Singleton()->GetInternalPlugins(&internal_plugins_);
     85 
     86   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
     87       base::Bind(&PluginLoaderPosix::LoadPluginsInternal,
     88                  make_scoped_refptr(this)));
     89 
     90   HISTOGRAM_TIMES("PluginLoaderPosix.GetPluginList",
     91                   (base::TimeTicks::Now() - start_time) *
     92                       base::Time::kMicrosecondsPerMillisecond);
     93 }
     94 
     95 void PluginLoaderPosix::LoadPluginsInternal() {
     96   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     97 
     98   // Check if the list is empty or all plugins have already been loaded before
     99   // forking.
    100   if (MaybeRunPendingCallbacks())
    101     return;
    102 
    103   if (load_start_time_.is_null())
    104     load_start_time_ = base::TimeTicks::Now();
    105 
    106   UtilityProcessHostImpl* host = new UtilityProcessHostImpl(
    107       this,
    108       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get());
    109   process_host_ = host->AsWeakPtr();
    110   process_host_->DisableSandbox();
    111 #if defined(OS_MACOSX)
    112   host->set_child_flags(ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION);
    113 #endif
    114 
    115   process_host_->Send(new UtilityMsg_LoadPlugins(canonical_list_));
    116 }
    117 
    118 void PluginLoaderPosix::OnPluginLoaded(uint32 index,
    119                                        const WebPluginInfo& plugin) {
    120   if (index != next_load_index_) {
    121     LOG(ERROR) << "Received unexpected plugin load message for "
    122                << plugin.path.value() << "; index=" << index;
    123     return;
    124   }
    125 
    126   if (!MaybeAddInternalPlugin(plugin.path))
    127     loaded_plugins_.push_back(plugin);
    128 
    129   ++next_load_index_;
    130 
    131   MaybeRunPendingCallbacks();
    132 }
    133 
    134 void PluginLoaderPosix::OnPluginLoadFailed(uint32 index,
    135                                            const base::FilePath& plugin_path) {
    136   if (index != next_load_index_) {
    137     LOG(ERROR) << "Received unexpected plugin load failure message for "
    138                << plugin_path.value() << "; index=" << index;
    139     return;
    140   }
    141 
    142   ++next_load_index_;
    143 
    144   MaybeAddInternalPlugin(plugin_path);
    145   MaybeRunPendingCallbacks();
    146 }
    147 
    148 bool PluginLoaderPosix::MaybeAddInternalPlugin(
    149     const base::FilePath& plugin_path) {
    150   for (std::vector<WebPluginInfo>::iterator it = internal_plugins_.begin();
    151        it != internal_plugins_.end();
    152        ++it) {
    153     if (it->path == plugin_path) {
    154       loaded_plugins_.push_back(*it);
    155       internal_plugins_.erase(it);
    156       return true;
    157     }
    158   }
    159   return false;
    160 }
    161 
    162 bool PluginLoaderPosix::MaybeRunPendingCallbacks() {
    163   if (next_load_index_ < canonical_list_.size())
    164     return false;
    165 
    166   PluginList::Singleton()->SetPlugins(loaded_plugins_);
    167 
    168   // Only call the first callback with loaded plugins because there may be
    169   // some extra plugin paths added since the first callback is added.
    170   if (!callbacks_.empty()) {
    171     PendingCallback callback = callbacks_.front();
    172     callbacks_.pop_front();
    173     callback.target_loop->PostTask(
    174         FROM_HERE,
    175         base::Bind(callback.callback, loaded_plugins_));
    176   }
    177 
    178   HISTOGRAM_TIMES("PluginLoaderPosix.LoadDone",
    179                   (base::TimeTicks::Now() - load_start_time_)
    180                       * base::Time::kMicrosecondsPerMillisecond);
    181   load_start_time_ = base::TimeTicks();
    182 
    183   if (!callbacks_.empty()) {
    184     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    185         base::Bind(&PluginLoaderPosix::GetPluginsToLoad, this));
    186     return false;
    187   }
    188   return true;
    189 }
    190 
    191 PluginLoaderPosix::PendingCallback::PendingCallback(
    192     scoped_refptr<base::MessageLoopProxy> loop,
    193     const PluginService::GetPluginsCallback& cb)
    194     : target_loop(loop),
    195       callback(cb) {
    196 }
    197 
    198 PluginLoaderPosix::PendingCallback::~PendingCallback() {
    199 }
    200 
    201 }  // namespace content
    202