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