| OLD | NEW |
| (Empty) |
| 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_process_host.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <utility> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/base_switches.h" | |
| 13 #include "base/bind.h" | |
| 14 #include "base/command_line.h" | |
| 15 #include "base/files/file_path.h" | |
| 16 #include "base/lazy_instance.h" | |
| 17 #include "base/logging.h" | |
| 18 #include "base/macros.h" | |
| 19 #include "base/metrics/histogram.h" | |
| 20 #include "base/strings/string_number_conversions.h" | |
| 21 #include "base/strings/string_util.h" | |
| 22 #include "base/strings/utf_string_conversions.h" | |
| 23 #include "base/synchronization/lock.h" | |
| 24 #include "build/build_config.h" | |
| 25 #include "components/tracing/tracing_switches.h" | |
| 26 #include "content/browser/browser_child_process_host_impl.h" | |
| 27 #include "content/browser/gpu/gpu_data_manager_impl.h" | |
| 28 #include "content/browser/loader/resource_message_filter.h" | |
| 29 #include "content/browser/plugin_service_impl.h" | |
| 30 #include "content/common/child_process_host_impl.h" | |
| 31 #include "content/common/plugin_process_messages.h" | |
| 32 #include "content/common/resource_messages.h" | |
| 33 #include "content/public/browser/browser_thread.h" | |
| 34 #include "content/public/browser/content_browser_client.h" | |
| 35 #include "content/public/browser/notification_types.h" | |
| 36 #include "content/public/browser/plugin_service.h" | |
| 37 #include "content/public/browser/resource_context.h" | |
| 38 #include "content/public/common/content_switches.h" | |
| 39 #include "content/public/common/process_type.h" | |
| 40 #include "content/public/common/sandboxed_process_launcher_delegate.h" | |
| 41 #include "ipc/ipc_switches.h" | |
| 42 #include "net/url_request/url_request_context_getter.h" | |
| 43 #include "ui/base/ui_base_switches.h" | |
| 44 #include "ui/gfx/native_widget_types.h" | |
| 45 #include "ui/gfx/switches.h" | |
| 46 #include "ui/gl/gl_switches.h" | |
| 47 | |
| 48 #if defined(OS_WIN) | |
| 49 #include <windows.h> | |
| 50 #endif | |
| 51 | |
| 52 #if defined(OS_MACOSX) | |
| 53 #include "base/mac/mac_util.h" | |
| 54 #include "ui/gfx/geometry/rect.h" | |
| 55 #endif | |
| 56 | |
| 57 #if defined(OS_WIN) | |
| 58 #include "base/win/windows_version.h" | |
| 59 #include "content/common/plugin_constants_win.h" | |
| 60 #endif | |
| 61 | |
| 62 namespace content { | |
| 63 | |
| 64 namespace { | |
| 65 | |
| 66 base::LazyInstance<std::map<base::ProcessId, WebPluginInfo> > | |
| 67 g_process_webplugin_info = LAZY_INSTANCE_INITIALIZER; | |
| 68 base::LazyInstance<base::Lock>::Leaky | |
| 69 g_process_webplugin_info_lock = LAZY_INSTANCE_INITIALIZER; | |
| 70 } | |
| 71 | |
| 72 bool PluginProcessHost::GetWebPluginInfoFromPluginPid(base::ProcessId pid, | |
| 73 WebPluginInfo* info) { | |
| 74 base::AutoLock lock(g_process_webplugin_info_lock.Get()); | |
| 75 if (!g_process_webplugin_info.Get().count(pid)) | |
| 76 return false; | |
| 77 | |
| 78 *info = g_process_webplugin_info.Get()[pid]; | |
| 79 return true; | |
| 80 } | |
| 81 | |
| 82 // NOTE: changes to this class need to be reviewed by the security team. | |
| 83 class PluginSandboxedProcessLauncherDelegate | |
| 84 : public SandboxedProcessLauncherDelegate { | |
| 85 public: | |
| 86 explicit PluginSandboxedProcessLauncherDelegate(ChildProcessHost* host) | |
| 87 #if defined(OS_POSIX) | |
| 88 : ipc_fd_(host->TakeClientFileDescriptor()) | |
| 89 #endif // OS_POSIX | |
| 90 {} | |
| 91 | |
| 92 ~PluginSandboxedProcessLauncherDelegate() override {} | |
| 93 | |
| 94 #if defined(OS_WIN) | |
| 95 bool ShouldSandbox() override { | |
| 96 return false; | |
| 97 } | |
| 98 | |
| 99 #elif defined(OS_POSIX) | |
| 100 base::ScopedFD TakeIpcFd() override { return std::move(ipc_fd_); } | |
| 101 #endif // OS_WIN | |
| 102 | |
| 103 private: | |
| 104 #if defined(OS_POSIX) | |
| 105 base::ScopedFD ipc_fd_; | |
| 106 #endif // OS_POSIX | |
| 107 | |
| 108 DISALLOW_COPY_AND_ASSIGN(PluginSandboxedProcessLauncherDelegate); | |
| 109 }; | |
| 110 | |
| 111 PluginProcessHost::PluginProcessHost() | |
| 112 : pid_(base::kNullProcessId) | |
| 113 #if defined(OS_MACOSX) | |
| 114 , plugin_cursor_visible_(true) | |
| 115 #endif | |
| 116 { | |
| 117 process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_PLUGIN, this)); | |
| 118 } | |
| 119 | |
| 120 PluginProcessHost::~PluginProcessHost() { | |
| 121 #if defined(OS_MACOSX) | |
| 122 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 123 // If the plugin process crashed but had fullscreen windows open at the time, | |
| 124 // make sure that the menu bar is visible. | |
| 125 for (size_t i = 0; i < plugin_fullscreen_windows_set_.size(); ++i) { | |
| 126 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 127 base::Bind(base::mac::ReleaseFullScreen, | |
| 128 base::mac::kFullScreenModeHideAll)); | |
| 129 } | |
| 130 // If the plugin hid the cursor, reset that. | |
| 131 if (!plugin_cursor_visible_) { | |
| 132 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 133 base::Bind(base::mac::SetCursorVisibility, true)); | |
| 134 } | |
| 135 #endif | |
| 136 // Cancel all pending and sent requests. | |
| 137 CancelRequests(); | |
| 138 | |
| 139 { | |
| 140 base::AutoLock lock(g_process_webplugin_info_lock.Get()); | |
| 141 g_process_webplugin_info.Get()[pid_] = info_; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 bool PluginProcessHost::Send(IPC::Message* message) { | |
| 146 return process_->Send(message); | |
| 147 } | |
| 148 | |
| 149 bool PluginProcessHost::Init(const WebPluginInfo& info) { | |
| 150 info_ = info; | |
| 151 process_->SetName(info_.name); | |
| 152 | |
| 153 std::string channel_id = process_->GetHost()->CreateChannel(); | |
| 154 if (channel_id.empty()) | |
| 155 return false; | |
| 156 | |
| 157 // Build command line for plugin. When we have a plugin launcher, we can't | |
| 158 // allow "self" on linux and we need the real file path. | |
| 159 const base::CommandLine& browser_command_line = | |
| 160 *base::CommandLine::ForCurrentProcess(); | |
| 161 base::CommandLine::StringType plugin_launcher = | |
| 162 browser_command_line.GetSwitchValueNative(switches::kPluginLauncher); | |
| 163 | |
| 164 #if defined(OS_LINUX) | |
| 165 int flags = plugin_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : | |
| 166 ChildProcessHost::CHILD_NORMAL; | |
| 167 #else | |
| 168 int flags = ChildProcessHost::CHILD_NORMAL; | |
| 169 #endif | |
| 170 | |
| 171 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags); | |
| 172 if (exe_path.empty()) | |
| 173 return false; | |
| 174 | |
| 175 base::CommandLine* cmd_line = new base::CommandLine(exe_path); | |
| 176 // Put the process type and plugin path first so they're easier to see | |
| 177 // in process listings using native process management tools. | |
| 178 cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kPluginProcess); | |
| 179 cmd_line->AppendSwitchPath(switches::kPluginPath, info.path); | |
| 180 | |
| 181 #if defined(OS_WIN) | |
| 182 if (GetContentClient()->browser()->ShouldUseWindowsPrefetchArgument()) | |
| 183 cmd_line->AppendArg(switches::kPrefetchArgumentOther); | |
| 184 #endif // defined(OS_WIN) | |
| 185 | |
| 186 // Propagate the following switches to the plugin command line (along with | |
| 187 // any associated values) if present in the browser command line | |
| 188 static const char* const kSwitchNames[] = { | |
| 189 switches::kDisableBreakpad, | |
| 190 switches::kDisableDirectNPAPIRequests, | |
| 191 switches::kEnableStatsTable, | |
| 192 switches::kFullMemoryCrashReport, | |
| 193 switches::kLoggingLevel, | |
| 194 switches::kLogPluginMessages, | |
| 195 switches::kNoSandbox, | |
| 196 switches::kPluginStartupDialog, | |
| 197 switches::kTraceConfigFile, | |
| 198 switches::kTraceStartup, | |
| 199 switches::kUseGL, | |
| 200 switches::kForceDeviceScaleFactor, | |
| 201 #if defined(OS_MACOSX) | |
| 202 switches::kDisableCoreAnimationPlugins, | |
| 203 switches::kEnableSandboxLogging, | |
| 204 #endif | |
| 205 }; | |
| 206 | |
| 207 cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, | |
| 208 arraysize(kSwitchNames)); | |
| 209 | |
| 210 GpuDataManagerImpl::GetInstance()->AppendPluginCommandLine(cmd_line); | |
| 211 | |
| 212 // If specified, prepend a launcher program to the command line. | |
| 213 if (!plugin_launcher.empty()) | |
| 214 cmd_line->PrependWrapper(plugin_launcher); | |
| 215 | |
| 216 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); | |
| 217 if (!locale.empty()) { | |
| 218 // Pass on the locale so the null plugin will use the right language in the | |
| 219 // prompt to install the desired plugin. | |
| 220 cmd_line->AppendSwitchASCII(switches::kLang, locale); | |
| 221 } | |
| 222 | |
| 223 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); | |
| 224 | |
| 225 // The plugin needs to be shutdown gracefully, i.e. NP_Shutdown needs to be | |
| 226 // called on the plugin. The plugin process exits when it receives the | |
| 227 // OnChannelError notification indicating that the browser plugin channel has | |
| 228 // been destroyed. | |
| 229 bool terminate_on_shutdown = false; | |
| 230 process_->Launch( | |
| 231 new PluginSandboxedProcessLauncherDelegate(process_->GetHost()), | |
| 232 cmd_line, | |
| 233 terminate_on_shutdown); | |
| 234 | |
| 235 ResourceMessageFilter::GetContextsCallback get_contexts_callback( | |
| 236 base::Bind(&PluginProcessHost::GetContexts, | |
| 237 base::Unretained(this))); | |
| 238 | |
| 239 // TODO(jam): right now we're passing NULL for appcache, blob storage, file | |
| 240 // system and host zoom level context. If NPAPI plugins actually use this, | |
| 241 // we'll have to plumb them. | |
| 242 ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( | |
| 243 process_->GetData().id, PROCESS_TYPE_PLUGIN, NULL, NULL, NULL, NULL, NULL, | |
| 244 get_contexts_callback); | |
| 245 process_->AddFilter(resource_message_filter); | |
| 246 return true; | |
| 247 } | |
| 248 | |
| 249 void PluginProcessHost::ForceShutdown() { | |
| 250 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 251 Send(new PluginProcessMsg_NotifyRenderersOfPendingShutdown()); | |
| 252 process_->ForceShutdown(); | |
| 253 } | |
| 254 | |
| 255 bool PluginProcessHost::OnMessageReceived(const IPC::Message& msg) { | |
| 256 bool handled = true; | |
| 257 IPC_BEGIN_MESSAGE_MAP(PluginProcessHost, msg) | |
| 258 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelCreated, OnChannelCreated) | |
| 259 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelDestroyed, | |
| 260 OnChannelDestroyed) | |
| 261 #if defined(OS_MACOSX) | |
| 262 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginShowWindow, | |
| 263 OnPluginShowWindow) | |
| 264 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginHideWindow, | |
| 265 OnPluginHideWindow) | |
| 266 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSetCursorVisibility, | |
| 267 OnPluginSetCursorVisibility) | |
| 268 #endif | |
| 269 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 270 IPC_END_MESSAGE_MAP() | |
| 271 | |
| 272 return handled; | |
| 273 } | |
| 274 | |
| 275 void PluginProcessHost::OnChannelConnected(int32_t peer_pid) { | |
| 276 for (size_t i = 0; i < pending_requests_.size(); ++i) { | |
| 277 RequestPluginChannel(pending_requests_[i]); | |
| 278 } | |
| 279 | |
| 280 pending_requests_.clear(); | |
| 281 | |
| 282 pid_ = peer_pid; | |
| 283 { | |
| 284 base::AutoLock lock(g_process_webplugin_info_lock.Get()); | |
| 285 g_process_webplugin_info.Get()[pid_] = info_; | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 void PluginProcessHost::OnChannelError() { | |
| 290 CancelRequests(); | |
| 291 } | |
| 292 | |
| 293 bool PluginProcessHost::CanShutdown() { | |
| 294 return sent_requests_.empty(); | |
| 295 } | |
| 296 | |
| 297 void PluginProcessHost::OnProcessCrashed(int exit_code) { | |
| 298 PluginServiceImpl::GetInstance()->RegisterPluginCrash(info_.path); | |
| 299 } | |
| 300 | |
| 301 void PluginProcessHost::CancelRequests() { | |
| 302 for (size_t i = 0; i < pending_requests_.size(); ++i) | |
| 303 pending_requests_[i]->OnError(); | |
| 304 pending_requests_.clear(); | |
| 305 | |
| 306 while (!sent_requests_.empty()) { | |
| 307 Client* client = sent_requests_.front(); | |
| 308 if (client) | |
| 309 client->OnError(); | |
| 310 sent_requests_.pop_front(); | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 void PluginProcessHost::OpenChannelToPlugin(Client* client) { | |
| 315 BrowserThread::PostTask( | |
| 316 BrowserThread::UI, FROM_HERE, | |
| 317 base::Bind(&BrowserChildProcessHostImpl::NotifyProcessInstanceCreated, | |
| 318 process_->GetData())); | |
| 319 client->SetPluginInfo(info_); | |
| 320 if (process_->GetHost()->IsChannelOpening()) { | |
| 321 // The channel is already in the process of being opened. Put | |
| 322 // this "open channel" request into a queue of requests that will | |
| 323 // be run once the channel is open. | |
| 324 pending_requests_.push_back(client); | |
| 325 return; | |
| 326 } | |
| 327 | |
| 328 // We already have an open channel, send a request right away to plugin. | |
| 329 RequestPluginChannel(client); | |
| 330 } | |
| 331 | |
| 332 void PluginProcessHost::CancelPendingRequest(Client* client) { | |
| 333 std::vector<Client*>::iterator it = pending_requests_.begin(); | |
| 334 while (it != pending_requests_.end()) { | |
| 335 if (client == *it) { | |
| 336 pending_requests_.erase(it); | |
| 337 return; | |
| 338 } | |
| 339 ++it; | |
| 340 } | |
| 341 DCHECK(it != pending_requests_.end()); | |
| 342 } | |
| 343 | |
| 344 void PluginProcessHost::CancelSentRequest(Client* client) { | |
| 345 std::list<Client*>::iterator it = sent_requests_.begin(); | |
| 346 while (it != sent_requests_.end()) { | |
| 347 if (client == *it) { | |
| 348 *it = NULL; | |
| 349 return; | |
| 350 } | |
| 351 ++it; | |
| 352 } | |
| 353 DCHECK(it != sent_requests_.end()); | |
| 354 } | |
| 355 | |
| 356 void PluginProcessHost::RequestPluginChannel(Client* client) { | |
| 357 // We can't send any sync messages from the browser because it might lead to | |
| 358 // a hang. However this async messages must be answered right away by the | |
| 359 // plugin process (i.e. unblocks a Send() call like a sync message) otherwise | |
| 360 // a deadlock can occur if the plugin creation request from the renderer is | |
| 361 // a result of a sync message by the plugin process. | |
| 362 PluginProcessMsg_CreateChannel* msg = | |
| 363 new PluginProcessMsg_CreateChannel( | |
| 364 client->ID(), | |
| 365 client->OffTheRecord()); | |
| 366 msg->set_unblock(true); | |
| 367 if (Send(msg)) { | |
| 368 sent_requests_.push_back(client); | |
| 369 client->OnSentPluginChannelRequest(); | |
| 370 } else { | |
| 371 client->OnError(); | |
| 372 } | |
| 373 } | |
| 374 | |
| 375 void PluginProcessHost::OnChannelCreated( | |
| 376 const IPC::ChannelHandle& channel_handle) { | |
| 377 Client* client = sent_requests_.front(); | |
| 378 | |
| 379 if (client) { | |
| 380 if (!resource_context_map_.count(client->ID())) { | |
| 381 ResourceContextEntry entry; | |
| 382 entry.ref_count = 0; | |
| 383 entry.resource_context = client->GetResourceContext(); | |
| 384 resource_context_map_[client->ID()] = entry; | |
| 385 } | |
| 386 resource_context_map_[client->ID()].ref_count++; | |
| 387 client->OnChannelOpened(channel_handle); | |
| 388 } | |
| 389 sent_requests_.pop_front(); | |
| 390 } | |
| 391 | |
| 392 void PluginProcessHost::OnChannelDestroyed(int renderer_id) { | |
| 393 resource_context_map_[renderer_id].ref_count--; | |
| 394 if (!resource_context_map_[renderer_id].ref_count) | |
| 395 resource_context_map_.erase(renderer_id); | |
| 396 } | |
| 397 | |
| 398 void PluginProcessHost::GetContexts(ResourceType resource_type, | |
| 399 int origin_pid, | |
| 400 ResourceContext** resource_context, | |
| 401 net::URLRequestContext** request_context) { | |
| 402 *resource_context = | |
| 403 resource_context_map_[origin_pid].resource_context; | |
| 404 *request_context = (*resource_context)->GetRequestContext(); | |
| 405 } | |
| 406 | |
| 407 } // namespace content | |
| OLD | NEW |