// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/plugin_loader_posix.h" #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy.h" #include "base/metrics/histogram.h" #include "content/browser/utility_process_host_impl.h" #include "content/common/child_process_host_impl.h" #include "content/common/plugin_list.h" #include "content/common/utility_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/plugin_service.h" namespace content { PluginLoaderPosix::PluginLoaderPosix() : next_load_index_(0) { } void PluginLoaderPosix::LoadPlugins( scoped_refptr target_loop, const PluginService::GetPluginsCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); callbacks_.push_back(PendingCallback(target_loop, callback)); if (callbacks_.size() == 1) { BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&PluginLoaderPosix::GetPluginsToLoad, this)); } } bool PluginLoaderPosix::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PluginLoaderPosix, message) IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadedPlugin, OnPluginLoaded) IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadPluginFailed, OnPluginLoadFailed) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void PluginLoaderPosix::OnProcessCrashed(int exit_code) { if (next_load_index_ == canonical_list_.size()) { // How this case occurs is unknown. See crbug.com/111935. canonical_list_.clear(); } else { canonical_list_.erase(canonical_list_.begin(), canonical_list_.begin() + next_load_index_ + 1); } next_load_index_ = 0; LoadPluginsInternal(); } bool PluginLoaderPosix::Send(IPC::Message* message) { if (process_host_.get()) return process_host_->Send(message); return false; } PluginLoaderPosix::~PluginLoaderPosix() { } void PluginLoaderPosix::GetPluginsToLoad() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); base::TimeTicks start_time(base::TimeTicks::Now()); loaded_plugins_.clear(); next_load_index_ = 0; canonical_list_.clear(); PluginList::Singleton()->GetPluginPathsToLoad( &canonical_list_, PluginService::GetInstance()->NPAPIPluginsSupported()); internal_plugins_.clear(); PluginList::Singleton()->GetInternalPlugins(&internal_plugins_); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&PluginLoaderPosix::LoadPluginsInternal, make_scoped_refptr(this))); HISTOGRAM_TIMES("PluginLoaderPosix.GetPluginList", (base::TimeTicks::Now() - start_time) * base::Time::kMicrosecondsPerMillisecond); } void PluginLoaderPosix::LoadPluginsInternal() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Check if the list is empty or all plugins have already been loaded before // forking. if (MaybeRunPendingCallbacks()) return; if (load_start_time_.is_null()) load_start_time_ = base::TimeTicks::Now(); UtilityProcessHostImpl* host = new UtilityProcessHostImpl( this, BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()); process_host_ = host->AsWeakPtr(); process_host_->DisableSandbox(); #if defined(OS_MACOSX) host->set_child_flags(ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION); #endif process_host_->Send(new UtilityMsg_LoadPlugins(canonical_list_)); } void PluginLoaderPosix::OnPluginLoaded(uint32 index, const WebPluginInfo& plugin) { if (index != next_load_index_) { LOG(ERROR) << "Received unexpected plugin load message for " << plugin.path.value() << "; index=" << index; return; } if (!MaybeAddInternalPlugin(plugin.path)) loaded_plugins_.push_back(plugin); ++next_load_index_; MaybeRunPendingCallbacks(); } void PluginLoaderPosix::OnPluginLoadFailed(uint32 index, const base::FilePath& plugin_path) { if (index != next_load_index_) { LOG(ERROR) << "Received unexpected plugin load failure message for " << plugin_path.value() << "; index=" << index; return; } ++next_load_index_; MaybeAddInternalPlugin(plugin_path); MaybeRunPendingCallbacks(); } bool PluginLoaderPosix::MaybeAddInternalPlugin( const base::FilePath& plugin_path) { for (std::vector::iterator it = internal_plugins_.begin(); it != internal_plugins_.end(); ++it) { if (it->path == plugin_path) { loaded_plugins_.push_back(*it); internal_plugins_.erase(it); return true; } } return false; } bool PluginLoaderPosix::MaybeRunPendingCallbacks() { if (next_load_index_ < canonical_list_.size()) return false; PluginList::Singleton()->SetPlugins(loaded_plugins_); // Only call the first callback with loaded plugins because there may be // some extra plugin paths added since the first callback is added. if (!callbacks_.empty()) { PendingCallback callback = callbacks_.front(); callbacks_.pop_front(); callback.target_loop->PostTask( FROM_HERE, base::Bind(callback.callback, loaded_plugins_)); } HISTOGRAM_TIMES("PluginLoaderPosix.LoadDone", (base::TimeTicks::Now() - load_start_time_) * base::Time::kMicrosecondsPerMillisecond); load_start_time_ = base::TimeTicks(); if (!callbacks_.empty()) { BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&PluginLoaderPosix::GetPluginsToLoad, this)); return false; } return true; } PluginLoaderPosix::PendingCallback::PendingCallback( scoped_refptr loop, const PluginService::GetPluginsCallback& cb) : target_loop(loop), callback(cb) { } PluginLoaderPosix::PendingCallback::~PendingCallback() { } } // namespace content