michael@0: // Copyright (c) 2009 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "chrome/common/child_process_host.h" michael@0: michael@0: #include "base/compiler_specific.h" michael@0: #include "base/logging.h" michael@0: #include "base/message_loop.h" michael@0: #include "base/process_util.h" michael@0: #include "base/singleton.h" michael@0: #include "base/waitable_event.h" michael@0: #include "mozilla/ipc/ProcessChild.h" michael@0: #include "mozilla/ipc/BrowserProcessSubThread.h" michael@0: #include "mozilla/ipc/Transport.h" michael@0: typedef mozilla::ipc::BrowserProcessSubThread ChromeThread; michael@0: #include "chrome/common/ipc_logging.h" michael@0: #include "chrome/common/notification_service.h" michael@0: #include "chrome/common/notification_type.h" michael@0: #include "chrome/common/process_watcher.h" michael@0: #include "chrome/common/result_codes.h" michael@0: michael@0: using mozilla::ipc::FileDescriptor; michael@0: michael@0: namespace { michael@0: typedef std::list ChildProcessList; michael@0: michael@0: // The NotificationTask is used to notify about plugin process connection/ michael@0: // disconnection. It is needed because the notifications in the michael@0: // NotificationService must happen in the main thread. michael@0: class ChildNotificationTask : public Task { michael@0: public: michael@0: ChildNotificationTask( michael@0: NotificationType notification_type, ChildProcessInfo* info) michael@0: : notification_type_(notification_type), info_(*info) { } michael@0: michael@0: virtual void Run() { michael@0: NotificationService::current()-> michael@0: Notify(notification_type_, NotificationService::AllSources(), michael@0: Details(&info_)); michael@0: } michael@0: michael@0: private: michael@0: NotificationType notification_type_; michael@0: ChildProcessInfo info_; michael@0: }; michael@0: michael@0: } // namespace michael@0: michael@0: michael@0: michael@0: ChildProcessHost::ChildProcessHost(ProcessType type) michael@0: : michael@0: ChildProcessInfo(type), michael@0: ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)), michael@0: opening_channel_(false), michael@0: process_event_(NULL) { michael@0: Singleton::get()->push_back(this); michael@0: } michael@0: michael@0: michael@0: ChildProcessHost::~ChildProcessHost() { michael@0: Singleton::get()->remove(this); michael@0: michael@0: if (handle()) { michael@0: watcher_.StopWatching(); michael@0: ProcessWatcher::EnsureProcessTerminated(handle()); michael@0: michael@0: #if defined(OS_WIN) michael@0: // Above call took ownership, so don't want WaitableEvent to assert because michael@0: // the handle isn't valid anymore. michael@0: process_event_->Release(); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: bool ChildProcessHost::CreateChannel() { michael@0: channel_id_ = GenerateRandomChannelID(this); michael@0: channel_.reset(new IPC::Channel( michael@0: channel_id_, IPC::Channel::MODE_SERVER, &listener_)); michael@0: if (!channel_->Connect()) michael@0: return false; michael@0: michael@0: opening_channel_ = true; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool ChildProcessHost::CreateChannel(FileDescriptor& aFileDescriptor) { michael@0: if (channel_.get()) { michael@0: channel_->Close(); michael@0: } michael@0: channel_.reset(mozilla::ipc::OpenDescriptor( michael@0: aFileDescriptor, IPC::Channel::MODE_SERVER)); michael@0: channel_->set_listener(&listener_); michael@0: if (!channel_->Connect()) { michael@0: return false; michael@0: } michael@0: michael@0: opening_channel_ = true; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void ChildProcessHost::SetHandle(base::ProcessHandle process) { michael@0: #if defined(OS_WIN) michael@0: process_event_.reset(new base::WaitableEvent(process)); michael@0: michael@0: DCHECK(!handle()); michael@0: set_handle(process); michael@0: watcher_.StartWatching(process_event_.get(), this); michael@0: #endif michael@0: } michael@0: michael@0: void ChildProcessHost::InstanceCreated() { michael@0: Notify(NotificationType::CHILD_INSTANCE_CREATED); michael@0: } michael@0: michael@0: bool ChildProcessHost::Send(IPC::Message* msg) { michael@0: if (!channel_.get()) { michael@0: delete msg; michael@0: return false; michael@0: } michael@0: return channel_->Send(msg); michael@0: } michael@0: michael@0: void ChildProcessHost::Notify(NotificationType type) { michael@0: MessageLoop* loop = ChromeThread::GetMessageLoop(ChromeThread::IO); michael@0: if (!loop) michael@0: loop = mozilla::ipc::ProcessChild::message_loop(); michael@0: if (!loop) michael@0: loop = MessageLoop::current(); michael@0: loop->PostTask( michael@0: FROM_HERE, new ChildNotificationTask(type, this)); michael@0: } michael@0: michael@0: void ChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event) { michael@0: #if defined(OS_WIN) michael@0: HANDLE object = event->handle(); michael@0: DCHECK(handle()); michael@0: DCHECK_EQ(object, handle()); michael@0: michael@0: bool did_crash = base::DidProcessCrash(NULL, object); michael@0: if (did_crash) { michael@0: // Report that this child process crashed. michael@0: Notify(NotificationType::CHILD_PROCESS_CRASHED); michael@0: } michael@0: // Notify in the main loop of the disconnection. michael@0: Notify(NotificationType::CHILD_PROCESS_HOST_DISCONNECTED); michael@0: #endif michael@0: } michael@0: michael@0: ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host) michael@0: : host_(host) { michael@0: } michael@0: michael@0: void ChildProcessHost::ListenerHook::OnMessageReceived( michael@0: const IPC::Message& msg) { michael@0: #ifdef IPC_MESSAGE_LOG_ENABLED michael@0: IPC::Logging* logger = IPC::Logging::current(); michael@0: if (msg.type() == IPC_LOGGING_ID) { michael@0: logger->OnReceivedLoggingMessage(msg); michael@0: return; michael@0: } michael@0: michael@0: if (logger->Enabled()) michael@0: logger->OnPreDispatchMessage(msg); michael@0: #endif michael@0: michael@0: bool msg_is_ok = true; michael@0: bool handled = false; michael@0: michael@0: if (!handled) { michael@0: host_->OnMessageReceived(msg); michael@0: } michael@0: michael@0: if (!msg_is_ok) michael@0: base::KillProcess(host_->handle(), ResultCodes::KILLED_BAD_MESSAGE, false); michael@0: michael@0: #ifdef IPC_MESSAGE_LOG_ENABLED michael@0: if (logger->Enabled()) michael@0: logger->OnPostDispatchMessage(msg, host_->channel_id_); michael@0: #endif michael@0: } michael@0: michael@0: void ChildProcessHost::ListenerHook::OnChannelConnected(int32_t peer_pid) { michael@0: host_->opening_channel_ = false; michael@0: host_->OnChannelConnected(peer_pid); michael@0: michael@0: // Notify in the main loop of the connection. michael@0: host_->Notify(NotificationType::CHILD_PROCESS_HOST_CONNECTED); michael@0: } michael@0: michael@0: void ChildProcessHost::ListenerHook::OnChannelError() { michael@0: host_->opening_channel_ = false; michael@0: host_->OnChannelError(); michael@0: } michael@0: michael@0: void ChildProcessHost::ListenerHook::GetQueuedMessages(std::queue& queue) { michael@0: host_->GetQueuedMessages(queue); michael@0: } michael@0: michael@0: ChildProcessHost::Iterator::Iterator() : all_(true) { michael@0: iterator_ = Singleton::get()->begin(); michael@0: } michael@0: michael@0: ChildProcessHost::Iterator::Iterator(ProcessType type) michael@0: : all_(false), type_(type) { michael@0: iterator_ = Singleton::get()->begin(); michael@0: if (!Done() && (*iterator_)->type() != type_) michael@0: ++(*this); michael@0: } michael@0: michael@0: ChildProcessHost* ChildProcessHost::Iterator::operator++() { michael@0: do { michael@0: ++iterator_; michael@0: if (Done()) michael@0: break; michael@0: michael@0: if (!all_ && (*iterator_)->type() != type_) michael@0: continue; michael@0: michael@0: return *iterator_; michael@0: } while (true); michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: bool ChildProcessHost::Iterator::Done() { michael@0: return iterator_ == Singleton::get()->end(); michael@0: }