// Copyright (c) 2010 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 "chrome/browser/gpu_process_host.h" #include "base/command_line.h" #include "base/thread.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/gpu_process_host_ui_shim.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/gpu_messages.h" #include "chrome/common/render_messages.h" #include "ipc/ipc_switches.h" namespace { // Tasks used by this file class RouteOnUIThreadTask : public Task { public: explicit RouteOnUIThreadTask(const IPC::Message& msg) { msg_ = new IPC::Message(msg); } private: void Run() { GpuProcessHostUIShim::Get()->OnMessageReceived(*msg_); delete msg_; msg_ = NULL; } IPC::Message* msg_; }; // Global GpuProcessHost instance. // We can not use Singleton because that gets // terminated on the wrong thread (main thread). We need the // GpuProcessHost to be terminated on the same thread on which it is // initialized, the IO thread. static GpuProcessHost* sole_instance_ = NULL; } // anonymous namespace GpuProcessHost::GpuProcessHost() : ChildProcessHost(GPU_PROCESS, NULL), initialized_(false), initialized_successfully_(false) { DCHECK_EQ(sole_instance_, static_cast(NULL)); } GpuProcessHost::~GpuProcessHost() { while (!queued_synchronization_replies_.empty()) { delete queued_synchronization_replies_.front().reply; queued_synchronization_replies_.pop(); } DCHECK_EQ(sole_instance_, this); sole_instance_ = NULL; } bool GpuProcessHost::EnsureInitialized() { if (!initialized_) { initialized_ = true; initialized_successfully_ = Init(); } return initialized_successfully_; } bool GpuProcessHost::Init() { if (!CreateChannel()) return false; const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); std::wstring gpu_launcher = browser_command_line.GetSwitchValue(switches::kGpuLauncher); FilePath exe_path = ChildProcessHost::GetChildPath(gpu_launcher.empty()); if (exe_path.empty()) return false; CommandLine* cmd_line = new CommandLine(exe_path); cmd_line->AppendSwitchWithValue(switches::kProcessType, switches::kGpuProcess); cmd_line->AppendSwitchWithValue(switches::kProcessChannelID, ASCIIToWide(channel_id())); const CommandLine& browser_cmd_line = *CommandLine::ForCurrentProcess(); PropagateBrowserCommandLineToGpu(browser_cmd_line, cmd_line); // If specified, prepend a launcher program to the command line. if (!gpu_launcher.empty()) cmd_line->PrependWrapper(gpu_launcher); Launch( #if defined(OS_WIN) FilePath(), #elif defined(POSIX) false, // Never use the zygote (GPU plugin can't be sandboxed). base::environment_vector(), #endif cmd_line); return true; } // static GpuProcessHost* GpuProcessHost::Get() { if (sole_instance_ == NULL) sole_instance_ = new GpuProcessHost(); return sole_instance_; } bool GpuProcessHost::Send(IPC::Message* msg) { if (!EnsureInitialized()) return false; return ChildProcessHost::Send(msg); } void GpuProcessHost::OnMessageReceived(const IPC::Message& message) { if (message.routing_id() == MSG_ROUTING_CONTROL) { OnControlMessageReceived(message); } else { // Need to transfer this message to the UI thread and the // GpuProcessHostUIShim for dispatching via its message router. ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, new RouteOnUIThreadTask(message)); } } void GpuProcessHost::EstablishGpuChannel(int renderer_id, ResourceMessageFilter* filter) { if (Send(new GpuMsg_EstablishChannel(renderer_id))) { sent_requests_.push(ChannelRequest(filter)); } else { ReplyToRenderer(IPC::ChannelHandle(), filter); } } void GpuProcessHost::Synchronize(IPC::Message* reply, ResourceMessageFilter* filter) { queued_synchronization_replies_.push(SynchronizationRequest(reply, filter)); Send(new GpuMsg_Synchronize()); } void GpuProcessHost::OnControlMessageReceived(const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(GpuProcessHost, message) IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished) IPC_MESSAGE_HANDLER(GpuHostMsg_SynchronizeReply, OnSynchronizeReply) IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP() } void GpuProcessHost::OnChannelEstablished( const IPC::ChannelHandle& channel_handle) { const ChannelRequest& request = sent_requests_.front(); ReplyToRenderer(channel_handle, request.filter); sent_requests_.pop(); } void GpuProcessHost::OnSynchronizeReply() { const SynchronizationRequest& request = queued_synchronization_replies_.front(); request.filter->Send(request.reply); queued_synchronization_replies_.pop(); } void GpuProcessHost::ReplyToRenderer( const IPC::ChannelHandle& channel, ResourceMessageFilter* filter) { ViewMsg_GpuChannelEstablished* message = new ViewMsg_GpuChannelEstablished(channel); // If the renderer process is performing synchronous initialization, // it needs to handle this message before receiving the reply for // the synchronous ViewHostMsg_SynchronizeGpu message. message->set_unblock(true); filter->Send(message); } void GpuProcessHost::PropagateBrowserCommandLineToGpu( const CommandLine& browser_cmd, CommandLine* gpu_cmd) const { // Propagate the following switches to the GPU process command line (along // with any associated values) if present in the browser command line. static const char* const switch_names[] = { switches::kDisableLogging, switches::kEnableLogging, switches::kGpuStartupDialog, switches::kLoggingLevel, }; for (size_t i = 0; i < arraysize(switch_names); ++i) { if (browser_cmd.HasSwitch(switch_names[i])) { gpu_cmd->AppendSwitchWithValue(switch_names[i], browser_cmd.GetSwitchValueASCII(switch_names[i])); } } } URLRequestContext* GpuProcessHost::GetRequestContext( uint32 request_id, const ViewHostMsg_Resource_Request& request_data) { return NULL; } bool GpuProcessHost::CanShutdown() { return true; }