// Copyright (c) 2009 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/common/child_process_host.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/singleton.h" #include "chrome/browser/chrome_thread.h" #include "chrome/common/ipc_logging.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" #include "chrome/common/process_watcher.h" typedef std::list ChildProcessList; // The NotificationTask is used to notify about plugin process connection/ // disconnection. It is needed because the notifications in the // NotificationService must happen in the main thread. class ChildNotificationTask : public Task { public: ChildNotificationTask( NotificationType notification_type, ChildProcessInfo* info) : notification_type_(notification_type), info_(*info) { } virtual void Run() { NotificationService::current()-> Notify(notification_type_, NotificationService::AllSources(), Details(&info_)); } private: NotificationType notification_type_; ChildProcessInfo info_; }; ChildProcessHost::ChildProcessHost( ProcessType type, MessageLoop* main_message_loop) : ChildProcessInfo(type), main_message_loop_(main_message_loop), opening_channel_(false), ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)) { Singleton::get()->push_back(this); } ChildProcessHost::~ChildProcessHost() { Singleton::get()->remove(this); if (handle()) { watcher_.StopWatching(); ProcessWatcher::EnsureProcessTerminated(handle()); } } bool ChildProcessHost::CreateChannel() { channel_id_ = GenerateRandomChannelID(this); channel_.reset(new IPC::Channel( channel_id_, IPC::Channel::MODE_SERVER, &listener_)); if (!channel_->Connect()) return false; opening_channel_ = true; return true; } void ChildProcessHost::SetHandle(base::ProcessHandle process) { DCHECK(handle() == NULL); set_handle(process); watcher_.StartWatching(process, this); } void ChildProcessHost::InstanceCreated() { Notify(NotificationType::CHILD_INSTANCE_CREATED); } bool ChildProcessHost::Send(IPC::Message* msg) { if (!channel_.get()) { delete msg; return false; } return channel_->Send(msg); } void ChildProcessHost::Notify(NotificationType type) { main_message_loop_->PostTask( FROM_HERE, new ChildNotificationTask(type, this)); } void ChildProcessHost::OnObjectSignaled(HANDLE object) { DCHECK(handle()); DCHECK_EQ(object, handle()); bool did_crash = base::DidProcessCrash(object); if (did_crash) { // Report that this child process crashed. Notify(NotificationType::CHILD_PROCESS_CRASHED); } // Notify in the main loop of the disconnection. Notify(NotificationType::CHILD_PROCESS_HOST_DISCONNECTED); delete this; } ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host) : host_(host) { } void ChildProcessHost::ListenerHook::OnMessageReceived(const IPC::Message& msg) { #ifdef IPC_MESSAGE_LOG_ENABLED IPC::Logging* logger = IPC::Logging::current(); if (msg.type() == IPC_LOGGING_ID) { logger->OnReceivedLoggingMessage(msg); return; } if (logger->Enabled()) logger->OnPreDispatchMessage(msg); #endif host_->OnMessageReceived(msg); #ifdef IPC_MESSAGE_LOG_ENABLED if (logger->Enabled()) logger->OnPostDispatchMessage(msg, host_->channel_id_); #endif } void ChildProcessHost::ListenerHook::OnChannelConnected(int32 peer_pid) { host_->opening_channel_ = false; host_->OnChannelConnected(peer_pid); // Notify in the main loop of the connection. host_->Notify(NotificationType::CHILD_PROCESS_HOST_CONNECTED); } void ChildProcessHost::ListenerHook::OnChannelError() { host_->opening_channel_ = false; host_->OnChannelError(); } ChildProcessHost::Iterator::Iterator() : all_(true) { iterator_ = Singleton::get()->begin(); DCHECK(MessageLoop::current() == ChromeThread::GetMessageLoop(ChromeThread::IO)) << "ChildProcessInfo::Iterator must be used on the IO thread."; } ChildProcessHost::Iterator::Iterator(ProcessType type) : all_(false), type_(type) { iterator_ = Singleton::get()->begin(); DCHECK(MessageLoop::current() == ChromeThread::GetMessageLoop(ChromeThread::IO)) << "ChildProcessInfo::Iterator must be used on the IO thread."; } ChildProcessInfo* ChildProcessHost::Iterator::operator++() { do { ++iterator_; if (Done()) break; if (!all_ && (*iterator_)->type() != type_) continue; return *iterator_; } while (true); return NULL; } bool ChildProcessHost::Iterator::Done() { return iterator_ == Singleton::get()->end(); }