summaryrefslogtreecommitdiffstats
path: root/ipc/chromium/src/chrome/common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /ipc/chromium/src/chrome/common
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/chromium/src/chrome/common')
-rw-r--r--ipc/chromium/src/chrome/common/child_process.cc29
-rw-r--r--ipc/chromium/src/chrome/common/child_process.h45
-rw-r--r--ipc/chromium/src/chrome/common/child_process_host.cc74
-rw-r--r--ipc/chromium/src/chrome/common/child_process_host.h82
-rw-r--r--ipc/chromium/src/chrome/common/child_thread.cc46
-rw-r--r--ipc/chromium/src/chrome/common/child_thread.h55
-rw-r--r--ipc/chromium/src/chrome/common/chrome_switches.cc18
-rw-r--r--ipc/chromium/src/chrome/common/chrome_switches.h22
-rw-r--r--ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc126
-rw-r--r--ipc/chromium/src/chrome/common/file_descriptor_set_posix.h103
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel.h201
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_posix.cc959
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_posix.h174
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_utils.cc42
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_utils.h21
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_win.cc664
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_win.h148
-rw-r--r--ipc/chromium/src/chrome/common/ipc_message.cc231
-rw-r--r--ipc/chromium/src/chrome/common/ipc_message.h464
-rw-r--r--ipc/chromium/src/chrome/common/ipc_message_utils.h495
-rw-r--r--ipc/chromium/src/chrome/common/mach_ipc_mac.h307
-rw-r--r--ipc/chromium/src/chrome/common/mach_ipc_mac.mm303
-rw-r--r--ipc/chromium/src/chrome/common/mach_message_source_mac.cc61
-rw-r--r--ipc/chromium/src/chrome/common/mach_message_source_mac.h60
-rw-r--r--ipc/chromium/src/chrome/common/process_watcher.h38
-rw-r--r--ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc188
-rw-r--r--ipc/chromium/src/chrome/common/process_watcher_win.cc117
27 files changed, 5073 insertions, 0 deletions
diff --git a/ipc/chromium/src/chrome/common/child_process.cc b/ipc/chromium/src/chrome/common/child_process.cc
new file mode 100644
index 0000000000..637564c30c
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_process.cc
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.h"
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "chrome/common/child_thread.h"
+
+ChildProcess* ChildProcess::child_process_;
+
+ChildProcess::ChildProcess(ChildThread* child_thread)
+ : child_thread_(child_thread) {
+ DCHECK(!child_process_);
+ child_process_ = this;
+ if (child_thread_.get()) // null in unittests.
+ child_thread_->Run();
+}
+
+ChildProcess::~ChildProcess() {
+ DCHECK(child_process_ == this);
+
+ if (child_thread_.get()) child_thread_->Stop();
+
+ child_process_ = NULL;
+}
diff --git a/ipc/chromium/src/chrome/common/child_process.h b/ipc/chromium/src/chrome/common/child_process.h
new file mode 100644
index 0000000000..7ce4cffa81
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_process.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_CHILD_PROCESS_H__
+#define CHROME_COMMON_CHILD_PROCESS_H__
+
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+#include "base/waitable_event.h"
+#include "mozilla/UniquePtr.h"
+
+class ChildThread;
+
+// Base class for child processes of the browser process (i.e. renderer and
+// plugin host). This is a singleton object for each child process.
+class ChildProcess {
+ public:
+ // Child processes should have an object that derives from this class. The
+ // constructor will return once ChildThread has started.
+ explicit ChildProcess(ChildThread* child_thread);
+ virtual ~ChildProcess();
+
+ // Getter for this process' main thread.
+ ChildThread* child_thread() { return child_thread_.get(); }
+
+ // Getter for the one ChildProcess object for this process.
+ static ChildProcess* current() { return child_process_; }
+
+ private:
+ // NOTE: make sure that child_thread_ is listed before shutdown_event_, since
+ // it depends on it (indirectly through IPC::SyncChannel).
+ mozilla::UniquePtr<ChildThread> child_thread_;
+
+ // The singleton instance for this process.
+ static ChildProcess* child_process_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ChildProcess);
+};
+
+#endif // CHROME_COMMON_CHILD_PROCESS_H__
diff --git a/ipc/chromium/src/chrome/common/child_process_host.cc b/ipc/chromium/src/chrome/common/child_process_host.cc
new file mode 100644
index 0000000000..dcbf14196a
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_process_host.cc
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// 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/process_util.h"
+#include "base/waitable_event.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/ipc/Transport.h"
+typedef mozilla::ipc::BrowserProcessSubThread ChromeThread;
+#include "chrome/common/process_watcher.h"
+
+using mozilla::ipc::FileDescriptor;
+
+ChildProcessHost::ChildProcessHost()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)),
+ opening_channel_(false) {}
+
+ChildProcessHost::~ChildProcessHost() {}
+
+bool ChildProcessHost::CreateChannel() {
+ channel_id_ = IPC::Channel::GenerateVerifiedChannelID();
+ channel_.reset(
+ new IPC::Channel(channel_id_, IPC::Channel::MODE_SERVER, &listener_));
+ if (!channel_->Connect()) return false;
+
+ opening_channel_ = true;
+
+ return true;
+}
+
+bool ChildProcessHost::CreateChannel(FileDescriptor& aFileDescriptor) {
+ if (channel_.get()) {
+ channel_->Close();
+ }
+ channel_ =
+ mozilla::ipc::OpenDescriptor(aFileDescriptor, IPC::Channel::MODE_SERVER);
+ if (!channel_->Connect()) {
+ return false;
+ }
+
+ opening_channel_ = true;
+
+ return true;
+}
+
+ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host)
+ : host_(host) {}
+
+void ChildProcessHost::ListenerHook::OnMessageReceived(IPC::Message&& msg) {
+ host_->OnMessageReceived(std::move(msg));
+}
+
+void ChildProcessHost::ListenerHook::OnChannelConnected(int32_t peer_pid) {
+ host_->opening_channel_ = false;
+ host_->OnChannelConnected(peer_pid);
+}
+
+void ChildProcessHost::ListenerHook::OnChannelError() {
+ host_->opening_channel_ = false;
+ host_->OnChannelError();
+}
+
+void ChildProcessHost::ListenerHook::GetQueuedMessages(
+ std::queue<IPC::Message>& queue) {
+ host_->GetQueuedMessages(queue);
+}
diff --git a/ipc/chromium/src/chrome/common/child_process_host.h b/ipc/chromium/src/chrome/common/child_process_host.h
new file mode 100644
index 0000000000..d96bc9cd0b
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_process_host.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// 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.
+
+#ifndef CHROME_COMMON_CHILD_PROCESS_HOST_H_
+#define CHROME_COMMON_CHILD_PROCESS_HOST_H_
+
+#include "build/build_config.h"
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "chrome/common/ipc_channel.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace ipc {
+class FileDescriptor;
+}
+} // namespace mozilla
+
+// Plugins/workers and other child processes that live on the IO thread should
+// derive from this class.
+class ChildProcessHost : public IPC::Channel::Listener {
+ public:
+ virtual ~ChildProcessHost();
+
+ using ChannelId = IPC::Channel::ChannelId;
+
+ protected:
+ explicit ChildProcessHost();
+
+ // Derived classes return true if it's ok to shut down the child process.
+ virtual bool CanShutdown() = 0;
+
+ // Creates the IPC channel. Returns true iff it succeeded.
+ bool CreateChannel();
+
+ bool CreateChannel(mozilla::ipc::FileDescriptor& aFileDescriptor);
+
+ // IPC::Channel::Listener implementation:
+ virtual void OnMessageReceived(IPC::Message&& msg) override {}
+ virtual void OnChannelConnected(int32_t peer_pid) override {}
+ virtual void OnChannelError() override {}
+
+ bool opening_channel() { return opening_channel_; }
+ const ChannelId& channel_id() { return channel_id_; }
+
+ IPC::Channel* channelp() const { return channel_.get(); }
+ mozilla::UniquePtr<IPC::Channel> TakeChannel() { return std::move(channel_); }
+
+ private:
+ // By using an internal class as the IPC::Channel::Listener, we can intercept
+ // OnMessageReceived/OnChannelConnected and do our own processing before
+ // calling the subclass' implementation.
+ class ListenerHook : public IPC::Channel::Listener {
+ public:
+ explicit ListenerHook(ChildProcessHost* host);
+ virtual void OnMessageReceived(IPC::Message&& msg) override;
+ virtual void OnChannelConnected(int32_t peer_pid) override;
+ virtual void OnChannelError() override;
+ virtual void GetQueuedMessages(std::queue<IPC::Message>& queue) override;
+
+ private:
+ ChildProcessHost* host_;
+ };
+
+ ListenerHook listener_;
+
+ // True while we're waiting the channel to be opened.
+ bool opening_channel_;
+
+ // The IPC::Channel.
+ mozilla::UniquePtr<IPC::Channel> channel_;
+
+ // IPC Channel's id.
+ ChannelId channel_id_;
+};
+
+#endif // CHROME_COMMON_CHILD_PROCESS_HOST_H_
diff --git a/ipc/chromium/src/chrome/common/child_thread.cc b/ipc/chromium/src/chrome/common/child_thread.cc
new file mode 100644
index 0000000000..e8426b20b6
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_thread.cc
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// 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_thread.h"
+
+#include "chrome/common/child_process.h"
+
+ChildThread::ChildThread(Thread::Options options)
+ : Thread("IPC I/O Child"),
+ owner_loop_(MessageLoop::current()),
+ options_(options) {
+ DCHECK(owner_loop_);
+ channel_name_ = IPC::Channel::ChannelIDForCurrentProcess();
+}
+
+ChildThread::~ChildThread() {}
+
+bool ChildThread::Run() {
+ bool r = StartWithOptions(options_);
+ return r;
+}
+
+void ChildThread::OnChannelError() {
+ RefPtr<mozilla::Runnable> task = new MessageLoop::QuitTask();
+ owner_loop_->PostTask(task.forget());
+}
+
+void ChildThread::OnMessageReceived(IPC::Message&& msg) {}
+
+ChildThread* ChildThread::current() {
+ return ChildProcess::current()->child_thread();
+}
+
+void ChildThread::Init() {
+ channel_ = mozilla::MakeUnique<IPC::Channel>(channel_name_,
+ IPC::Channel::MODE_CLIENT, this);
+}
+
+void ChildThread::CleanUp() {
+ // Need to destruct the SyncChannel to the browser before we go away because
+ // it caches a pointer to this thread.
+ channel_ = nullptr;
+}
diff --git a/ipc/chromium/src/chrome/common/child_thread.h b/ipc/chromium/src/chrome/common/child_thread.h
new file mode 100644
index 0000000000..0261a1229f
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_thread.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// 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.
+
+#ifndef CHROME_COMMON_CHILD_THREAD_H_
+#define CHROME_COMMON_CHILD_THREAD_H_
+
+#include "base/thread.h"
+#include "chrome/common/ipc_channel.h"
+#include "mozilla/UniquePtr.h"
+
+class ResourceDispatcher;
+
+// Child processes's background thread should derive from this class.
+class ChildThread : public IPC::Channel::Listener, public base::Thread {
+ public:
+ // Creates the thread.
+ explicit ChildThread(Thread::Options options);
+ virtual ~ChildThread();
+
+ protected:
+ friend class ChildProcess;
+
+ // Starts the thread.
+ bool Run();
+
+ protected:
+ // Returns the one child thread.
+ static ChildThread* current();
+
+ mozilla::UniquePtr<IPC::Channel> TakeChannel() { return std::move(channel_); }
+
+ // Thread implementation.
+ virtual void Init() override;
+ virtual void CleanUp() override;
+
+ private:
+ // IPC::Channel::Listener implementation:
+ virtual void OnMessageReceived(IPC::Message&& msg) override;
+ virtual void OnChannelError() override;
+
+ // The message loop used to run tasks on the thread that started this thread.
+ MessageLoop* owner_loop_;
+
+ IPC::Channel::ChannelId channel_name_;
+ mozilla::UniquePtr<IPC::Channel> channel_;
+
+ Thread::Options options_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ChildThread);
+};
+
+#endif // CHROME_COMMON_CHILD_THREAD_H_
diff --git a/ipc/chromium/src/chrome/common/chrome_switches.cc b/ipc/chromium/src/chrome/common/chrome_switches.cc
new file mode 100644
index 0000000000..185934c87d
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/chrome_switches.cc
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-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/chrome_switches.h"
+
+namespace switches {
+
+// Can't find the switch you are looking for? try looking in
+// base/base_switches.cc instead.
+
+// The value of this switch tells the child process which
+// IPC channel the browser expects to use to communicate with it.
+const wchar_t kProcessChannelID[] = L"channel";
+
+} // namespace switches
diff --git a/ipc/chromium/src/chrome/common/chrome_switches.h b/ipc/chromium/src/chrome/common/chrome_switches.h
new file mode 100644
index 0000000000..d0a60508c0
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/chrome_switches.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-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.
+
+// Defines all the command-line switches used by Chrome.
+
+#ifndef CHROME_COMMON_CHROME_SWITCHES_H__
+#define CHROME_COMMON_CHROME_SWITCHES_H__
+
+#if defined(COMPILER_MSVC)
+# include <string.h>
+#endif
+
+namespace switches {
+
+extern const wchar_t kProcessChannelID[];
+
+} // namespace switches
+
+#endif // CHROME_COMMON_CHROME_SWITCHES_H__
diff --git a/ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc
new file mode 100644
index 0000000000..6c13d2953b
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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/file_descriptor_set_posix.h"
+
+#include "base/eintr_wrapper.h"
+#include "base/logging.h"
+
+#include <unistd.h>
+
+FileDescriptorSet::FileDescriptorSet() : consumed_descriptor_highwater_(0) {}
+
+FileDescriptorSet::~FileDescriptorSet() {
+ if (consumed_descriptor_highwater_ == descriptors_.size()) return;
+
+ CHROMIUM_LOG(WARNING)
+ << "FileDescriptorSet destroyed with unconsumed descriptors";
+
+ // We close all the descriptors where the close flag is set. If this
+ // message should have been transmitted, then closing those with close
+ // flags set mirrors the expected behaviour.
+ //
+ // If this message was received with more descriptors than expected
+ // (which could a DOS against the browser by a rogue renderer) then all
+ // the descriptors have their close flag set and we free all the extra
+ // kernel resources.
+ for (unsigned i = consumed_descriptor_highwater_; i < descriptors_.size();
+ ++i) {
+ if (descriptors_[i].auto_close) IGNORE_EINTR(close(descriptors_[i].fd));
+ }
+}
+
+void FileDescriptorSet::CopyFrom(const FileDescriptorSet& other) {
+ for (std::vector<base::FileDescriptor>::const_iterator i =
+ other.descriptors_.begin();
+ i != other.descriptors_.end(); ++i) {
+ int fd = IGNORE_EINTR(dup(i->fd));
+ AddAndAutoClose(fd);
+ }
+}
+
+bool FileDescriptorSet::Add(int fd) {
+ if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) return false;
+
+ struct base::FileDescriptor sd;
+ sd.fd = fd;
+ sd.auto_close = false;
+ descriptors_.push_back(sd);
+ return true;
+}
+
+bool FileDescriptorSet::AddAndAutoClose(int fd) {
+ if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) return false;
+
+ struct base::FileDescriptor sd;
+ sd.fd = fd;
+ sd.auto_close = true;
+ descriptors_.push_back(sd);
+ DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE);
+ return true;
+}
+
+int FileDescriptorSet::GetDescriptorAt(unsigned index) const {
+ if (index >= descriptors_.size()) return -1;
+
+ // We should always walk the descriptors in order, so it's reasonable to
+ // enforce this. Consider the case where a compromised renderer sends us
+ // the following message:
+ //
+ // ExampleMsg:
+ // num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m}
+ //
+ // Here the renderer sent us a message which should have a descriptor, but
+ // actually sent two in an attempt to fill our fd table and kill us. By
+ // setting the index of the descriptor in the message to 1 (it should be
+ // 0), we would record a highwater of 1 and then consider all the
+ // descriptors to have been used.
+ //
+ // So we can either track of the use of each descriptor in a bitset, or we
+ // can enforce that we walk the indexes strictly in order.
+ //
+ // There's one more wrinkle: When logging messages, we may reparse them. So
+ // we have an exception: When the consumed_descriptor_highwater_ is at the
+ // end of the array and index 0 is requested, we reset the highwater value.
+ if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size())
+ consumed_descriptor_highwater_ = 0;
+
+ if (index != consumed_descriptor_highwater_) return -1;
+
+ consumed_descriptor_highwater_ = index + 1;
+ return descriptors_[index].fd;
+}
+
+void FileDescriptorSet::GetDescriptors(int* buffer) const {
+ for (std::vector<base::FileDescriptor>::const_iterator i =
+ descriptors_.begin();
+ i != descriptors_.end(); ++i) {
+ *(buffer++) = i->fd;
+ }
+}
+
+void FileDescriptorSet::CommitAll() {
+ for (std::vector<base::FileDescriptor>::iterator i = descriptors_.begin();
+ i != descriptors_.end(); ++i) {
+ if (i->auto_close) IGNORE_EINTR(close(i->fd));
+ }
+ descriptors_.clear();
+ consumed_descriptor_highwater_ = 0;
+}
+
+void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) {
+ DCHECK_LE(count, MAX_DESCRIPTORS_PER_MESSAGE);
+ DCHECK_EQ(descriptors_.size(), 0u);
+ DCHECK_EQ(consumed_descriptor_highwater_, 0u);
+
+ descriptors_.reserve(count);
+ for (unsigned i = 0; i < count; ++i) {
+ struct base::FileDescriptor sd;
+ sd.fd = buffer[i];
+ sd.auto_close = true;
+ descriptors_.push_back(sd);
+ }
+}
diff --git a/ipc/chromium/src/chrome/common/file_descriptor_set_posix.h b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.h
new file mode 100644
index 0000000000..4192f5b56b
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-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.
+
+#ifndef CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
+#define CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_descriptor_posix.h"
+#include "nsISupportsImpl.h"
+
+// -----------------------------------------------------------------------------
+// A FileDescriptorSet is an ordered set of POSIX file descriptors. These are
+// associated with IPC messages so that descriptors can be transmitted over a
+// UNIX domain socket.
+// -----------------------------------------------------------------------------
+class FileDescriptorSet {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileDescriptorSet)
+ FileDescriptorSet();
+
+ // Mac and Linux both limit the number of file descriptors per message to
+ // slightly more than 250.
+ enum { MAX_DESCRIPTORS_PER_MESSAGE = 200 };
+
+ void CopyFrom(const FileDescriptorSet& other);
+
+ // ---------------------------------------------------------------------------
+ // Interfaces for building during message serialisation...
+
+ // Add a descriptor to the end of the set. Returns false iff the set is full.
+ bool Add(int fd);
+ // Add a descriptor to the end of the set and automatically close it after
+ // transmission. Returns false iff the set is full.
+ bool AddAndAutoClose(int fd);
+
+ // ---------------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------------
+ // Interfaces for accessing during message deserialisation...
+
+ // Return the number of descriptors
+ unsigned size() const { return descriptors_.size(); }
+ // Return true if no unconsumed descriptors remain
+ bool empty() const { return descriptors_.empty(); }
+ // Fetch the nth descriptor from the beginning of the set. Code using this
+ // /must/ access the descriptors in order, except that it may wrap from the
+ // end to index 0 again.
+ //
+ // This interface is designed for the deserialising code as it doesn't
+ // support close flags.
+ // returns: file descriptor, or -1 on error
+ int GetDescriptorAt(unsigned n) const;
+
+ // ---------------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------------
+ // Interfaces for transmission...
+
+ // Fill an array with file descriptors without 'consuming' them. CommitAll
+ // must be called after these descriptors have been transmitted.
+ // buffer: (output) a buffer of, at least, size() integers.
+ void GetDescriptors(int* buffer) const;
+ // This must be called after transmitting the descriptors returned by
+ // GetDescriptors. It marks all the descriptors as consumed and closes those
+ // which are auto-close.
+ void CommitAll();
+
+ // ---------------------------------------------------------------------------
+
+ // ---------------------------------------------------------------------------
+ // Interfaces for receiving...
+
+ // Set the contents of the set from the given buffer. This set must be empty
+ // before calling. The auto-close flag is set on all the descriptors so that
+ // unconsumed descriptors are closed on destruction.
+ void SetDescriptors(const int* buffer, unsigned count);
+
+ // ---------------------------------------------------------------------------
+
+ private:
+ ~FileDescriptorSet();
+
+ // A vector of descriptors and close flags. If this message is sent, then
+ // these descriptors are sent as control data. After sending, any descriptors
+ // with a true flag are closed. If this message has been received, then these
+ // are the descriptors which were received and all close flags are true.
+ std::vector<base::FileDescriptor> descriptors_;
+
+ // This contains the index of the next descriptor which should be consumed.
+ // It's used in a couple of ways. Firstly, at destruction we can check that
+ // all the descriptors have been read (with GetNthDescriptor). Secondly, we
+ // can check that they are read in order.
+ mutable unsigned consumed_descriptor_highwater_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorSet);
+};
+
+#endif // CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_channel.h b/ipc/chromium/src/chrome/common/ipc_channel.h
new file mode 100644
index 0000000000..92280bb0eb
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel.h
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_H_
+#define CHROME_COMMON_IPC_CHANNEL_H_
+
+#include <cstdint>
+#include <queue>
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "mozilla/UniquePtr.h"
+
+#ifdef OS_WIN
+# include <string>
+#endif
+
+namespace IPC {
+
+class Message;
+
+//------------------------------------------------------------------------------
+
+class Channel {
+ // Security tests need access to the pipe handle.
+ friend class ChannelTest;
+
+ public:
+ // Windows channels use named objects and connect to them by name,
+ // but on Unix we use unnamed socketpairs and pass capabilities
+ // directly using SCM_RIGHTS messages. This type abstracts away
+ // that difference.
+#ifdef OS_WIN
+ typedef std::wstring ChannelId;
+#else
+ struct ChannelId {};
+#endif
+
+ // Implemented by consumers of a Channel to receive messages.
+ class Listener {
+ public:
+ virtual ~Listener() {}
+
+ // Called when a message is received.
+ virtual void OnMessageReceived(Message&& message) = 0;
+
+ // Called when the channel is connected and we have received the internal
+ // Hello message from the peer.
+ virtual void OnChannelConnected(int32_t peer_pid) {}
+
+ // Called when an error is detected that causes the channel to close.
+ // This method is not called when a channel is closed normally.
+ virtual void OnChannelError() {}
+
+ // If the listener has queued messages, swap them for |queue| like so
+ // swap(impl->my_queued_messages, queue);
+ virtual void GetQueuedMessages(std::queue<Message>& queue) {}
+ };
+
+ enum Mode { MODE_SERVER, MODE_CLIENT };
+
+ enum {
+ // The maximum message size in bytes. Attempting to receive a
+ // message of this size or bigger results in a channel error.
+ kMaximumMessageSize = 256 * 1024 * 1024,
+
+ // Ammount of data to read at once from the pipe.
+ kReadBufferSize = 4 * 1024,
+
+ // Maximum size of a message that we allow to be copied (rather than moved).
+ kMaxCopySize = 32 * 1024,
+ };
+
+ // Initialize a Channel.
+ //
+ // |channel_id| identifies the communication Channel.
+ // |mode| specifies whether this Channel is to operate in server mode or
+ // client mode. In server mode, the Channel is responsible for setting up the
+ // IPC object, whereas in client mode, the Channel merely connects to the
+ // already established IPC object.
+ // |listener| receives a callback on the current thread for each newly
+ // received message.
+ //
+ Channel(const ChannelId& channel_id, Mode mode, Listener* listener);
+
+ // XXX it would nice not to have yet more platform-specific code in
+ // here but it's just not worth the trouble.
+#if defined(OS_POSIX)
+ // Connect to a pre-created channel |fd| as |mode|.
+ Channel(int fd, Mode mode, Listener* listener);
+#elif defined(OS_WIN)
+ // Connect to a pre-created channel as |mode|. Clients connect to
+ // the pre-existing server pipe, and servers take over |server_pipe|.
+ Channel(const ChannelId& channel_id, void* server_pipe, Mode mode,
+ Listener* listener);
+#endif
+
+ ~Channel();
+
+ // Connect the pipe. On the server side, this will initiate
+ // waiting for connections. On the client, it attempts to
+ // connect to a pre-existing pipe. Note, calling Connect()
+ // will not block the calling thread and may complete
+ // asynchronously.
+ bool Connect();
+
+ // Close this Channel explicitly. May be called multiple times.
+ void Close();
+
+ // Modify the Channel's listener.
+ Listener* set_listener(Listener* listener);
+
+ // Send a message over the Channel to the listener on the other end.
+ //
+ // |message| must be allocated using operator new. This object will be
+ // deleted once the contents of the Message have been sent.
+ //
+ // If you Send() a message on a Close()'d channel, we delete the message
+ // immediately.
+ bool Send(mozilla::UniquePtr<Message> message);
+
+ // Unsound_IsClosed() and Unsound_NumQueuedMessages() are safe to call from
+ // any thread, but the value returned may be out of date, because we don't
+ // use any synchronization when reading or writing it.
+ bool Unsound_IsClosed() const;
+ uint32_t Unsound_NumQueuedMessages() const;
+
+#if defined(OS_POSIX)
+ // On POSIX an IPC::Channel wraps a socketpair(), this method returns the
+ // FD # for the client end of the socket and the equivalent FD# to use for
+ // mapping it into the Child process.
+ // This method may only be called on the server side of a channel.
+ //
+ // If the kTestingChannelID flag is specified on the command line then
+ // a named FIFO is used as the channel transport mechanism rather than a
+ // socketpair() in which case this method returns -1 for both parameters.
+ void GetClientFileDescriptorMapping(int* src_fd, int* dest_fd) const;
+
+ // Return the file descriptor for communication with the peer.
+ int GetFileDescriptor() const;
+
+ // Reset the file descriptor for communication with the peer.
+ void ResetFileDescriptor(int fd);
+
+ // Close the client side of the socketpair.
+ void CloseClientFileDescriptor();
+
+#elif defined(OS_WIN)
+ // Return the server pipe handle.
+ void* GetServerPipeHandle() const;
+#endif // defined(OS_POSIX)
+
+ // On Windows: Generates a channel ID that, if passed to the client
+ // as a shared secret, will validate the client's authenticity.
+ // Other platforms don't use channel IDs, so this returns the dummy
+ // ChannelId value.
+ static ChannelId GenerateVerifiedChannelID();
+
+ // On Windows: Retrieves the initial channel ID passed to the
+ // current process by its parent. Other platforms don't do this;
+ // the dummy ChannelId value is returned instead.
+ static ChannelId ChannelIDForCurrentProcess();
+
+#if defined(MOZ_WIDGET_ANDROID)
+ // Used to set the first IPC file descriptor in the child process on Android.
+ // See ipc_channel_posix.cc for further details on how this is used.
+ static void SetClientChannelFd(int fd);
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ private:
+ // PIMPL to which all channel calls are delegated.
+ class ChannelImpl;
+ ChannelImpl* channel_impl_;
+
+ enum {
+#if defined(OS_MACOSX)
+ // If the channel receives a message that contains file descriptors, then
+ // it will reply back with this message, indicating that the message has
+ // been received. The sending channel can then close any descriptors that
+ // had been marked as auto_close. This works around a sendmsg() bug on BSD
+ // where the kernel can eagerly close file descriptors that are in message
+ // queues but not yet delivered.
+ RECEIVED_FDS_MESSAGE_TYPE = kuint16max - 1,
+#endif
+
+ // The Hello message is internal to the Channel class. It is sent
+ // by the peer when the channel is connected. The message contains
+ // just the process id (pid). The message has a special routing_id
+ // (MSG_ROUTING_NONE) and type (HELLO_MESSAGE_TYPE).
+ HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type
+ // (uint16_t), to avoid conflicting with
+ // normal message types, which are
+ // enumeration constants starting from 0.
+ };
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
new file mode 100644
index 0000000000..d79c898356
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -0,0 +1,959 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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/ipc_channel_posix.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#if defined(OS_MACOSX) || defined(OS_NETBSD)
+# include <sched.h>
+#endif
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+
+#include <string>
+#include <map>
+
+#include "base/command_line.h"
+#include "base/eintr_wrapper.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/file_descriptor_set_posix.h"
+#include "chrome/common/ipc_channel_utils.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+#ifdef FUZZING
+# include "mozilla/ipc/Faulty.h"
+#endif
+
+// Use OS specific iovec array limit where it's possible.
+#if defined(IOV_MAX)
+static const size_t kMaxIOVecSize = IOV_MAX;
+#elif defined(ANDROID)
+static const size_t kMaxIOVecSize = 256;
+#else
+static const size_t kMaxIOVecSize = 16;
+#endif
+
+#ifdef MOZ_TASK_TRACER
+# include "GeckoTaskTracerImpl.h"
+using namespace mozilla::tasktracer;
+#endif
+
+using namespace mozilla::ipc;
+
+namespace IPC {
+
+// IPC channels on Windows use named pipes (CreateNamedPipe()) with
+// channel ids as the pipe names. Channels on POSIX use anonymous
+// Unix domain sockets created via socketpair() as pipes. These don't
+// quite line up.
+//
+// When creating a child subprocess, the parent side of the fork
+// arranges it such that the initial control channel ends up on the
+// magic file descriptor gClientChannelFd in the child. Future
+// connections (file descriptors) can then be passed via that
+// connection via sendmsg().
+//
+// On Android, child processes are created as a service instead of
+// forking the parent process. The Android Binder service is used to
+// transport the IPC channel file descriptor to the child process.
+// So rather than re-mapping the file descriptor to a known value,
+// the received channel file descriptor is set by calling
+// SetClientChannelFd before gecko has been initialized and started
+// in the child process.
+
+//------------------------------------------------------------------------------
+namespace {
+
+// This is the file descriptor number that a client process expects to find its
+// IPC socket.
+static int gClientChannelFd =
+#if defined(MOZ_WIDGET_ANDROID)
+ // On android the fd is set at the time of child creation.
+ -1
+#else
+ 3
+#endif // defined(MOZ_WIDGET_ANDROID)
+ ;
+
+//------------------------------------------------------------------------------
+const size_t kMaxPipeNameLength = sizeof(((sockaddr_un*)0)->sun_path);
+
+bool SetCloseOnExec(int fd) {
+ int flags = fcntl(fd, F_GETFD);
+ if (flags == -1) return false;
+
+ flags |= FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, flags) == -1) return false;
+
+ return true;
+}
+
+bool ErrorIsBrokenPipe(int err) { return err == EPIPE || err == ECONNRESET; }
+
+// Some Android ARM64 devices appear to have a bug where sendmsg
+// sometimes returns 0xFFFFFFFF, which we're assuming is a -1 that was
+// incorrectly truncated to 32-bit and then zero-extended.
+// See bug 1660826 for details.
+//
+// This is a workaround to detect that value and replace it with -1
+// (and check that there really was an error), because the largest
+// amount we'll ever write is Channel::kMaximumMessageSize (256MiB).
+//
+// The workaround is also enabled on x86_64 Android on debug builds,
+// although the bug isn't known to manifest there, so that there will
+// be some CI coverage of this code.
+
+static inline ssize_t corrected_sendmsg(int socket,
+ const struct msghdr* message,
+ int flags) {
+#if defined(ANDROID) && \
+ (defined(__aarch64__) || (defined(DEBUG) && defined(__x86_64__)))
+ static constexpr auto kBadValue = static_cast<ssize_t>(0xFFFFFFFF);
+ static_assert(kBadValue > 0);
+
+# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ errno = 0;
+# endif
+ ssize_t bytes_written = sendmsg(socket, message, flags);
+ if (bytes_written == kBadValue) {
+ MOZ_DIAGNOSTIC_ASSERT(errno != 0);
+ bytes_written = -1;
+ }
+ MOZ_DIAGNOSTIC_ASSERT(bytes_written < kBadValue);
+ return bytes_written;
+#else
+ return sendmsg(socket, message, flags);
+#endif
+}
+
+} // namespace
+//------------------------------------------------------------------------------
+
+#if defined(MOZ_WIDGET_ANDROID)
+void Channel::SetClientChannelFd(int fd) { gClientChannelFd = fd; }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
+ Listener* listener)
+ : factory_(this) {
+ Init(mode, listener);
+
+ if (!CreatePipe(mode)) {
+ CHROMIUM_LOG(WARNING) << "Unable to create pipe in "
+ << (mode == MODE_SERVER ? "server" : "client")
+ << " mode error(" << strerror(errno) << ").";
+ closed_ = true;
+ return;
+ }
+
+ EnqueueHelloMessage();
+}
+
+Channel::ChannelImpl::ChannelImpl(int fd, Mode mode, Listener* listener)
+ : factory_(this) {
+ Init(mode, listener);
+ pipe_ = fd;
+ waiting_connect_ = (MODE_SERVER == mode);
+
+ EnqueueHelloMessage();
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+ // Verify that we fit in a "quantum-spaced" jemalloc bucket.
+ static_assert(sizeof(*this) <= 512, "Exceeded expected size class");
+
+ DCHECK(kControlBufferHeaderSize >= CMSG_SPACE(0));
+
+ mode_ = mode;
+ is_blocked_on_write_ = false;
+ partial_write_iter_.reset();
+ input_buf_offset_ = 0;
+ input_buf_ = mozilla::MakeUnique<char[]>(Channel::kReadBufferSize);
+ input_cmsg_buf_ = mozilla::MakeUnique<char[]>(kControlBufferSize);
+ server_listen_pipe_ = -1;
+ pipe_ = -1;
+ client_pipe_ = -1;
+ listener_ = listener;
+ waiting_connect_ = true;
+ processing_incoming_ = false;
+ closed_ = false;
+#if defined(OS_MACOSX)
+ last_pending_fd_id_ = 0;
+#endif
+ output_queue_length_ = 0;
+}
+
+bool Channel::ChannelImpl::CreatePipe(Mode mode) {
+ DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
+
+ if (mode == MODE_SERVER) {
+ // socketpair()
+ int pipe_fds[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) {
+ mozilla::ipc::AnnotateCrashReportWithErrno(
+ CrashReporter::Annotation::IpcCreatePipeSocketPairErrno, errno);
+ return false;
+ }
+ // Set both ends to be non-blocking.
+ if (fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == -1) {
+ mozilla::ipc::AnnotateCrashReportWithErrno(
+ CrashReporter::Annotation::IpcCreatePipeFcntlErrno, errno);
+ IGNORE_EINTR(close(pipe_fds[0]));
+ IGNORE_EINTR(close(pipe_fds[1]));
+ return false;
+ }
+
+ if (!SetCloseOnExec(pipe_fds[0]) || !SetCloseOnExec(pipe_fds[1])) {
+ mozilla::ipc::AnnotateCrashReportWithErrno(
+ CrashReporter::Annotation::IpcCreatePipeCloExecErrno, errno);
+ IGNORE_EINTR(close(pipe_fds[0]));
+ IGNORE_EINTR(close(pipe_fds[1]));
+ return false;
+ }
+
+ pipe_ = pipe_fds[0];
+ client_pipe_ = pipe_fds[1];
+ } else {
+ static mozilla::Atomic<bool> consumed(false);
+ CHECK(!consumed.exchange(true))
+ << "child process main channel can be created only once";
+ pipe_ = gClientChannelFd;
+ waiting_connect_ = false;
+ }
+
+ return true;
+}
+
+/**
+ * Reset the file descriptor for communication with the peer.
+ */
+void Channel::ChannelImpl::ResetFileDescriptor(int fd) {
+ NS_ASSERTION(fd > 0 && fd == pipe_, "Invalid file descriptor");
+
+ EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
+ mozilla::UniquePtr<Message> msg(
+ new Message(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE));
+ if (!msg->WriteInt(base::GetCurrentProcId())) {
+ Close();
+ return false;
+ }
+
+ OutputQueuePush(std::move(msg));
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+ if (pipe_ == -1) {
+ return false;
+ }
+
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_, true, MessageLoopForIO::WATCH_READ, &read_watcher_, this);
+ waiting_connect_ = false;
+
+ if (!waiting_connect_) return ProcessOutgoingMessages();
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessIncomingMessages() {
+ struct msghdr msg = {0};
+ struct iovec iov;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = input_cmsg_buf_.get();
+
+ for (;;) {
+ msg.msg_controllen = kControlBufferSize;
+
+ if (pipe_ == -1) return false;
+
+ // In some cases the beginning of a message will be stored in input_buf_. We
+ // don't want to overwrite that, so we store the new data after it.
+ iov.iov_base = input_buf_.get() + input_buf_offset_;
+ iov.iov_len = Channel::kReadBufferSize - input_buf_offset_;
+
+ // Read from pipe.
+ // recvmsg() returns 0 if the connection has closed or EAGAIN if no data
+ // is waiting on the pipe.
+ ssize_t bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
+
+ if (bytes_read < 0) {
+ if (errno == EAGAIN) {
+ return true;
+ } else {
+ if (!ErrorIsBrokenPipe(errno)) {
+ CHROMIUM_LOG(ERROR)
+ << "pipe error (fd " << pipe_ << "): " << strerror(errno);
+ }
+ return false;
+ }
+ } else if (bytes_read == 0) {
+ // The pipe has closed...
+ Close();
+ return false;
+ }
+ DCHECK(bytes_read);
+
+ // a pointer to an array of |num_wire_fds| file descriptors from the read
+ const int* wire_fds = NULL;
+ unsigned num_wire_fds = 0;
+
+ // walk the list of control messages and, if we find an array of file
+ // descriptors, save a pointer to the array
+
+ // This next if statement is to work around an OSX issue where
+ // CMSG_FIRSTHDR will return non-NULL in the case that controllen == 0.
+ // Here's a test case:
+ //
+ // int main() {
+ // struct msghdr msg;
+ // msg.msg_control = &msg;
+ // msg.msg_controllen = 0;
+ // if (CMSG_FIRSTHDR(&msg))
+ // printf("Bug found!\n");
+ // }
+ if (msg.msg_controllen > 0) {
+ // On OSX, CMSG_FIRSTHDR doesn't handle the case where controllen is 0
+ // and will return a pointer into nowhere.
+ for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ DCHECK(payload_len % sizeof(int) == 0);
+ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ num_wire_fds = payload_len / 4;
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ CHROMIUM_LOG(ERROR)
+ << "SCM_RIGHTS message was truncated"
+ << " cmsg_len:" << cmsg->cmsg_len << " fd:" << pipe_;
+ for (unsigned i = 0; i < num_wire_fds; ++i)
+ IGNORE_EINTR(close(wire_fds[i]));
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ // Process messages from input buffer.
+ const char* p = input_buf_.get();
+ const char* end = input_buf_.get() + input_buf_offset_ + bytes_read;
+
+ // A pointer to an array of |num_fds| file descriptors which includes any
+ // fds that have spilled over from a previous read.
+ const int* fds;
+ unsigned num_fds;
+ unsigned fds_i = 0; // the index of the first unused descriptor
+
+ if (input_overflow_fds_.empty()) {
+ fds = wire_fds;
+ num_fds = num_wire_fds;
+ } else {
+ // This code may look like a no-op in the case where
+ // num_wire_fds == 0, but in fact:
+ //
+ // 1. wire_fds will be nullptr, so passing it to memcpy is
+ // undefined behavior according to the C standard, even though
+ // the memcpy length is 0.
+ //
+ // 2. prev_size will be an out-of-bounds index for
+ // input_overflow_fds_; this is undefined behavior according to
+ // the C++ standard, even though the element only has its
+ // pointer taken and isn't accessed (and the corresponding
+ // operation on a C array would be defined).
+ //
+ // UBSan makes #1 a fatal error, and assertions in libstdc++ do
+ // the same for #2 if enabled.
+ if (num_wire_fds > 0) {
+ const size_t prev_size = input_overflow_fds_.size();
+ input_overflow_fds_.resize(prev_size + num_wire_fds);
+ memcpy(&input_overflow_fds_[prev_size], wire_fds,
+ num_wire_fds * sizeof(int));
+ }
+ fds = &input_overflow_fds_[0];
+ num_fds = input_overflow_fds_.size();
+ }
+
+ // The data for the message we're currently reading consists of any data
+ // stored in incoming_message_ followed by data in input_buf_ (followed by
+ // other messages).
+
+ while (p < end) {
+ // Try to figure out how big the message is. Size is 0 if we haven't read
+ // enough of the header to know the size.
+ uint32_t message_length = 0;
+ if (incoming_message_.isSome()) {
+ message_length = incoming_message_.ref().size();
+ } else {
+ message_length = Message::MessageSize(p, end);
+ }
+
+ if (!message_length) {
+ // We haven't seen the full message header.
+ MOZ_ASSERT(incoming_message_.isNothing());
+
+ // Move everything we have to the start of the buffer. We'll finish
+ // reading this message when we get more data. For now we leave it in
+ // input_buf_.
+ memmove(input_buf_.get(), p, end - p);
+ input_buf_offset_ = end - p;
+
+ break;
+ }
+
+ input_buf_offset_ = 0;
+
+ bool partial;
+ if (incoming_message_.isSome()) {
+ // We already have some data for this message stored in
+ // incoming_message_. We want to append the new data there.
+ Message& m = incoming_message_.ref();
+
+ // How much data from this message remains to be added to
+ // incoming_message_?
+ MOZ_DIAGNOSTIC_ASSERT(message_length > m.CurrentSize());
+ uint32_t remaining = message_length - m.CurrentSize();
+
+ // How much data from this message is stored in input_buf_?
+ uint32_t in_buf = std::min(remaining, uint32_t(end - p));
+
+ m.InputBytes(p, in_buf);
+ p += in_buf;
+
+ // Are we done reading this message?
+ partial = in_buf != remaining;
+ } else {
+ // How much data from this message is stored in input_buf_?
+ uint32_t in_buf = std::min(message_length, uint32_t(end - p));
+
+ incoming_message_.emplace(p, in_buf);
+ p += in_buf;
+
+ // Are we done reading this message?
+ partial = in_buf != message_length;
+ }
+
+ if (partial) {
+ break;
+ }
+
+ Message& m = incoming_message_.ref();
+
+ if (m.header()->num_fds) {
+ // the message has file descriptors
+ const char* error = NULL;
+ if (m.header()->num_fds > num_fds - fds_i) {
+ // the message has been completely received, but we didn't get
+ // enough file descriptors.
+ error = "Message needs unreceived descriptors";
+ }
+
+ if (m.header()->num_fds >
+ FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
+ // There are too many descriptors in this message
+ error = "Message requires an excessive number of descriptors";
+ }
+
+ if (error) {
+ CHROMIUM_LOG(WARNING)
+ << error << " channel:" << this << " message-type:" << m.type()
+ << " header()->num_fds:" << m.header()->num_fds
+ << " num_fds:" << num_fds << " fds_i:" << fds_i;
+ // close the existing file descriptors so that we don't leak them
+ for (unsigned i = fds_i; i < num_fds; ++i)
+ IGNORE_EINTR(close(fds[i]));
+ input_overflow_fds_.clear();
+ // abort the connection
+ return false;
+ }
+
+#if defined(OS_MACOSX)
+ // Send a message to the other side, indicating that we are now
+ // responsible for closing the descriptor.
+ auto fdAck = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE,
+ RECEIVED_FDS_MESSAGE_TYPE);
+ DCHECK(m.fd_cookie() != 0);
+ fdAck->set_fd_cookie(m.fd_cookie());
+ OutputQueuePush(std::move(fdAck));
+#endif
+
+ m.file_descriptor_set()->SetDescriptors(&fds[fds_i],
+ m.header()->num_fds);
+ fds_i += m.header()->num_fds;
+ }
+
+ // Note: We set other_pid_ below when we receive a Hello message (which
+ // has no routing ID), but we only emit a profiler marker for messages
+ // with a routing ID, so there's no conflict here.
+ AddIPCProfilerMarker(m, other_pid_, MessageDirection::eReceiving,
+ MessagePhase::TransferEnd);
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "received message on channel @" << this << " with type "
+ << m.type();
+#endif
+
+ if (m.routing_id() == MSG_ROUTING_NONE &&
+ m.type() == HELLO_MESSAGE_TYPE) {
+ // The Hello message contains only the process id.
+ other_pid_ = MessageIterator(m).NextInt();
+ listener_->OnChannelConnected(other_pid_);
+#if defined(OS_MACOSX)
+ } else if (m.routing_id() == MSG_ROUTING_NONE &&
+ m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
+ DCHECK(m.fd_cookie() != 0);
+ CloseDescriptors(m.fd_cookie());
+#endif
+ } else {
+ mozilla::LogIPCMessage::Run run(&m);
+ listener_->OnMessageReceived(std::move(m));
+ }
+
+ incoming_message_.reset();
+ }
+
+ input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
+
+ // When the input data buffer is empty, the overflow fds should be too. If
+ // this is not the case, we probably have a rogue renderer which is trying
+ // to fill our descriptor table.
+ if (incoming_message_.isNothing() && input_buf_offset_ == 0 &&
+ !input_overflow_fds_.empty()) {
+ // We close these descriptors in Close()
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessOutgoingMessages() {
+ DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
+ // no connection?
+ is_blocked_on_write_ = false;
+
+ if (output_queue_.IsEmpty()) return true;
+
+ if (pipe_ == -1) return false;
+
+ // Write out all the messages we can till the write blocks or there are no
+ // more outgoing messages.
+ while (!output_queue_.IsEmpty()) {
+#ifdef FUZZING
+ mozilla::ipc::Faulty::instance().MaybeCollectAndClosePipe(pipe_);
+#endif
+ Message* msg = output_queue_.FirstElement().get();
+
+ struct msghdr msgh = {0};
+
+ static const int tmp =
+ CMSG_SPACE(sizeof(int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]));
+ char buf[tmp];
+
+ if (partial_write_iter_.isNothing()) {
+ Pickle::BufferList::IterImpl iter(msg->Buffers());
+ MOZ_DIAGNOSTIC_ASSERT(!iter.Done(), "empty message");
+ partial_write_iter_.emplace(iter);
+ }
+
+ if (partial_write_iter_.ref().Done()) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "partial_write_iter_ should not be null");
+ // report a send error to our caller, which will close the channel.
+ return false;
+ }
+
+ if (partial_write_iter_.value().Data() == msg->Buffers().Start()) {
+ AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
+ MessagePhase::TransferStart);
+
+ if (!msg->file_descriptor_set()->empty()) {
+ // This is the first chunk of a message which has descriptors to send
+ struct cmsghdr* cmsg;
+ const unsigned num_fds = msg->file_descriptor_set()->size();
+
+ if (num_fds > FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Too many file descriptors!");
+ CHROMIUM_LOG(FATAL) << "Too many file descriptors!";
+ // This should not be reached.
+ return false;
+ }
+
+ msgh.msg_control = buf;
+ msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds);
+ cmsg = CMSG_FIRSTHDR(&msgh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
+ msg->file_descriptor_set()->GetDescriptors(
+ reinterpret_cast<int*>(CMSG_DATA(cmsg)));
+ msgh.msg_controllen = cmsg->cmsg_len;
+
+ msg->header()->num_fds = num_fds;
+#if defined(OS_MACOSX)
+ msg->set_fd_cookie(++last_pending_fd_id_);
+#endif
+ }
+ }
+
+ struct iovec iov[kMaxIOVecSize];
+ size_t iov_count = 0;
+ size_t amt_to_write = 0;
+
+ // How much of this message have we written so far?
+ Pickle::BufferList::IterImpl iter = partial_write_iter_.value();
+
+ // Store the unwritten part of the first segment to write into the iovec.
+ iov[0].iov_base = const_cast<char*>(iter.Data());
+ iov[0].iov_len = iter.RemainingInSegment();
+ amt_to_write += iov[0].iov_len;
+ iter.Advance(msg->Buffers(), iov[0].iov_len);
+ iov_count++;
+
+ // Store remaining segments to write into iovec.
+ while (!iter.Done()) {
+ char* data = iter.Data();
+ size_t size = iter.RemainingInSegment();
+
+ // Don't add more than kMaxIOVecSize to the iovec so that we avoid
+ // OS-dependent limits.
+ if (iov_count < kMaxIOVecSize) {
+ iov[iov_count].iov_base = data;
+ iov[iov_count].iov_len = size;
+ iov_count++;
+ }
+ amt_to_write += size;
+ iter.Advance(msg->Buffers(), size);
+ }
+
+ msgh.msg_iov = iov;
+ msgh.msg_iovlen = iov_count;
+
+ ssize_t bytes_written =
+ HANDLE_EINTR(corrected_sendmsg(pipe_, &msgh, MSG_DONTWAIT));
+
+#if !defined(OS_MACOSX)
+ // On OSX CommitAll gets called later, once we get the
+ // RECEIVED_FDS_MESSAGE_TYPE message.
+ if (bytes_written > 0) msg->file_descriptor_set()->CommitAll();
+#endif
+
+ if (bytes_written < 0) {
+ switch (errno) {
+ case EAGAIN:
+ // Not an error; the sendmsg would have blocked, so return to the
+ // event loop and try again later.
+ break;
+#if defined(OS_MACOSX) || defined(OS_NETBSD)
+ // (Note: this comment is copied from https://crrev.com/86c3d9ef4fdf6;
+ // see also bug 1142693 comment #73.)
+ //
+ // On OS X if sendmsg() is trying to send fds between processes and
+ // there isn't enough room in the output buffer to send the fd
+ // structure over atomically then EMSGSIZE is returned. The same
+ // applies to NetBSD as well.
+ //
+ // EMSGSIZE presents a problem since the system APIs can only call us
+ // when there's room in the socket buffer and not when there is
+ // "enough" room.
+ //
+ // The current behavior is to return to the event loop when EMSGSIZE
+ // is received and hopefull service another FD. This is however still
+ // technically a busy wait since the event loop will call us right
+ // back until the receiver has read enough data to allow passing the
+ // FD over atomically.
+ case EMSGSIZE:
+ // Because this is likely to result in a busy-wait, we'll try to make
+ // it easier for the receiver to make progress.
+ sched_yield();
+ break;
+#endif
+ default:
+ if (!ErrorIsBrokenPipe(errno)) {
+ CHROMIUM_LOG(ERROR) << "pipe error: " << strerror(errno);
+ }
+ return false;
+ }
+ }
+
+ if (static_cast<size_t>(bytes_written) != amt_to_write) {
+ // If write() fails with EAGAIN then bytes_written will be -1.
+ if (bytes_written > 0) {
+ MOZ_DIAGNOSTIC_ASSERT(static_cast<size_t>(bytes_written) <
+ amt_to_write);
+ partial_write_iter_.ref().AdvanceAcrossSegments(msg->Buffers(),
+ bytes_written);
+ // We should not hit the end of the buffer.
+ MOZ_DIAGNOSTIC_ASSERT(!partial_write_iter_.ref().Done());
+ }
+
+ // Tell libevent to call us back once things are unblocked.
+ is_blocked_on_write_ = true;
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_,
+ false, // One shot
+ MessageLoopForIO::WATCH_WRITE, &write_watcher_, this);
+ return true;
+ } else {
+ partial_write_iter_.reset();
+
+#if defined(OS_MACOSX)
+ if (!msg->file_descriptor_set()->empty())
+ pending_fds_.push_back(
+ PendingDescriptors(msg->fd_cookie(), msg->file_descriptor_set()));
+#endif
+
+ // Message sent OK!
+
+ AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
+ MessagePhase::TransferEnd);
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent message @" << msg << " on channel @" << this
+ << " with type " << msg->type();
+#endif
+ OutputQueuePop();
+ // msg has been destroyed, so clear the dangling reference.
+ msg = nullptr;
+ }
+ }
+ return true;
+}
+
+bool Channel::ChannelImpl::Send(mozilla::UniquePtr<Message> message) {
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
+ << " with type " << message->type() << " ("
+ << output_queue_.Count() << " in queue)";
+#endif
+
+#ifdef FUZZING
+ message = mozilla::ipc::Faulty::instance().MutateIPCMessage(
+ "Channel::ChannelImpl::Send", std::move(message));
+#endif
+
+ // If the channel has been closed, ProcessOutgoingMessages() is never going
+ // to pop anything off output_queue; output_queue will only get emptied when
+ // the channel is destructed. We might as well delete message now, instead
+ // of waiting for the channel to be destructed.
+ if (closed_) {
+ if (mozilla::ipc::LoggingEnabled()) {
+ fprintf(stderr,
+ "Can't send message %s, because this channel is closed.\n",
+ message->name());
+ }
+ return false;
+ }
+
+ OutputQueuePush(std::move(message));
+ if (!waiting_connect_) {
+ if (!is_blocked_on_write_) {
+ if (!ProcessOutgoingMessages()) return false;
+ }
+ }
+
+ return true;
+}
+
+void Channel::ChannelImpl::GetClientFileDescriptorMapping(int* src_fd,
+ int* dest_fd) const {
+ DCHECK(mode_ == MODE_SERVER);
+ *src_fd = client_pipe_;
+ *dest_fd = gClientChannelFd;
+}
+
+void Channel::ChannelImpl::CloseClientFileDescriptor() {
+ if (client_pipe_ != -1) {
+ IGNORE_EINTR(close(client_pipe_));
+ client_pipe_ = -1;
+ }
+}
+
+// Called by libevent when we can read from th pipe without blocking.
+void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
+ if (!waiting_connect_ && fd == pipe_) {
+ if (!ProcessIncomingMessages()) {
+ Close();
+ listener_->OnChannelError();
+ // The OnChannelError() call may delete this, so we need to exit now.
+ return;
+ }
+ }
+}
+
+#if defined(OS_MACOSX)
+void Channel::ChannelImpl::CloseDescriptors(uint32_t pending_fd_id) {
+ DCHECK(pending_fd_id != 0);
+ for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin();
+ i != pending_fds_.end(); i++) {
+ if ((*i).id == pending_fd_id) {
+ (*i).fds->CommitAll();
+ pending_fds_.erase(i);
+ return;
+ }
+ }
+ DCHECK(false) << "pending_fd_id not in our list!";
+}
+#endif
+
+void Channel::ChannelImpl::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
+ mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);
+
+ MOZ_DIAGNOSTIC_ASSERT(!closed_);
+ msg->AssertAsLargeAsHeader();
+ output_queue_.Push(std::move(msg));
+ output_queue_length_++;
+}
+
+void Channel::ChannelImpl::OutputQueuePop() {
+ // Clear any reference to the front of output_queue_ before we destroy it.
+ partial_write_iter_.reset();
+
+ mozilla::UniquePtr<Message> message = output_queue_.Pop();
+ output_queue_length_--;
+}
+
+// Called by libevent when we can write to the pipe without blocking.
+void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) {
+ if (!ProcessOutgoingMessages()) {
+ Close();
+ listener_->OnChannelError();
+ }
+}
+
+void Channel::ChannelImpl::Close() {
+ // Close can be called multiple times, so we need to make sure we're
+ // idempotent.
+
+ // Unregister libevent for the listening socket and close it.
+ server_listen_connection_watcher_.StopWatchingFileDescriptor();
+
+ if (server_listen_pipe_ != -1) {
+ IGNORE_EINTR(close(server_listen_pipe_));
+ server_listen_pipe_ = -1;
+ }
+
+ // Unregister libevent for the FIFO and close it.
+ read_watcher_.StopWatchingFileDescriptor();
+ write_watcher_.StopWatchingFileDescriptor();
+ if (pipe_ != -1) {
+ IGNORE_EINTR(close(pipe_));
+ pipe_ = -1;
+ }
+ if (client_pipe_ != -1) {
+ IGNORE_EINTR(close(client_pipe_));
+ client_pipe_ = -1;
+ }
+
+ while (!output_queue_.IsEmpty()) {
+ OutputQueuePop();
+ }
+
+ // Close any outstanding, received file descriptors
+ for (std::vector<int>::iterator i = input_overflow_fds_.begin();
+ i != input_overflow_fds_.end(); ++i) {
+ IGNORE_EINTR(close(*i));
+ }
+ input_overflow_fds_.clear();
+
+#if defined(OS_MACOSX)
+ for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin();
+ i != pending_fds_.end(); i++) {
+ (*i).fds->CommitAll();
+ }
+ pending_fds_.clear();
+#endif
+
+ closed_ = true;
+}
+
+bool Channel::ChannelImpl::Unsound_IsClosed() const { return closed_; }
+
+uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const {
+ return output_queue_length_;
+}
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const ChannelId& channel_id, Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
+ MOZ_COUNT_CTOR(IPC::Channel);
+}
+
+Channel::Channel(int fd, Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(fd, mode, listener)) {
+ MOZ_COUNT_CTOR(IPC::Channel);
+}
+
+Channel::~Channel() {
+ MOZ_COUNT_DTOR(IPC::Channel);
+ delete channel_impl_;
+}
+
+bool Channel::Connect() { return channel_impl_->Connect(); }
+
+void Channel::Close() { channel_impl_->Close(); }
+
+Channel::Listener* Channel::set_listener(Listener* listener) {
+ return channel_impl_->set_listener(listener);
+}
+
+bool Channel::Send(mozilla::UniquePtr<Message> message) {
+ return channel_impl_->Send(std::move(message));
+}
+
+void Channel::GetClientFileDescriptorMapping(int* src_fd, int* dest_fd) const {
+ return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
+}
+
+void Channel::ResetFileDescriptor(int fd) {
+ channel_impl_->ResetFileDescriptor(fd);
+}
+
+int Channel::GetFileDescriptor() const {
+ return channel_impl_->GetFileDescriptor();
+}
+
+void Channel::CloseClientFileDescriptor() {
+ channel_impl_->CloseClientFileDescriptor();
+}
+
+bool Channel::Unsound_IsClosed() const {
+ return channel_impl_->Unsound_IsClosed();
+}
+
+uint32_t Channel::Unsound_NumQueuedMessages() const {
+ return channel_impl_->Unsound_NumQueuedMessages();
+}
+
+// static
+Channel::ChannelId Channel::GenerateVerifiedChannelID() { return {}; }
+
+// static
+Channel::ChannelId Channel::ChannelIDForCurrentProcess() { return {}; }
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.h b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
new file mode 100644
index 0000000000..c48121f776
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_POSIX_H_
+#define CHROME_COMMON_IPC_CHANNEL_POSIX_H_
+
+#include "chrome/common/ipc_channel.h"
+
+#include <sys/socket.h> // for CMSG macros
+
+#include <string>
+#include <vector>
+#include <list>
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "chrome/common/file_descriptor_set_posix.h"
+
+#include "mozilla/Maybe.h"
+#include "mozilla/Queue.h"
+#include "mozilla/UniquePtr.h"
+
+namespace IPC {
+
+// An implementation of ChannelImpl for POSIX systems that works via
+// socketpairs. See the .cc file for an overview of the implementation.
+class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
+ public:
+ using ChannelId = Channel::ChannelId;
+
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const ChannelId& channel_id, Mode mode, Listener* listener);
+ ChannelImpl(int fd, Mode mode, Listener* listener);
+ ~ChannelImpl() { Close(); }
+ bool Connect();
+ void Close();
+ Listener* set_listener(Listener* listener) {
+ Listener* old = listener_;
+ listener_ = listener;
+ return old;
+ }
+ bool Send(mozilla::UniquePtr<Message> message);
+ void GetClientFileDescriptorMapping(int* src_fd, int* dest_fd) const;
+
+ void ResetFileDescriptor(int fd);
+
+ int GetFileDescriptor() const { return pipe_; }
+ void CloseClientFileDescriptor();
+
+ // See the comment in ipc_channel.h for info on Unsound_IsClosed() and
+ // Unsound_NumQueuedMessages().
+ bool Unsound_IsClosed() const;
+ uint32_t Unsound_NumQueuedMessages() const;
+
+ private:
+ void Init(Mode mode, Listener* listener);
+ bool CreatePipe(Mode mode);
+ bool EnqueueHelloMessage();
+
+ bool ProcessIncomingMessages();
+ bool ProcessOutgoingMessages();
+
+ // MessageLoopForIO::Watcher implementation.
+ virtual void OnFileCanReadWithoutBlocking(int fd) override;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) override;
+
+#if defined(OS_MACOSX)
+ void CloseDescriptors(uint32_t pending_fd_id);
+#endif
+
+ void OutputQueuePush(mozilla::UniquePtr<Message> msg);
+ void OutputQueuePop();
+
+ Mode mode_;
+
+ // After accepting one client connection on our server socket we want to
+ // stop listening.
+ MessageLoopForIO::FileDescriptorWatcher server_listen_connection_watcher_;
+ MessageLoopForIO::FileDescriptorWatcher read_watcher_;
+ MessageLoopForIO::FileDescriptorWatcher write_watcher_;
+
+ // Indicates whether we're currently blocked waiting for a write to complete.
+ bool is_blocked_on_write_;
+
+ // If sending a message blocks then we use this iterator to keep track of
+ // where in the message we are. It gets reset when the message is finished
+ // sending.
+ mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_;
+
+ int server_listen_pipe_;
+ int pipe_;
+ int client_pipe_; // The client end of our socketpair().
+
+ Listener* listener_;
+
+ // Messages to be sent are queued here.
+ mozilla::Queue<mozilla::UniquePtr<Message>, 64> output_queue_;
+
+ // We read from the pipe into these buffers.
+ size_t input_buf_offset_;
+ mozilla::UniquePtr<char[]> input_buf_;
+ mozilla::UniquePtr<char[]> input_cmsg_buf_;
+
+ // The control message buffer will hold all of the file descriptors that will
+ // be read in during a single recvmsg call. Message::WriteFileDescriptor
+ // always writes one word of data for every file descriptor added to the
+ // message, and the number of file descriptors per message will not exceed
+ // MAX_DESCRIPTORS_PER_MESSAGE.
+ //
+ // This buffer also holds a control message header of size CMSG_SPACE(0)
+ // bytes. However, CMSG_SPACE is not a constant on Macs, so we can't use it
+ // here. Consequently, we pick a number here that is at least CMSG_SPACE(0) on
+ // all platforms. We assert at runtime, in Channel::ChannelImpl::Init, that
+ // it's big enough.
+ static constexpr size_t kControlBufferHeaderSize = 32;
+ static constexpr size_t kControlBufferSize =
+ FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE * sizeof(int) +
+ kControlBufferHeaderSize;
+
+ // Large incoming messages that span multiple pipe buffers get built-up in the
+ // buffers of this message.
+ mozilla::Maybe<Message> incoming_message_;
+ std::vector<int> input_overflow_fds_;
+
+ // In server-mode, we have to wait for the client to connect before we
+ // can begin reading. We make use of the input_state_ when performing
+ // the connect operation in overlapped mode.
+ bool waiting_connect_;
+
+ // This flag is set when processing incoming messages. It is used to
+ // avoid recursing through ProcessIncomingMessages, which could cause
+ // problems. TODO(darin): make this unnecessary
+ bool processing_incoming_;
+
+ // This flag is set after we've closed the channel.
+ bool closed_;
+
+ // We keep track of the PID of the other side of this channel so that we can
+ // record this when generating logs of IPC messages.
+ int32_t other_pid_ = -1;
+
+#if defined(OS_MACOSX)
+ struct PendingDescriptors {
+ uint32_t id;
+ RefPtr<FileDescriptorSet> fds;
+
+ PendingDescriptors() : id(0) {}
+ PendingDescriptors(uint32_t id, FileDescriptorSet* fds)
+ : id(id), fds(fds) {}
+ };
+
+ std::list<PendingDescriptors> pending_fds_;
+
+ // A generation ID for RECEIVED_FD messages.
+ uint32_t last_pending_fd_id_;
+#endif
+
+ // This variable is updated so it matches output_queue_.Count(), except we can
+ // read output_queue_length_ from any thread (if we're OK getting an
+ // occasional out-of-date or bogus value). We use output_queue_length_ to
+ // implement Unsound_NumQueuedMessages.
+ size_t output_queue_length_;
+
+ ScopedRunnableMethodFactory<ChannelImpl> factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_POSIX_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_utils.cc b/ipc/chromium/src/chrome/common/ipc_channel_utils.cc
new file mode 100644
index 0000000000..150e54693e
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_utils.cc
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "chrome/common/ipc_channel_utils.h"
+
+#include "GeckoProfiler.h"
+#include "chrome/common/ipc_message.h"
+
+namespace IPC {
+
+void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid,
+ mozilla::ipc::MessageDirection aDirection,
+ mozilla::ipc::MessagePhase aPhase) {
+#ifdef MOZ_GECKO_PROFILER
+ if (aMessage.routing_id() != MSG_ROUTING_NONE &&
+ profiler_feature_active(ProfilerFeature::IPCMessages)) {
+ if (aOtherPid == -1) {
+ DLOG(WARNING) << "Unable to record IPC profile marker, other PID not set";
+ return;
+ }
+
+ if (profiler_is_locked_on_current_thread()) {
+ // One of the profiler mutexes is locked on this thread, don't record
+ // markers, because we don't want to expose profiler IPCs due to the
+ // profiler itself, and also to avoid possible re-entrancy issues.
+ return;
+ }
+
+ // The current timestamp must be given to the `IPCMarker` payload.
+ const mozilla::TimeStamp now = mozilla::TimeStamp::NowUnfuzzed();
+ PROFILER_MARKER("IPC", IPC, mozilla::MarkerTiming::InstantAt(now),
+ IPCMarker, now, now, aOtherPid, aMessage.seqno(),
+ aMessage.type(), mozilla::ipc::UnknownSide, aDirection,
+ aPhase, aMessage.is_sync());
+ }
+#endif
+}
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_utils.h b/ipc/chromium/src/chrome/common/ipc_channel_utils.h
new file mode 100644
index 0000000000..68787e0c24
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_utils.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_UTILS_H_
+#define CHROME_COMMON_IPC_CHANNEL_UTILS_H_
+
+#include "chrome/common/ipc_message.h"
+#include "MessageChannel.h"
+
+namespace IPC {
+
+void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid,
+ mozilla::ipc::MessageDirection aDirection,
+ mozilla::ipc::MessagePhase aPhase);
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_UTILS_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.cc b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
new file mode 100644
index 0000000000..8dd0475735
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -0,0 +1,664 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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/ipc_channel_win.h"
+
+#include <windows.h>
+#include <sstream>
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/rand_util.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/ipc_channel_utils.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/LateWriteChecks.h"
+#include "nsThreadUtils.h"
+
+#ifdef FUZZING
+# include "mozilla/ipc/Faulty.h"
+#endif
+
+using namespace mozilla::ipc;
+
+// ChannelImpl is used on the IPC thread, but constructed on a different thread,
+// so it has to hold the nsAutoOwningThread as a pointer, and we need a slightly
+// different macro.
+#ifdef DEBUG
+# define ASSERT_OWNINGTHREAD(_class) \
+ if (nsAutoOwningThread* owningThread = _mOwningThread.get()) { \
+ owningThread->AssertOwnership(#_class " not thread-safe"); \
+ }
+#else
+# define ASSERT_OWNINGTHREAD(_class) ((void)0)
+#endif
+
+namespace IPC {
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
+ memset(&context.overlapped, 0, sizeof(context.overlapped));
+ context.handler = channel;
+}
+
+Channel::ChannelImpl::State::~State() {
+ COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
+ starts_with_io_context);
+}
+
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
+ Listener* listener)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
+ shared_secret_(0),
+ waiting_for_shared_secret_(false) {
+ Init(mode, listener);
+
+ if (!CreatePipe(channel_id, mode)) {
+ // The pipe may have been closed already.
+ CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id
+ << "\" in " << (mode == 0 ? "server" : "client")
+ << " mode.";
+ }
+}
+
+Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id,
+ HANDLE server_pipe, Mode mode,
+ Listener* listener)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
+ shared_secret_(0),
+ waiting_for_shared_secret_(false) {
+ Init(mode, listener);
+
+ if (mode == MODE_SERVER) {
+ // We don't need the pipe name because we've been passed a handle, but we do
+ // need to get the shared secret from the channel_id.
+ PipeName(channel_id, &shared_secret_);
+ waiting_for_shared_secret_ = !!shared_secret_;
+
+ // Use the existing handle that was dup'd to us
+ pipe_ = server_pipe;
+ EnqueueHelloMessage();
+ } else {
+ // Take the normal init path to connect to the server pipe
+ CreatePipe(channel_id, mode);
+ }
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+ // Verify that we fit in a "quantum-spaced" jemalloc bucket.
+ static_assert(sizeof(*this) <= 512, "Exceeded expected size class");
+
+ pipe_ = INVALID_HANDLE_VALUE;
+ listener_ = listener;
+ waiting_connect_ = (mode == MODE_SERVER);
+ processing_incoming_ = false;
+ closed_ = false;
+ output_queue_length_ = 0;
+ input_buf_offset_ = 0;
+ input_buf_ = mozilla::MakeUnique<char[]>(Channel::kReadBufferSize);
+}
+
+void Channel::ChannelImpl::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
+ mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);
+
+ output_queue_.Push(std::move(msg));
+ output_queue_length_++;
+}
+
+void Channel::ChannelImpl::OutputQueuePop() {
+ mozilla::UniquePtr<Message> message = output_queue_.Pop();
+ output_queue_length_--;
+}
+
+HANDLE Channel::ChannelImpl::GetServerPipeHandle() const { return pipe_; }
+
+void Channel::ChannelImpl::Close() {
+ ASSERT_OWNINGTHREAD(ChannelImpl);
+
+ bool waited = false;
+ if (input_state_.is_pending || output_state_.is_pending) {
+ CancelIo(pipe_);
+ waited = true;
+ }
+
+ // Closing the handle at this point prevents us from issuing more requests
+ // form OnIOCompleted().
+ if (pipe_ != INVALID_HANDLE_VALUE) {
+ CloseHandle(pipe_);
+ pipe_ = INVALID_HANDLE_VALUE;
+ }
+
+ while (input_state_.is_pending || output_state_.is_pending) {
+ MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
+ }
+
+ while (!output_queue_.IsEmpty()) {
+ OutputQueuePop();
+ }
+
+#ifdef DEBUG
+ _mOwningThread = nullptr;
+#endif
+ closed_ = true;
+}
+
+bool Channel::ChannelImpl::Send(mozilla::UniquePtr<Message> message) {
+ ASSERT_OWNINGTHREAD(ChannelImpl);
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
+ << " with type " << message->type() << " ("
+ << output_queue_.Count() << " in queue)";
+#endif
+
+#ifdef FUZZING
+ message = mozilla::ipc::Faulty::instance().MutateIPCMessage(
+ "Channel::ChannelImpl::Send", std::move(message));
+#endif
+
+ if (closed_) {
+ if (mozilla::ipc::LoggingEnabled()) {
+ fprintf(stderr,
+ "Can't send message %s, because this channel is closed.\n",
+ message->name());
+ }
+ return false;
+ }
+
+ OutputQueuePush(std::move(message));
+ // ensure waiting to write
+ if (!waiting_connect_) {
+ if (!output_state_.is_pending) {
+ if (!ProcessOutgoingMessages(NULL, 0)) return false;
+ }
+ }
+
+ return true;
+}
+
+const Channel::ChannelId Channel::ChannelImpl::PipeName(
+ const ChannelId& channel_id, int32_t* secret) const {
+ MOZ_ASSERT(secret);
+
+ std::wostringstream ss;
+ ss << L"\\\\.\\pipe\\chrome.";
+
+ // Prevent the shared secret from ending up in the pipe name.
+ size_t index = channel_id.find_first_of(L'\\');
+ if (index != std::string::npos) {
+ StringToInt(channel_id.substr(index + 1), secret);
+ ss << channel_id.substr(0, index - 1);
+ } else {
+ // This case is here to support predictable named pipes in tests.
+ *secret = 0;
+ ss << channel_id;
+ }
+ return ss.str();
+}
+
+bool Channel::ChannelImpl::CreatePipe(const ChannelId& channel_id, Mode mode) {
+ DCHECK(pipe_ == INVALID_HANDLE_VALUE);
+ const ChannelId pipe_name = PipeName(channel_id, &shared_secret_);
+ if (mode == MODE_SERVER) {
+ waiting_for_shared_secret_ = !!shared_secret_;
+ pipe_ = CreateNamedPipeW(pipe_name.c_str(),
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+ FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1, // number of pipe instances
+ // output buffer size (XXX tune)
+ Channel::kReadBufferSize,
+ // input buffer size (XXX tune)
+ Channel::kReadBufferSize,
+ 5000, // timeout in milliseconds (XXX tune)
+ NULL);
+ } else {
+ pipe_ = CreateFileW(
+ pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | FILE_FLAG_OVERLAPPED,
+ NULL);
+ }
+ if (pipe_ == INVALID_HANDLE_VALUE) {
+ // If this process is being closed, the pipe may be gone already.
+ CHROMIUM_LOG(WARNING) << "failed to create pipe: " << GetLastError();
+ closed_ = true;
+ return false;
+ }
+
+ // Create the Hello message to be sent when Connect is called
+ return EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
+ auto m = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE);
+
+ // If we're waiting for our shared secret from the other end's hello message
+ // then don't give the game away by sending it in ours.
+ int32_t secret = waiting_for_shared_secret_ ? 0 : shared_secret_;
+
+ // Also, don't send if the value is zero (for IPC backwards compatability).
+ if (!m->WriteInt(GetCurrentProcessId()) ||
+ (secret && !m->WriteUInt32(secret))) {
+ CloseHandle(pipe_);
+ pipe_ = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ OutputQueuePush(std::move(m));
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+#ifdef DEBUG
+ if (!_mOwningThread) {
+ _mOwningThread = mozilla::MakeUnique<nsAutoOwningThread>();
+ }
+#endif
+
+ if (pipe_ == INVALID_HANDLE_VALUE) return false;
+
+ MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
+
+ // Check to see if there is a client connected to our pipe...
+ if (waiting_connect_) {
+ if (!ProcessConnection()) {
+ return false;
+ }
+ }
+
+ if (!input_state_.is_pending) {
+ // Complete setup asynchronously. By not setting input_state_.is_pending
+ // to true, we indicate to OnIOCompleted that this is the special
+ // initialization signal.
+ MessageLoopForIO::current()->PostTask(factory_.NewRunnableMethod(
+ &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
+ }
+
+ if (!waiting_connect_) ProcessOutgoingMessages(NULL, 0);
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessConnection() {
+ ASSERT_OWNINGTHREAD(ChannelImpl);
+ if (input_state_.is_pending) input_state_.is_pending = false;
+
+ // Do we have a client connected to our pipe?
+ if (INVALID_HANDLE_VALUE == pipe_) return false;
+
+ BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
+
+ DWORD err = GetLastError();
+ if (ok) {
+ // Uhm, the API documentation says that this function should never
+ // return success when used in overlapped mode.
+ NOTREACHED();
+ return false;
+ }
+
+ switch (err) {
+ case ERROR_IO_PENDING:
+ input_state_.is_pending = true;
+ break;
+ case ERROR_PIPE_CONNECTED:
+ waiting_connect_ = false;
+ break;
+ case ERROR_NO_DATA:
+ // The pipe is being closed.
+ return false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessIncomingMessages(
+ MessageLoopForIO::IOContext* context, DWORD bytes_read) {
+ ASSERT_OWNINGTHREAD(ChannelImpl);
+ if (input_state_.is_pending) {
+ input_state_.is_pending = false;
+ DCHECK(context);
+
+ if (!context || !bytes_read) return false;
+ } else {
+ // This happens at channel initialization.
+ DCHECK(!bytes_read && context == &input_state_.context);
+ }
+
+ for (;;) {
+ if (bytes_read == 0) {
+ if (INVALID_HANDLE_VALUE == pipe_) return false;
+
+ // Read from pipe...
+ BOOL ok = ReadFile(pipe_, input_buf_.get() + input_buf_offset_,
+ Channel::kReadBufferSize - input_buf_offset_,
+ &bytes_read, &input_state_.context.overlapped);
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ input_state_.is_pending = true;
+ return true;
+ }
+ if (err != ERROR_BROKEN_PIPE) {
+ CHROMIUM_LOG(ERROR) << "pipe error: " << err;
+ }
+ return false;
+ }
+ input_state_.is_pending = true;
+ return true;
+ }
+ DCHECK(bytes_read);
+
+ // Process messages from input buffer.
+
+ const char* p = input_buf_.get();
+ const char* end = input_buf_.get() + input_buf_offset_ + bytes_read;
+
+ while (p < end) {
+ // Try to figure out how big the message is. Size is 0 if we haven't read
+ // enough of the header to know the size.
+ uint32_t message_length = 0;
+ if (incoming_message_.isSome()) {
+ message_length = incoming_message_.ref().size();
+ } else {
+ message_length = Message::MessageSize(p, end);
+ }
+
+ if (!message_length) {
+ // We haven't seen the full message header.
+ MOZ_ASSERT(incoming_message_.isNothing());
+
+ // Move everything we have to the start of the buffer. We'll finish
+ // reading this message when we get more data. For now we leave it in
+ // input_buf_.
+ memmove(input_buf_.get(), p, end - p);
+ input_buf_offset_ = end - p;
+
+ break;
+ }
+
+ input_buf_offset_ = 0;
+
+ bool partial;
+ if (incoming_message_.isSome()) {
+ // We already have some data for this message stored in
+ // incoming_message_. We want to append the new data there.
+ Message& m = incoming_message_.ref();
+
+ // How much data from this message remains to be added to
+ // incoming_message_?
+ MOZ_ASSERT(message_length > m.CurrentSize());
+ uint32_t remaining = message_length - m.CurrentSize();
+
+ // How much data from this message is stored in input_buf_?
+ uint32_t in_buf = std::min(remaining, uint32_t(end - p));
+
+ m.InputBytes(p, in_buf);
+ p += in_buf;
+
+ // Are we done reading this message?
+ partial = in_buf != remaining;
+ } else {
+ // How much data from this message is stored in input_buf_?
+ uint32_t in_buf = std::min(message_length, uint32_t(end - p));
+
+ incoming_message_.emplace(p, in_buf);
+ p += in_buf;
+
+ // Are we done reading this message?
+ partial = in_buf != message_length;
+ }
+
+ if (partial) {
+ break;
+ }
+
+ Message& m = incoming_message_.ref();
+
+ // Note: We set other_pid_ below when we receive a Hello message (which
+ // has no routing ID), but we only emit a profiler marker for messages
+ // with a routing ID, so there's no conflict here.
+ AddIPCProfilerMarker(m, other_pid_, MessageDirection::eReceiving,
+ MessagePhase::TransferStart);
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "received message on channel @" << this << " with type "
+ << m.type();
+#endif
+ if (m.routing_id() == MSG_ROUTING_NONE &&
+ m.type() == HELLO_MESSAGE_TYPE) {
+ // The Hello message contains the process id and must include the
+ // shared secret, if we are waiting for it.
+ MessageIterator it = MessageIterator(m);
+ other_pid_ = it.NextInt();
+ if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
+ NOTREACHED();
+ // Something went wrong. Abort connection.
+ Close();
+ listener_->OnChannelError();
+ return false;
+ }
+ waiting_for_shared_secret_ = false;
+ listener_->OnChannelConnected(other_pid_);
+ } else {
+ mozilla::LogIPCMessage::Run run(&m);
+ listener_->OnMessageReceived(std::move(m));
+ }
+
+ incoming_message_.reset();
+ }
+
+ bytes_read = 0; // Get more data.
+ }
+
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessOutgoingMessages(
+ MessageLoopForIO::IOContext* context, DWORD bytes_written) {
+ DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
+ // no connection?
+ ASSERT_OWNINGTHREAD(ChannelImpl);
+
+ if (output_state_.is_pending) {
+ DCHECK(context);
+ output_state_.is_pending = false;
+ if (!context || bytes_written == 0) {
+ DWORD err = GetLastError();
+ if (err != ERROR_BROKEN_PIPE) {
+ CHROMIUM_LOG(ERROR) << "pipe error: " << err;
+ }
+ return false;
+ }
+ // Message was sent.
+ DCHECK(!output_queue_.IsEmpty());
+ Message* m = output_queue_.FirstElement().get();
+
+ MOZ_RELEASE_ASSERT(partial_write_iter_.isSome());
+ Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
+ iter.Advance(m->Buffers(), bytes_written);
+ if (iter.Done()) {
+ AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
+ MessagePhase::TransferEnd);
+
+ partial_write_iter_.reset();
+ OutputQueuePop();
+ // m has been destroyed, so clear the dangling reference.
+ m = nullptr;
+ }
+ }
+
+ if (output_queue_.IsEmpty()) return true;
+
+ if (INVALID_HANDLE_VALUE == pipe_) return false;
+
+ // Write to pipe...
+ Message* m = output_queue_.FirstElement().get();
+
+ if (partial_write_iter_.isNothing()) {
+ Pickle::BufferList::IterImpl iter(m->Buffers());
+ partial_write_iter_.emplace(iter);
+ }
+
+ Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
+
+ AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
+ MessagePhase::TransferStart);
+
+ // Don't count this write for the purposes of late write checking. If this
+ // message results in a legitimate file write, that will show up when it
+ // happens.
+ mozilla::PushSuspendLateWriteChecks();
+ BOOL ok = WriteFile(pipe_, iter.Data(), iter.RemainingInSegment(),
+ &bytes_written, &output_state_.context.overlapped);
+ mozilla::PopSuspendLateWriteChecks();
+
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ output_state_.is_pending = true;
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent pending message @" << m << " on channel @" << this
+ << " with type " << m->type();
+#endif
+
+ return true;
+ }
+ if (err != ERROR_BROKEN_PIPE) {
+ CHROMIUM_LOG(ERROR) << "pipe error: " << err;
+ }
+ return false;
+ }
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent message @" << m << " on channel @" << this
+ << " with type " << m->type();
+#endif
+
+ output_state_.is_pending = true;
+ return true;
+}
+
+void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered, DWORD error) {
+ bool ok;
+ ASSERT_OWNINGTHREAD(ChannelImpl);
+ if (context == &input_state_.context) {
+ if (waiting_connect_) {
+ if (!ProcessConnection()) return;
+ // We may have some messages queued up to send...
+ if (!output_queue_.IsEmpty() && !output_state_.is_pending)
+ ProcessOutgoingMessages(NULL, 0);
+ if (input_state_.is_pending) return;
+ // else, fall-through and look for incoming messages...
+ }
+ // we don't support recursion through OnMessageReceived yet!
+ DCHECK(!processing_incoming_);
+ processing_incoming_ = true;
+ ok = ProcessIncomingMessages(context, bytes_transfered);
+ processing_incoming_ = false;
+ } else {
+ DCHECK(context == &output_state_.context);
+ ok = ProcessOutgoingMessages(context, bytes_transfered);
+ }
+ if (!ok && INVALID_HANDLE_VALUE != pipe_) {
+ // We don't want to re-enter Close().
+ Close();
+ listener_->OnChannelError();
+ }
+}
+
+bool Channel::ChannelImpl::Unsound_IsClosed() const { return closed_; }
+
+uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const {
+ return output_queue_length_;
+}
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const ChannelId& channel_id, Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
+ MOZ_COUNT_CTOR(IPC::Channel);
+}
+
+Channel::Channel(const ChannelId& channel_id, void* server_pipe, Mode mode,
+ Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, server_pipe, mode, listener)) {
+ MOZ_COUNT_CTOR(IPC::Channel);
+}
+
+Channel::~Channel() {
+ MOZ_COUNT_DTOR(IPC::Channel);
+ delete channel_impl_;
+}
+
+bool Channel::Connect() { return channel_impl_->Connect(); }
+
+void Channel::Close() { channel_impl_->Close(); }
+
+void* Channel::GetServerPipeHandle() const {
+ return channel_impl_->GetServerPipeHandle();
+}
+
+Channel::Listener* Channel::set_listener(Listener* listener) {
+ return channel_impl_->set_listener(listener);
+}
+
+bool Channel::Send(mozilla::UniquePtr<Message> message) {
+ return channel_impl_->Send(std::move(message));
+}
+
+bool Channel::Unsound_IsClosed() const {
+ return channel_impl_->Unsound_IsClosed();
+}
+
+uint32_t Channel::Unsound_NumQueuedMessages() const {
+ return channel_impl_->Unsound_NumQueuedMessages();
+}
+
+namespace {
+
+// Global atomic used to guarantee channel IDs are unique.
+mozilla::Atomic<int> g_last_id;
+
+} // namespace
+
+// static
+Channel::ChannelId Channel::GenerateVerifiedChannelID() {
+ // Windows pipes can be enumerated by low-privileged processes. So, we
+ // append a strong random value after the \ character. This value is not
+ // included in the pipe name, but sent as part of the client hello, to
+ // prevent hijacking the pipe name to spoof the client.
+ int secret;
+ do { // Guarantee we get a non-zero value.
+ secret = base::RandInt(0, std::numeric_limits<int>::max());
+ } while (secret == 0);
+ return StringPrintf(L"%d.%u.%d\\%d", base::GetCurrentProcId(), g_last_id++,
+ base::RandInt(0, std::numeric_limits<int32_t>::max()),
+ secret);
+}
+
+// static
+Channel::ChannelId Channel::ChannelIDForCurrentProcess() {
+ return CommandLine::ForCurrentProcess()->GetSwitchValue(
+ switches::kProcessChannelID);
+}
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.h b/ipc/chromium/src/chrome/common/ipc_channel_win.h
new file mode 100644
index 0000000000..ac21c16b8c
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_WIN_H_
+#define CHROME_COMMON_IPC_CHANNEL_WIN_H_
+
+#include "chrome/common/ipc_channel.h"
+#include "chrome/common/ipc_message.h"
+
+#include <string>
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/Maybe.h"
+#include "mozilla/Queue.h"
+#include "mozilla/UniquePtr.h"
+
+namespace IPC {
+
+class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
+ public:
+ using ChannelId = Channel::ChannelId;
+
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const ChannelId& channel_id, Mode mode, Listener* listener);
+ ChannelImpl(const ChannelId& channel_id, HANDLE server_pipe, Mode mode,
+ Listener* listener);
+ ~ChannelImpl() {
+ if (pipe_ != INVALID_HANDLE_VALUE) {
+ Close();
+ }
+ }
+ bool Connect();
+ void Close();
+ HANDLE GetServerPipeHandle() const;
+ Listener* set_listener(Listener* listener) {
+ Listener* old = listener_;
+ listener_ = listener;
+ return old;
+ }
+ bool Send(mozilla::UniquePtr<Message> message);
+
+ // See the comment in ipc_channel.h for info on Unsound_IsClosed() and
+ // Unsound_NumQueuedMessages().
+ bool Unsound_IsClosed() const;
+ uint32_t Unsound_NumQueuedMessages() const;
+
+ private:
+ void Init(Mode mode, Listener* listener);
+
+ void OutputQueuePush(mozilla::UniquePtr<Message> msg);
+ void OutputQueuePop();
+
+ const ChannelId PipeName(const ChannelId& channel_id, int32_t* secret) const;
+ bool CreatePipe(const ChannelId& channel_id, Mode mode);
+ bool EnqueueHelloMessage();
+
+ bool ProcessConnection();
+ bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
+ DWORD bytes_read);
+ bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
+ DWORD bytes_written);
+
+ // MessageLoop::IOHandler implementation.
+ virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered, DWORD error);
+
+ private:
+ struct State {
+ explicit State(ChannelImpl* channel);
+ ~State();
+ MessageLoopForIO::IOContext context;
+ bool is_pending;
+ };
+
+ State input_state_;
+ State output_state_;
+
+ HANDLE pipe_;
+
+ Listener* listener_;
+
+ // Messages to be sent are queued here.
+ mozilla::Queue<mozilla::UniquePtr<Message>, 64> output_queue_;
+
+ // If sending a message blocks then we use this iterator to keep track of
+ // where in the message we are. It gets reset when the message is finished
+ // sending.
+ mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_;
+
+ // We read from the pipe into this buffer
+ mozilla::UniquePtr<char[]> input_buf_;
+ size_t input_buf_offset_;
+
+ // Large incoming messages that span multiple pipe buffers get built-up in the
+ // buffers of this message.
+ mozilla::Maybe<Message> incoming_message_;
+
+ // In server-mode, we have to wait for the client to connect before we
+ // can begin reading. We make use of the input_state_ when performing
+ // the connect operation in overlapped mode.
+ bool waiting_connect_;
+
+ // This flag is set when processing incoming messages. It is used to
+ // avoid recursing through ProcessIncomingMessages, which could cause
+ // problems. TODO(darin): make this unnecessary
+ bool processing_incoming_;
+
+ // This flag is set after Close() is run on the channel.
+ bool closed_;
+
+ // We keep track of the PID of the other side of this channel so that we can
+ // record this when generating logs of IPC messages.
+ int32_t other_pid_ = -1;
+
+ // This variable is updated so it matches output_queue_.Count(), except we can
+ // read output_queue_length_ from any thread (if we're OK getting an
+ // occasional out-of-date or bogus value). We use output_queue_length_ to
+ // implement Unsound_NumQueuedMessages.
+ size_t output_queue_length_;
+
+ ScopedRunnableMethodFactory<ChannelImpl> factory_;
+
+ // This is a unique per-channel value used to authenticate the client end of
+ // a connection. If the value is non-zero, the client passes it in the hello
+ // and the host validates. (We don't send the zero value to preserve IPC
+ // compatibility with existing clients that don't validate the channel.)
+ int32_t shared_secret_;
+
+ // In server-mode, we wait for the channel at the other side of the pipe to
+ // send us back our shared secret, if we are using one.
+ bool waiting_for_shared_secret_;
+
+#ifdef DEBUG
+ mozilla::UniquePtr<nsAutoOwningThread> _mOwningThread;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_WIN_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_message.cc b/ipc/chromium/src/chrome/common/ipc_message.cc
new file mode 100644
index 0000000000..dc83f9acdf
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_message.cc
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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/ipc_message.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+# include "chrome/common/file_descriptor_set_posix.h"
+#endif
+#ifdef MOZ_TASK_TRACER
+# include "GeckoTaskTracerImpl.h"
+#endif
+
+#include <utility>
+
+#include "nsISupportsImpl.h"
+
+#ifdef MOZ_TASK_TRACER
+using namespace mozilla::tasktracer;
+
+# define MSG_HEADER_SZ \
+ (IsStartLogging() && GetOrCreateTraceInfo() == nullptr \
+ ? sizeof(Header) \
+ : sizeof(HeaderTaskTracer))
+#else
+# define MSG_HEADER_SZ sizeof(Header)
+#endif
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+Message::~Message() { MOZ_COUNT_DTOR(IPC::Message); }
+
+Message::Message() : Pickle(MSG_HEADER_SZ) {
+ MOZ_COUNT_CTOR(IPC::Message);
+ header()->routing = header()->type = 0;
+#if defined(OS_POSIX)
+ header()->num_fds = 0;
+#endif
+#ifdef MOZ_TASK_TRACER
+ if (UseTaskTracerHeader()) {
+ header()->flags.SetTaskTracer();
+ HeaderTaskTracer* _header = static_cast<HeaderTaskTracer*>(header());
+ GetCurTraceInfo(&_header->source_event_id, &_header->parent_task_id,
+ &_header->source_event_type);
+ }
+#endif
+}
+
+Message::Message(int32_t routing_id, msgid_t type, uint32_t segment_capacity,
+ HeaderFlags flags, bool recordWriteLatency)
+ : Pickle(MSG_HEADER_SZ, segment_capacity) {
+ MOZ_COUNT_CTOR(IPC::Message);
+ header()->routing = routing_id;
+ header()->type = type;
+ header()->flags = flags;
+#if defined(OS_POSIX)
+ header()->num_fds = 0;
+#endif
+ header()->interrupt_remote_stack_depth_guess = static_cast<uint32_t>(-1);
+ header()->interrupt_local_stack_depth = static_cast<uint32_t>(-1);
+ header()->seqno = 0;
+#if defined(OS_MACOSX)
+ header()->cookie = 0;
+#endif
+#ifdef MOZ_TASK_TRACER
+ if (UseTaskTracerHeader()) {
+ header()->flags.SetTaskTracer();
+ HeaderTaskTracer* _header = static_cast<HeaderTaskTracer*>(header());
+ GetCurTraceInfo(&_header->source_event_id, &_header->parent_task_id,
+ &_header->source_event_type);
+ }
+#endif
+ if (recordWriteLatency) {
+ create_time_ = mozilla::TimeStamp::Now();
+ }
+}
+
+#ifndef MOZ_TASK_TRACER
+# define MSG_HEADER_SZ_DATA sizeof(Header)
+#else
+# define MSG_HEADER_SZ_DATA \
+ (reinterpret_cast<const Header*>(data)->flags.IsTaskTracer() \
+ ? sizeof(HeaderTaskTracer) \
+ : sizeof(Header))
+#endif
+
+Message::Message(const char* data, int data_len)
+ : Pickle(MSG_HEADER_SZ_DATA, data, data_len) {
+ MOZ_COUNT_CTOR(IPC::Message);
+}
+
+Message::Message(Message&& other) : Pickle(std::move(other)) {
+ MOZ_COUNT_CTOR(IPC::Message);
+#if defined(OS_POSIX)
+ file_descriptor_set_ = std::move(other.file_descriptor_set_);
+#endif
+}
+
+/*static*/ Message* Message::IPDLMessage(int32_t routing_id, msgid_t type,
+ HeaderFlags flags) {
+ return new Message(routing_id, type, 0, flags, true);
+}
+
+/*static*/ Message* Message::ForSyncDispatchError(NestedLevel level) {
+ auto* m = new Message(0, 0, 0, HeaderFlags(level));
+ auto& flags = m->header()->flags;
+ flags.SetSync();
+ flags.SetReply();
+ flags.SetReplyError();
+ return m;
+}
+
+/*static*/ Message* Message::ForInterruptDispatchError() {
+ auto* m = new Message();
+ auto& flags = m->header()->flags;
+ flags.SetInterrupt();
+ flags.SetReply();
+ flags.SetReplyError();
+ return m;
+}
+
+Message& Message::operator=(Message&& other) {
+ *static_cast<Pickle*>(this) = std::move(other);
+#if defined(OS_POSIX)
+ file_descriptor_set_.swap(other.file_descriptor_set_);
+#endif
+ return *this;
+}
+
+void Message::CopyFrom(const Message& other) {
+ Pickle::CopyFrom(other);
+#if defined(OS_POSIX)
+ MOZ_ASSERT(!file_descriptor_set_);
+ if (other.file_descriptor_set_) {
+ file_descriptor_set_ = new FileDescriptorSet;
+ file_descriptor_set_->CopyFrom(*other.file_descriptor_set_);
+ }
+#endif
+}
+
+#if defined(OS_POSIX)
+bool Message::WriteFileDescriptor(const base::FileDescriptor& descriptor) {
+ // We write the index of the descriptor so that we don't have to
+ // keep the current descriptor as extra decoding state when deserialising.
+ // Also, we rely on each file descriptor being accompanied by sizeof(int)
+ // bytes of data in the message. See the comment for input_cmsg_buf_.
+ WriteInt(file_descriptor_set()->size());
+ if (descriptor.auto_close) {
+ return file_descriptor_set()->AddAndAutoClose(descriptor.fd);
+ } else {
+ return file_descriptor_set()->Add(descriptor.fd);
+ }
+}
+
+bool Message::ReadFileDescriptor(PickleIterator* iter,
+ base::FileDescriptor* descriptor) const {
+ int descriptor_index;
+ if (!ReadInt(iter, &descriptor_index)) return false;
+
+ FileDescriptorSet* file_descriptor_set = file_descriptor_set_.get();
+ if (!file_descriptor_set) return false;
+
+ descriptor->fd = file_descriptor_set->GetDescriptorAt(descriptor_index);
+ descriptor->auto_close = false;
+
+ return descriptor->fd >= 0;
+}
+
+void Message::EnsureFileDescriptorSet() {
+ if (file_descriptor_set_.get() == NULL)
+ file_descriptor_set_ = new FileDescriptorSet;
+}
+
+uint32_t Message::num_fds() const {
+ return file_descriptor_set() ? file_descriptor_set()->size() : 0;
+}
+
+#endif
+
+void Message::AssertAsLargeAsHeader() const {
+ MOZ_DIAGNOSTIC_ASSERT(size() >= MSG_HEADER_SZ);
+ MOZ_DIAGNOSTIC_ASSERT(CurrentSize() >= MSG_HEADER_SZ);
+ // Our buffers should agree with what our header specifies.
+ MOZ_DIAGNOSTIC_ASSERT(size() == CurrentSize());
+}
+
+#ifdef MOZ_TASK_TRACER
+void* MessageTask() { return reinterpret_cast<void*>(&MessageTask); }
+
+void Message::TaskTracerDispatch() {
+ if (header()->flags.IsTaskTracer()) {
+ HeaderTaskTracer* _header = static_cast<HeaderTaskTracer*>(header());
+ _header->task_id = GenNewUniqueTaskId();
+ uintptr_t* vtab = reinterpret_cast<uintptr_t*>(&MessageTask);
+ LogVirtualTablePtr(_header->task_id, _header->source_event_id, vtab);
+ LogDispatch(_header->task_id, _header->parent_task_id,
+ _header->source_event_id, _header->source_event_type);
+ }
+}
+
+Message::AutoTaskTracerRun::AutoTaskTracerRun(Message& aMsg)
+ : mMsg(aMsg), mTaskId(0), mSourceEventId(0) {
+ if (mMsg.header()->flags.IsTaskTracer()) {
+ const HeaderTaskTracer* _header =
+ static_cast<HeaderTaskTracer*>(mMsg.header());
+ LogBegin(_header->task_id, _header->source_event_id);
+ SetCurTraceInfo(_header->source_event_id, _header->task_id,
+ _header->source_event_type);
+ mTaskId = _header->task_id;
+ mSourceEventId = _header->source_event_id;
+ } else {
+ SetCurTraceInfo(0, 0, SourceEventType::Unknown);
+ }
+}
+
+Message::AutoTaskTracerRun::~AutoTaskTracerRun() {
+ if (mTaskId) {
+ AddLabel("IPC Message %s", mMsg.name());
+ LogEnd(mTaskId, mSourceEventId);
+ }
+}
+#endif
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_message.h b/ipc/chromium/src/chrome/common/ipc_message.h
new file mode 100644
index 0000000000..934a9b3459
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_message.h
@@ -0,0 +1,464 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_IPC_MESSAGE_H__
+#define CHROME_COMMON_IPC_MESSAGE_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/pickle.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+
+#ifdef MOZ_TASK_TRACER
+# include "GeckoTaskTracer.h"
+#endif
+
+#ifdef FUZZING
+# include "mozilla/ipc/Faulty.h"
+#endif
+
+namespace base {
+struct FileDescriptor;
+}
+
+namespace mozilla {
+namespace ipc {
+class MiniTransceiver;
+}
+} // namespace mozilla
+
+class FileDescriptorSet;
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+// Generated by IPDL compiler
+const char* StringFromIPCMessageType(uint32_t aMessageType);
+
+class Channel;
+class Message;
+#ifdef FUZZING
+class Faulty;
+#endif
+struct LogData;
+
+class Message : public Pickle {
+ public:
+ typedef uint32_t msgid_t;
+
+ enum NestedLevel {
+ NOT_NESTED = 1,
+ NESTED_INSIDE_SYNC = 2,
+ NESTED_INSIDE_CPOW = 3
+ };
+
+ enum PriorityValue {
+ NORMAL_PRIORITY = 0,
+ INPUT_PRIORITY = 1,
+ HIGH_PRIORITY = 2,
+ MEDIUMHIGH_PRIORITY = 3,
+ };
+
+ enum MessageCompression {
+ COMPRESSION_NONE,
+ COMPRESSION_ENABLED,
+ COMPRESSION_ALL
+ };
+
+ enum Sync {
+ SYNC = 0,
+ ASYNC = 1,
+ };
+
+ enum Interrupt {
+ NOT_INTERRUPT = 0,
+ INTERRUPT = 1,
+ };
+
+ enum Constructor {
+ NOT_CONSTRUCTOR = 0,
+ CONSTRUCTOR = 1,
+ };
+
+ enum Reply {
+ NOT_REPLY = 0,
+ REPLY = 1,
+ };
+
+ class HeaderFlags {
+ friend class Message;
+
+ enum {
+ NESTED_MASK = 0x0003,
+ PRIO_MASK = 0x000C,
+ SYNC_BIT = 0x0010,
+ REPLY_BIT = 0x0020,
+ REPLY_ERROR_BIT = 0x0040,
+ INTERRUPT_BIT = 0x0080,
+ COMPRESS_BIT = 0x0100,
+ COMPRESSALL_BIT = 0x0200,
+ COMPRESS_MASK = 0x0300,
+ CONSTRUCTOR_BIT = 0x0400,
+#ifdef MOZ_TASK_TRACER
+ TASKTRACER_BIT = 0x0800,
+#endif
+ };
+
+ public:
+ constexpr HeaderFlags() : mFlags(NOT_NESTED) {}
+
+ explicit constexpr HeaderFlags(NestedLevel level) : mFlags(level) {}
+
+ constexpr HeaderFlags(NestedLevel level, PriorityValue priority,
+ MessageCompression compression,
+ Constructor constructor, Sync sync,
+ Interrupt interrupt, Reply reply)
+ : mFlags(level | (priority << 2) |
+ (compression == COMPRESSION_ENABLED ? COMPRESS_BIT
+ : compression == COMPRESSION_ALL ? COMPRESSALL_BIT
+ : 0) |
+ (constructor == CONSTRUCTOR ? CONSTRUCTOR_BIT : 0) |
+ (sync == SYNC ? SYNC_BIT : 0) |
+ (interrupt == INTERRUPT ? INTERRUPT_BIT : 0) |
+ (reply == REPLY ? REPLY_BIT : 0)) {}
+
+ NestedLevel Level() const {
+ return static_cast<NestedLevel>(mFlags & NESTED_MASK);
+ }
+
+ PriorityValue Priority() const {
+ return static_cast<PriorityValue>((mFlags & PRIO_MASK) >> 2);
+ }
+
+ MessageCompression Compression() const {
+ return ((mFlags & COMPRESS_BIT) ? COMPRESSION_ENABLED
+ : (mFlags & COMPRESSALL_BIT) ? COMPRESSION_ALL
+ : COMPRESSION_NONE);
+ }
+
+ bool IsConstructor() const { return (mFlags & CONSTRUCTOR_BIT) != 0; }
+ bool IsSync() const { return (mFlags & SYNC_BIT) != 0; }
+ bool IsInterrupt() const { return (mFlags & INTERRUPT_BIT) != 0; }
+ bool IsReply() const { return (mFlags & REPLY_BIT) != 0; }
+
+ bool IsReplyError() const { return (mFlags & REPLY_ERROR_BIT) != 0; }
+
+#ifdef MOZ_TASK_TRACER
+ bool IsTaskTracer() const { return (mFlags & TASKTRACER_BIT) != 0; }
+#endif
+
+ private:
+ void SetSync() { mFlags |= SYNC_BIT; }
+ void SetInterrupt() { mFlags |= INTERRUPT_BIT; }
+ void SetReply() { mFlags |= REPLY_BIT; }
+ void SetReplyError() { mFlags |= REPLY_ERROR_BIT; }
+
+#ifdef MOZ_TASK_TRACER
+ void SetTaskTracer() { mFlags |= TASKTRACER_BIT; }
+#endif
+
+ uint32_t mFlags;
+ };
+
+ virtual ~Message();
+
+ Message();
+
+ // Initialize a message with a user-defined type, priority value, and
+ // destination WebView ID.
+ //
+ // NOTE: `recordWriteLatency` is only passed by IPDL generated message code,
+ // and is used to trigger the IPC_WRITE_LATENCY_MS telemetry.
+ Message(int32_t routing_id, msgid_t type,
+ uint32_t segmentCapacity = 0, // 0 for the default capacity.
+ HeaderFlags flags = HeaderFlags(), bool recordWriteLatency = false);
+
+ Message(const char* data, int data_len);
+
+ Message(const Message& other) = delete;
+ Message(Message&& other);
+ Message& operator=(const Message& other) = delete;
+ Message& operator=(Message&& other);
+
+ void CopyFrom(const Message& other);
+
+ // Helper method for the common case (default segmentCapacity, recording
+ // the write latency of messages) of IPDL message creation. This helps
+ // move the malloc and some of the parameter setting out of autogenerated
+ // code.
+ static Message* IPDLMessage(int32_t routing_id, msgid_t type,
+ HeaderFlags flags);
+
+ // One-off constructors for special error-handling messages.
+ static Message* ForSyncDispatchError(NestedLevel level);
+ static Message* ForInterruptDispatchError();
+
+ NestedLevel nested_level() const { return header()->flags.Level(); }
+
+ PriorityValue priority() const { return header()->flags.Priority(); }
+
+ bool is_constructor() const { return header()->flags.IsConstructor(); }
+
+ // True if this is a synchronous message.
+ bool is_sync() const { return header()->flags.IsSync(); }
+
+ // True if this is a synchronous message.
+ bool is_interrupt() const { return header()->flags.IsInterrupt(); }
+
+ MessageCompression compress_type() const {
+ return header()->flags.Compression();
+ }
+
+ bool is_reply() const { return header()->flags.IsReply(); }
+
+ bool is_reply_error() const { return header()->flags.IsReplyError(); }
+
+ bool is_valid() const { return !!header(); }
+
+ msgid_t type() const { return header()->type; }
+
+ int32_t routing_id() const { return header()->routing; }
+
+ void set_routing_id(int32_t new_id) { header()->routing = new_id; }
+
+ int32_t transaction_id() const { return header()->txid; }
+
+ void set_transaction_id(int32_t txid) { header()->txid = txid; }
+
+ uint32_t interrupt_remote_stack_depth_guess() const {
+ return header()->interrupt_remote_stack_depth_guess;
+ }
+
+ void set_interrupt_remote_stack_depth_guess(uint32_t depth) {
+ DCHECK(is_interrupt());
+ header()->interrupt_remote_stack_depth_guess = depth;
+ }
+
+ uint32_t interrupt_local_stack_depth() const {
+ return header()->interrupt_local_stack_depth;
+ }
+
+ void set_interrupt_local_stack_depth(uint32_t depth) {
+ DCHECK(is_interrupt());
+ header()->interrupt_local_stack_depth = depth;
+ }
+
+ int32_t seqno() const { return header()->seqno; }
+
+ void set_seqno(int32_t aSeqno) { header()->seqno = aSeqno; }
+
+ const char* name() const { return StringFromIPCMessageType(type()); }
+
+ const mozilla::TimeStamp& create_time() const { return create_time_; }
+
+#if defined(OS_POSIX)
+ uint32_t num_fds() const;
+#endif
+
+ template <class T>
+ static bool Dispatch(const Message* msg, T* obj, void (T::*func)()) {
+ (obj->*func)();
+ return true;
+ }
+
+ template <class T>
+ static bool Dispatch(const Message* msg, T* obj, void (T::*func)() const) {
+ (obj->*func)();
+ return true;
+ }
+
+ template <class T>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&)) {
+ (obj->*func)(*msg);
+ return true;
+ }
+
+ template <class T>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&) const) {
+ (obj->*func)(*msg);
+ return true;
+ }
+
+ // We should not be sending messages that are smaller than our header size.
+ void AssertAsLargeAsHeader() const;
+
+ // Used for async messages with no parameters.
+ static void Log(const Message* msg, std::wstring* l) {}
+
+ static int HeaderSizeFromData(const char* range_start,
+ const char* range_end) {
+#ifdef MOZ_TASK_TRACER
+ return ((static_cast<unsigned int>(range_end - range_start) >=
+ sizeof(Header)) &&
+ (reinterpret_cast<const Header*>(range_start)
+ ->flags.IsTaskTracer()))
+ ? sizeof(HeaderTaskTracer)
+ : sizeof(Header);
+#else
+ return sizeof(Header);
+#endif
+ }
+
+ // Figure out how big the message starting at range_start is. Returns 0 if
+ // there's no enough data to determine (i.e., if [range_start, range_end) does
+ // not contain enough of the message header to know the size).
+ static uint32_t MessageSize(const char* range_start, const char* range_end) {
+ return Pickle::MessageSize(HeaderSizeFromData(range_start, range_end),
+ range_start, range_end);
+ }
+
+#if defined(OS_POSIX)
+ // On POSIX, a message supports reading / writing FileDescriptor objects.
+ // This is used to pass a file descriptor to the peer of an IPC channel.
+
+ // Add a descriptor to the end of the set. Returns false iff the set is full.
+ bool WriteFileDescriptor(const base::FileDescriptor& descriptor);
+ // Get a file descriptor from the message. Returns false on error.
+ // iter: a Pickle iterator to the current location in the message.
+ bool ReadFileDescriptor(PickleIterator* iter,
+ base::FileDescriptor* descriptor) const;
+
+# if defined(OS_MACOSX)
+ void set_fd_cookie(uint32_t cookie) { header()->cookie = cookie; }
+ uint32_t fd_cookie() const { return header()->cookie; }
+# endif
+#endif
+
+ friend class Channel;
+ friend class MessageReplyDeserializer;
+ friend class SyncMessage;
+#ifdef FUZZING
+ friend class mozilla::ipc::Faulty;
+#endif
+ friend class mozilla::ipc::MiniTransceiver;
+
+#ifdef MOZ_TASK_TRACER
+ void TaskTracerDispatch();
+ class AutoTaskTracerRun : public mozilla::tasktracer::AutoSaveCurTraceInfo {
+ Message& mMsg;
+ uint64_t mTaskId;
+ uint64_t mSourceEventId;
+
+ public:
+ explicit AutoTaskTracerRun(Message& aMsg);
+ ~AutoTaskTracerRun();
+ };
+#endif
+
+#if !defined(OS_MACOSX)
+ protected:
+#endif
+
+ struct Header : Pickle::Header {
+ int32_t routing; // ID of the view that this message is destined for
+ msgid_t type; // specifies the user-defined message type
+ HeaderFlags flags; // specifies control flags for the message
+#if defined(OS_POSIX)
+ uint32_t num_fds; // the number of descriptors included with this message
+# if defined(OS_MACOSX)
+ uint32_t cookie; // cookie to ACK that the descriptors have been read.
+# endif
+#endif
+ union {
+ // For Interrupt messages, a guess at what the *other* side's stack depth
+ // is.
+ uint32_t interrupt_remote_stack_depth_guess;
+
+ // For RPC and Urgent messages, a transaction ID for message ordering.
+ int32_t txid;
+ };
+ // The actual local stack depth.
+ uint32_t interrupt_local_stack_depth;
+ // Sequence number
+ int32_t seqno;
+ };
+
+#ifdef MOZ_TASK_TRACER
+ /**
+ * The type is used as headers of Messages only if TaskTracer is
+ * enabled, or type |Header| would be used instead.
+ */
+ struct HeaderTaskTracer : public Header {
+ uint64_t task_id;
+ uint64_t source_event_id;
+ uint64_t parent_task_id;
+ mozilla::tasktracer::SourceEventType source_event_type;
+ };
+#endif
+
+#ifdef MOZ_TASK_TRACER
+ bool UseTaskTracerHeader() const {
+ return sizeof(HeaderTaskTracer) == (size() - payload_size());
+ }
+
+ Header* header() {
+ return UseTaskTracerHeader() ? headerT<HeaderTaskTracer>()
+ : headerT<Header>();
+ }
+ const Header* header() const {
+ return UseTaskTracerHeader() ? headerT<HeaderTaskTracer>()
+ : headerT<Header>();
+ }
+#else
+ Header* header() { return headerT<Header>(); }
+ const Header* header() const { return headerT<Header>(); }
+#endif
+
+#if defined(OS_POSIX)
+ // The set of file descriptors associated with this message.
+ RefPtr<FileDescriptorSet> file_descriptor_set_;
+
+ // Ensure that a FileDescriptorSet is allocated
+ void EnsureFileDescriptorSet();
+
+ FileDescriptorSet* file_descriptor_set() {
+ EnsureFileDescriptorSet();
+ return file_descriptor_set_.get();
+ }
+ const FileDescriptorSet* file_descriptor_set() const {
+ return file_descriptor_set_.get();
+ }
+#endif
+
+ mozilla::TimeStamp create_time_;
+};
+
+class MessageInfo {
+ public:
+ typedef uint32_t msgid_t;
+
+ explicit MessageInfo(const Message& aMsg)
+ : mSeqno(aMsg.seqno()), mType(aMsg.type()) {}
+
+ int32_t seqno() const { return mSeqno; }
+ msgid_t type() const { return mType; }
+
+ private:
+ int32_t mSeqno;
+ msgid_t mType;
+};
+
+//------------------------------------------------------------------------------
+
+} // namespace IPC
+
+enum SpecialRoutingIDs {
+ // indicates that we don't have a routing ID yet.
+ MSG_ROUTING_NONE = kint32min,
+
+ // indicates a general message not sent to a particular tab.
+ MSG_ROUTING_CONTROL = kint32max
+};
+
+#define IPC_REPLY_ID 0xFFF0 // Special message id for replies
+#define IPC_LOGGING_ID 0xFFF1 // Special message id for logging
+
+#endif // CHROME_COMMON_IPC_MESSAGE_H__
diff --git a/ipc/chromium/src/chrome/common/ipc_message_utils.h b/ipc/chromium/src/chrome/common/ipc_message_utils.h
new file mode 100644
index 0000000000..d8bed78ac7
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_message_utils.h
@@ -0,0 +1,495 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_IPC_MESSAGE_UTILS_H_
+#define CHROME_COMMON_IPC_MESSAGE_UTILS_H_
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include "ErrorList.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/string_util.h"
+#include "build/build_config.h"
+#include "chrome/common/ipc_message.h"
+
+#if defined(OS_POSIX)
+# include "chrome/common/file_descriptor_set_posix.h"
+#endif
+#if defined(OS_WIN)
+# include "windows.h"
+#endif
+
+template <typename T>
+class RefPtr;
+template <typename T>
+class nsCOMPtr;
+
+namespace IPC {
+
+//-----------------------------------------------------------------------------
+// An iterator class for reading the fields contained within a Message.
+
+class MessageIterator {
+ public:
+ explicit MessageIterator(const Message& m) : msg_(m), iter_(m) {}
+ int NextInt() const {
+ int val;
+ if (!msg_.ReadInt(&iter_, &val)) NOTREACHED();
+ return val;
+ }
+ intptr_t NextIntPtr() const {
+ intptr_t val;
+ if (!msg_.ReadIntPtr(&iter_, &val)) NOTREACHED();
+ return val;
+ }
+ const std::string NextString() const {
+ std::string val;
+ if (!msg_.ReadString(&iter_, &val)) NOTREACHED();
+ return val;
+ }
+ const std::wstring NextWString() const {
+ std::wstring val;
+ if (!msg_.ReadWString(&iter_, &val)) NOTREACHED();
+ return val;
+ }
+
+ private:
+ const Message& msg_;
+ mutable PickleIterator iter_;
+};
+
+//-----------------------------------------------------------------------------
+// ParamTraits specializations, etc.
+//
+// The full set of types ParamTraits is specialized upon contains *possibly*
+// repeated types: unsigned long may be uint32_t or size_t, unsigned long long
+// may be uint64_t or size_t, nsresult may be uint32_t, and so on. You can't
+// have ParamTraits<unsigned int> *and* ParamTraits<uint32_t> if unsigned int
+// is uint32_t -- that's multiple definitions, and you can only have one.
+//
+// You could use #ifs and macro conditions to avoid duplicates, but they'd be
+// hairy: heavily dependent upon OS and compiler author choices, forced to
+// address all conflicts by hand. Happily there's a better way. The basic
+// idea looks like this, where T -> U represents T inheriting from U:
+//
+// class ParamTraits<P>
+// |
+// --> class ParamTraits1<P>
+// |
+// --> class ParamTraits2<P>
+// |
+// --> class ParamTraitsN<P> // or however many levels
+//
+// The default specialization of ParamTraits{M}<P> is an empty class that
+// inherits from ParamTraits{M + 1}<P> (or nothing in the base case).
+//
+// Now partition the set of parameter types into sets without duplicates.
+// Assign each set of types to a level M. Then specialize ParamTraitsM for
+// each of those types. A reference to ParamTraits<P> will consist of some
+// number of empty classes inheriting in sequence, ending in a non-empty
+// ParamTraits{N}<P>. It's okay for the parameter types to be duplicative:
+// either name of a type will resolve to the same ParamTraits{N}<P>.
+//
+// The nice thing is that because templates are instantiated lazily, if we
+// indeed have uint32_t == unsigned int, say, with the former in level N and
+// the latter in M > N, ParamTraitsM<unsigned int> won't be created (as long as
+// nobody uses ParamTraitsM<unsigned int>, but why would you), and no duplicate
+// code will be compiled or extra symbols generated. It's as efficient at
+// runtime as manually figuring out and avoiding conflicts by #ifs.
+//
+// The scheme we follow below names the various classes according to the types
+// in them, and the number of ParamTraits levels is larger, but otherwise it's
+// exactly the above idea.
+//
+
+template <class P>
+struct ParamTraits;
+
+template <typename P>
+static inline void WriteParam(Message* m, P&& p) {
+ ParamTraits<std::decay_t<P>>::Write(m, std::forward<P>(p));
+}
+
+template <typename P>
+static inline bool WARN_UNUSED_RESULT ReadParam(const Message* m,
+ PickleIterator* iter, P* p) {
+ return ParamTraits<P>::Read(m, iter, p);
+}
+
+template <typename P>
+static inline void LogParam(const P& p, std::wstring* l) {
+ ParamTraits<P>::Log(p, l);
+}
+
+// Fundamental types.
+
+template <class P>
+struct ParamTraitsFundamental {};
+
+template <>
+struct ParamTraitsFundamental<bool> {
+ typedef bool param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteBool(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadBool(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(p ? L"true" : L"false");
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<int> {
+ typedef int param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteInt(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadInt(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%d", p));
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<long> {
+ typedef long param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteLong(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadLong(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%l", p));
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<unsigned long> {
+ typedef unsigned long param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteULong(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadULong(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%ul", p));
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<long long> {
+ typedef long long param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteBytes(&p, sizeof(param_type));
+ }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadBytesInto(iter, r, sizeof(*r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%ll", p));
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<unsigned long long> {
+ typedef unsigned long long param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteBytes(&p, sizeof(param_type));
+ }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadBytesInto(iter, r, sizeof(*r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%ull", p));
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<double> {
+ typedef double param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteDouble(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadDouble(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"e", p));
+ }
+};
+
+// Fixed-size <stdint.h> types.
+
+template <class P>
+struct ParamTraitsFixed : ParamTraitsFundamental<P> {};
+
+template <>
+struct ParamTraitsFixed<int16_t> {
+ typedef int16_t param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteInt16(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadInt16(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%hd", p));
+ }
+};
+
+template <>
+struct ParamTraitsFixed<uint16_t> {
+ typedef uint16_t param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteUInt16(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadUInt16(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%hu", p));
+ }
+};
+
+template <>
+struct ParamTraitsFixed<uint32_t> {
+ typedef uint32_t param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteUInt32(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadUInt32(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%u", p));
+ }
+};
+
+template <>
+struct ParamTraitsFixed<int64_t> {
+ typedef int64_t param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteInt64(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadInt64(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%" PRId64L, p));
+ }
+};
+
+template <>
+struct ParamTraitsFixed<uint64_t> {
+ typedef uint64_t param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt64(static_cast<int64_t>(p));
+ }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadInt64(iter, reinterpret_cast<int64_t*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%" PRIu64L, p));
+ }
+};
+
+// std::* types.
+
+template <class P>
+struct ParamTraitsStd : ParamTraitsFixed<P> {};
+
+template <>
+struct ParamTraitsStd<std::string> {
+ typedef std::string param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteString(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadString(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(UTF8ToWide(p));
+ }
+};
+
+template <>
+struct ParamTraitsStd<std::wstring> {
+ typedef std::wstring param_type;
+ static void Write(Message* m, const param_type& p) { m->WriteWString(p); }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadWString(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) { l->append(p); }
+};
+
+template <class K, class V>
+struct ParamTraitsStd<std::map<K, V>> {
+ typedef std::map<K, V> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, static_cast<int>(p.size()));
+ typename param_type::const_iterator iter;
+ for (iter = p.begin(); iter != p.end(); ++iter) {
+ WriteParam(m, iter->first);
+ WriteParam(m, iter->second);
+ }
+ }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ int size;
+ if (!ReadParam(m, iter, &size) || size < 0) return false;
+ for (int i = 0; i < size; ++i) {
+ K k;
+ if (!ReadParam(m, iter, &k)) return false;
+ V& value = (*r)[k];
+ if (!ReadParam(m, iter, &value)) return false;
+ }
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(L"<std::map>");
+ }
+};
+
+// Windows-specific types.
+
+template <class P>
+struct ParamTraitsWindows : ParamTraitsStd<P> {};
+
+#if defined(OS_WIN)
+template <>
+struct ParamTraitsWindows<HANDLE> {
+ static_assert(sizeof(HANDLE) == sizeof(intptr_t), "Wrong size for HANDLE?");
+
+ static void Write(Message* m, HANDLE p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, PickleIterator* iter, HANDLE* r) {
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+ static void Log(const HANDLE& p, std::wstring* l) {
+ l->append(StringPrintf(L"0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraitsWindows<HWND> {
+ static_assert(sizeof(HWND) == sizeof(intptr_t), "Wrong size for HWND?");
+
+ static void Write(Message* m, HWND p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, PickleIterator* iter, HWND* r) {
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+ static void Log(const HWND& p, std::wstring* l) {
+ l->append(StringPrintf(L"0x%X", p));
+ }
+};
+#endif // defined(OS_WIN)
+
+// Various ipc/chromium types.
+
+template <class P>
+struct ParamTraitsIPC : ParamTraitsWindows<P> {};
+
+#if defined(OS_POSIX)
+// FileDescriptors may be serialised over IPC channels on POSIX. On the
+// receiving side, the FileDescriptor is a valid duplicate of the file
+// descriptor which was transmitted: *it is not just a copy of the integer like
+// HANDLEs on Windows*. The only exception is if the file descriptor is < 0. In
+// this case, the receiving end will see a value of -1. *Zero is a valid file
+// descriptor*.
+//
+// The received file descriptor will have the |auto_close| flag set to true. The
+// code which handles the message is responsible for taking ownership of it.
+// File descriptors are OS resources and must be closed when no longer needed.
+//
+// When sending a file descriptor, the file descriptor must be valid at the time
+// of transmission. Since transmission is not synchronous, one should consider
+// dup()ing any file descriptors to be transmitted and setting the |auto_close|
+// flag, which causes the file descriptor to be closed after writing.
+template <>
+struct ParamTraitsIPC<base::FileDescriptor> {
+ typedef base::FileDescriptor param_type;
+ static void Write(Message* m, const param_type& p) {
+ const bool valid = p.fd >= 0;
+ WriteParam(m, valid);
+
+ if (valid) {
+ if (!m->WriteFileDescriptor(p)) {
+ NOTREACHED() << "Too many file descriptors for one message!";
+ }
+ }
+ }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ bool valid;
+ if (!ReadParam(m, iter, &valid)) return false;
+
+ if (!valid) {
+ r->fd = -1;
+ r->auto_close = false;
+ return true;
+ }
+
+ return m->ReadFileDescriptor(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ if (p.auto_close) {
+ l->append(StringPrintf(L"FD(%d auto-close)", p.fd));
+ } else {
+ l->append(StringPrintf(L"FD(%d)", p.fd));
+ }
+ }
+};
+#endif // defined(OS_POSIX)
+
+// Mozilla-specific types.
+
+template <class P>
+struct ParamTraitsMozilla : ParamTraitsIPC<P> {};
+
+template <>
+struct ParamTraitsMozilla<nsresult> {
+ typedef nsresult param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteUInt32(static_cast<uint32_t>(p));
+ }
+ static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
+ return m->ReadUInt32(iter, reinterpret_cast<uint32_t*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%u", static_cast<uint32_t>(p)));
+ }
+};
+
+// See comments for the IPDLParamTraits specializations for RefPtr<T> and
+// nsCOMPtr<T> for more details.
+template <class T>
+struct ParamTraitsMozilla<RefPtr<T>> {
+ static void Write(Message* m, const RefPtr<T>& p) {
+ ParamTraits<T*>::Write(m, p.get());
+ }
+
+ static bool Read(const Message* m, PickleIterator* iter, RefPtr<T>* r) {
+ return ParamTraits<T*>::Read(m, iter, r);
+ }
+};
+
+template <class T>
+struct ParamTraitsMozilla<nsCOMPtr<T>> {
+ static void Write(Message* m, const nsCOMPtr<T>& p) {
+ ParamTraits<T*>::Write(m, p.get());
+ }
+
+ static bool Read(const Message* m, PickleIterator* iter, nsCOMPtr<T>* r) {
+ RefPtr<T> refptr;
+ if (!ParamTraits<T*>::Read(m, iter, &refptr)) {
+ return false;
+ }
+ *r = std::move(refptr);
+ return true;
+ }
+};
+
+// Finally, ParamTraits itself.
+
+template <class P>
+struct ParamTraits : ParamTraitsMozilla<P> {};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_MESSAGE_UTILS_H_
diff --git a/ipc/chromium/src/chrome/common/mach_ipc_mac.h b/ipc/chromium/src/chrome/common/mach_ipc_mac.h
new file mode 100644
index 0000000000..d9a8483600
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/mach_ipc_mac.h
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef BASE_MACH_IPC_MAC_H_
+#define BASE_MACH_IPC_MAC_H_
+
+#include <mach/mach.h>
+#include <mach/message.h>
+#include <servers/bootstrap.h>
+#include <sys/types.h>
+
+#include "base/basictypes.h"
+
+//==============================================================================
+// DISCUSSION:
+//
+// The three main classes of interest are
+//
+// MachMessage: a wrapper for a mach message of the following form
+// mach_msg_header_t
+// mach_msg_body_t
+// optional descriptors
+// optional extra message data
+//
+// MachReceiveMessage and MachSendMessage subclass MachMessage
+// and are used instead of MachMessage which is an abstract base class
+//
+// ReceivePort:
+// Represents a mach port for which we have receive rights
+//
+// MachPortSender:
+// Represents a mach port for which we have send rights
+//
+// Here's an example to receive a message on a server port:
+//
+// // This creates our named server port
+// ReceivePort receivePort("com.Google.MyService");
+//
+// MachReceiveMessage message;
+// kern_return_t result = receivePort.WaitForMessage(&message, 0);
+//
+// if (result == KERN_SUCCESS && message.GetMessageID() == 57) {
+// mach_port_t task = message.GetTranslatedPort(0);
+// mach_port_t thread = message.GetTranslatedPort(1);
+//
+// char *messageString = message.GetData();
+//
+// printf("message string = %s\n", messageString);
+// }
+//
+// Here is an example of using these classes to send a message to this port:
+//
+// // send to already named port
+// MachPortSender sender("com.Google.MyService");
+// MachSendMessage message(57); // our message ID is 57
+//
+// // add some ports to be translated for us
+// message.AddDescriptor(mach_task_self()); // our task
+// message.AddDescriptor(mach_thread_self()); // this thread
+//
+// char messageString[] = "Hello server!\n";
+// message.SetData(messageString, strlen(messageString)+1);
+// // timeout 1000ms
+// kern_return_t result = sender.SendMessage(message, 1000);
+//
+
+#ifndef PRINT_MACH_RESULT
+# define PRINT_MACH_RESULT(result_, message_) \
+ printf(message_ " %s (%d)\n", mach_error_string(result_), result_);
+#endif
+
+//==============================================================================
+// A wrapper class for mach_msg_port_descriptor_t (with same memory layout)
+// with convenient constructors and accessors
+class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
+ public:
+ // General-purpose constructor
+ MachMsgPortDescriptor(mach_port_t in_name,
+ mach_msg_type_name_t in_disposition) {
+ name = in_name;
+ pad1 = 0;
+ pad2 = 0;
+ disposition = in_disposition;
+ type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+
+ // For passing send rights to a port
+ explicit MachMsgPortDescriptor(mach_port_t in_name) {
+ name = in_name;
+ pad1 = 0;
+ pad2 = 0;
+ disposition = MACH_MSG_TYPE_PORT_SEND;
+ type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+
+ // Copy constructor
+ MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) {
+ name = desc.name;
+ pad1 = desc.pad1;
+ pad2 = desc.pad2;
+ disposition = desc.disposition;
+ type = desc.type;
+ }
+
+ mach_port_t GetMachPort() const { return name; }
+
+ mach_msg_type_name_t GetDisposition() const { return disposition; }
+
+ // For convenience
+ operator mach_port_t() const { return GetMachPort(); }
+};
+
+//==============================================================================
+// MachMessage: a wrapper for a mach message
+// (mach_msg_header_t, mach_msg_body_t, extra data)
+//
+// This considerably simplifies the construction of a message for sending
+// and the getting at relevant data and descriptors for the receiver.
+//
+// This class can be initialized using external storage of an arbitrary size
+// or it can manage storage internally.
+// 1. If storage is allocated internally, the combined size of the descriptors
+// plus data must be less than 1024. But as a benefit no memory allocation is
+// necessary.
+// 2. For external storage, a buffer of at least EmptyMessageSize() must be
+// provided.
+//
+// A MachMessage object is used by ReceivePort::WaitForMessage
+// and MachPortSender::SendMessage
+//
+class MachMessage {
+ public:
+ virtual ~MachMessage();
+
+ // The receiver of the message can retrieve the raw data this way
+ u_int8_t* GetData() {
+ return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
+ }
+
+ u_int32_t GetDataLength();
+
+ // The message ID may be used as a code identifying the type of message
+ void SetMessageID(int32_t message_id);
+
+ int32_t GetMessageID();
+
+ // Adds a descriptor (typically a mach port) to be translated
+ // returns true if successful, otherwise not enough space
+ bool AddDescriptor(const MachMsgPortDescriptor& desc);
+
+ int GetDescriptorCount() const {
+ return storage_->body.msgh_descriptor_count;
+ }
+ MachMsgPortDescriptor* GetDescriptor(int n);
+
+ // Convenience method which gets the mach port described by the descriptor
+ mach_port_t GetTranslatedPort(int n);
+
+ // A simple message is one with no descriptors
+ bool IsSimpleMessage() const { return GetDescriptorCount() == 0; }
+
+ // Sets raw data for the message (returns false if not enough space)
+ bool SetData(const void* data, int32_t data_length);
+
+ protected:
+ // Consider this an abstract base class - must create an actual instance
+ // of MachReceiveMessage or MachSendMessage
+ MachMessage();
+
+ // Constructor for use with preallocate storage.
+ // storage_length must be >= EmptyMessageSize()
+ MachMessage(void* storage, size_t storage_length);
+
+ friend class ReceivePort;
+ friend class MachPortSender;
+
+ // Represents raw data in our message
+ struct MessageDataPacket {
+ int32_t id; // little-endian
+ int32_t data_length; // little-endian
+ u_int8_t data[1]; // actual size limited by storage_length_bytes_
+ };
+
+ MessageDataPacket* GetDataPacket();
+
+ void SetDescriptorCount(int n);
+ void SetDescriptor(int n, const MachMsgPortDescriptor& desc);
+
+ // Returns total message size setting msgh_size in the header to this value
+ int CalculateSize();
+
+ // Returns total storage size that this object can grow to, this is inclusive
+ // of the mach header.
+ size_t MaxSize() const { return storage_length_bytes_; }
+
+ protected:
+ mach_msg_header_t* Head() { return &(storage_->head); }
+
+ private:
+ struct MachMessageData {
+ mach_msg_header_t head;
+ mach_msg_body_t body;
+ // descriptors and data may be embedded here.
+ u_int8_t padding[1024];
+ };
+
+ // kEmptyMessageSize needs to have the definition of MachMessageData before
+ // it.
+ public:
+ // The size of an empty message with no data.
+ static const size_t kEmptyMessageSize = sizeof(mach_msg_header_t) +
+ sizeof(mach_msg_body_t) +
+ sizeof(MessageDataPacket);
+
+ private:
+ MachMessageData* storage_;
+ size_t storage_length_bytes_;
+ bool own_storage_; // Is storage owned by this object?
+};
+
+//==============================================================================
+// MachReceiveMessage and MachSendMessage are useful to separate the idea
+// of a mach message being sent and being received, and adds increased type
+// safety:
+// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage
+// MachPortSender::SendMessage() only accepts a MachSendMessage
+
+//==============================================================================
+class MachReceiveMessage : public MachMessage {
+ public:
+ MachReceiveMessage() : MachMessage() {}
+ MachReceiveMessage(void* storage, size_t storage_length)
+ : MachMessage(storage, storage_length) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MachReceiveMessage);
+};
+
+//==============================================================================
+class MachSendMessage : public MachMessage {
+ public:
+ explicit MachSendMessage(int32_t message_id);
+ MachSendMessage(void* storage, size_t storage_length, int32_t message_id);
+
+ private:
+ void Initialize(int32_t message_id);
+
+ DISALLOW_COPY_AND_ASSIGN(MachSendMessage);
+};
+
+//==============================================================================
+// Represents a mach port for which we have receive rights
+class ReceivePort {
+ public:
+ // Creates a new mach port for receiving messages and registers a name for it
+ explicit ReceivePort(const char* receive_port_name);
+
+ // Given an already existing mach port, use it. We take ownership of the
+ // port and deallocate it in our destructor.
+ explicit ReceivePort(mach_port_t receive_port);
+
+ // Create a new mach port for receiving messages
+ ReceivePort();
+
+ ~ReceivePort();
+
+ // Waits on the mach port until message received or timeout
+ kern_return_t WaitForMessage(MachReceiveMessage* out_message,
+ mach_msg_timeout_t timeout);
+
+ kern_return_t SendMessageToSelf(MachSendMessage& msg,
+ mach_msg_timeout_t timeout);
+
+ // The underlying mach port that we wrap
+ mach_port_t GetPort() const { return port_; }
+
+ private:
+ mach_port_t port_;
+ kern_return_t init_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReceivePort);
+};
+
+//==============================================================================
+// Represents a mach port for which we have send rights
+class MachPortSender {
+ public:
+ // get a port with send rights corresponding to a named registered service
+ explicit MachPortSender(const char* receive_port_name);
+
+ // Given an already existing mach port, use it.
+ explicit MachPortSender(mach_port_t send_port);
+
+ kern_return_t SendMessage(MachSendMessage& message,
+ mach_msg_timeout_t timeout);
+
+ private:
+ mach_port_t send_port_;
+ kern_return_t init_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachPortSender);
+};
+
+#endif // BASE_MACH_IPC_MAC_H_
diff --git a/ipc/chromium/src/chrome/common/mach_ipc_mac.mm b/ipc/chromium/src/chrome/common/mach_ipc_mac.mm
new file mode 100644
index 0000000000..f0c5671782
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/mach_ipc_mac.mm
@@ -0,0 +1,303 @@
+// Copyright (c) 2006-2008 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/mach_ipc_mac.h"
+
+#import <Foundation/Foundation.h>
+
+#include <stdio.h>
+#include "base/logging.h"
+
+//==============================================================================
+MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { Initialize(message_id); }
+
+MachSendMessage::MachSendMessage(void* storage, size_t storage_length, int32_t message_id)
+ : MachMessage(storage, storage_length) {
+ Initialize(message_id);
+}
+
+void MachSendMessage::Initialize(int32_t message_id) {
+ Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+
+ // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
+ Head()->msgh_local_port = MACH_PORT_NULL;
+ Head()->msgh_reserved = 0;
+ Head()->msgh_id = 0;
+
+ SetDescriptorCount(0); // start out with no descriptors
+
+ SetMessageID(message_id);
+ SetData(NULL, 0); // client may add data later
+}
+
+//==============================================================================
+MachMessage::MachMessage()
+ : storage_(new MachMessageData), // Allocate storage_ ourselves
+ storage_length_bytes_(sizeof(MachMessageData)),
+ own_storage_(true) {
+ memset(storage_, 0, storage_length_bytes_);
+}
+
+//==============================================================================
+MachMessage::MachMessage(void* storage, size_t storage_length)
+ : storage_(static_cast<MachMessageData*>(storage)),
+ storage_length_bytes_(storage_length),
+ own_storage_(false) {
+ DCHECK(storage);
+ DCHECK(storage_length >= kEmptyMessageSize);
+}
+
+//==============================================================================
+MachMessage::~MachMessage() {
+ if (own_storage_) {
+ delete storage_;
+ storage_ = NULL;
+ }
+}
+
+u_int32_t MachMessage::GetDataLength() { return EndianU32_LtoN(GetDataPacket()->data_length); }
+
+// The message ID may be used as a code identifying the type of message
+void MachMessage::SetMessageID(int32_t message_id) {
+ GetDataPacket()->id = EndianU32_NtoL(message_id);
+}
+
+int32_t MachMessage::GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
+
+//==============================================================================
+// returns true if successful
+bool MachMessage::SetData(const void* data, int32_t data_length) {
+ // Enforce the fact that it's only safe to call this method once on a
+ // message.
+ DCHECK(GetDataPacket()->data_length == 0);
+
+ // first check to make sure we have enough space
+ int size = CalculateSize();
+ int new_size = size + data_length;
+
+ if ((unsigned)new_size > storage_length_bytes_) {
+ return false; // not enough space
+ }
+
+ GetDataPacket()->data_length = EndianU32_NtoL(data_length);
+ if (data) memcpy(GetDataPacket()->data, data, data_length);
+
+ // Update the Mach header with the new aligned size of the message.
+ CalculateSize();
+
+ return true;
+}
+
+//==============================================================================
+// calculates and returns the total size of the message
+// Currently, the entire message MUST fit inside of the MachMessage
+// messsage size <= EmptyMessageSize()
+int MachMessage::CalculateSize() {
+ int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
+
+ // add space for MessageDataPacket
+ int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
+ size += 2 * sizeof(int32_t) + alignedDataLength;
+
+ // add space for descriptors
+ size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
+
+ Head()->msgh_size = size;
+
+ return size;
+}
+
+//==============================================================================
+MachMessage::MessageDataPacket* MachMessage::GetDataPacket() {
+ int desc_size = sizeof(MachMsgPortDescriptor) * GetDescriptorCount();
+ MessageDataPacket* packet = reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size);
+
+ return packet;
+}
+
+//==============================================================================
+void MachMessage::SetDescriptor(int n, const MachMsgPortDescriptor& desc) {
+ MachMsgPortDescriptor* desc_array = reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
+ desc_array[n] = desc;
+}
+
+//==============================================================================
+// returns true if successful otherwise there was not enough space
+bool MachMessage::AddDescriptor(const MachMsgPortDescriptor& desc) {
+ // first check to make sure we have enough space
+ int size = CalculateSize();
+ int new_size = size + sizeof(MachMsgPortDescriptor);
+
+ if ((unsigned)new_size > storage_length_bytes_) {
+ return false; // not enough space
+ }
+
+ // unfortunately, we need to move the data to allow space for the
+ // new descriptor
+ u_int8_t* p = reinterpret_cast<u_int8_t*>(GetDataPacket());
+ bcopy(p, p + sizeof(MachMsgPortDescriptor), GetDataLength() + 2 * sizeof(int32_t));
+
+ SetDescriptor(GetDescriptorCount(), desc);
+ SetDescriptorCount(GetDescriptorCount() + 1);
+
+ CalculateSize();
+
+ return true;
+}
+
+//==============================================================================
+void MachMessage::SetDescriptorCount(int n) {
+ storage_->body.msgh_descriptor_count = n;
+
+ if (n > 0) {
+ Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
+ } else {
+ Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
+ }
+}
+
+//==============================================================================
+MachMsgPortDescriptor* MachMessage::GetDescriptor(int n) {
+ if (n < GetDescriptorCount()) {
+ MachMsgPortDescriptor* desc = reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
+ return desc + n;
+ }
+
+ return nil;
+}
+
+//==============================================================================
+mach_port_t MachMessage::GetTranslatedPort(int n) {
+ if (n < GetDescriptorCount()) {
+ return GetDescriptor(n)->GetMachPort();
+ }
+ return MACH_PORT_NULL;
+}
+
+#pragma mark -
+
+//==============================================================================
+// create a new mach port for receiving messages and register a name for it
+ReceivePort::ReceivePort(const char* receive_port_name) {
+ mach_port_t current_task = mach_task_self();
+
+ init_result_ = mach_port_allocate(current_task, MACH_PORT_RIGHT_RECEIVE, &port_);
+
+ if (init_result_ != KERN_SUCCESS) return;
+
+ init_result_ = mach_port_insert_right(current_task, port_, port_, MACH_MSG_TYPE_MAKE_SEND);
+
+ if (init_result_ != KERN_SUCCESS) return;
+
+ NSPort* ns_port = [NSMachPort portWithMachPort:port_];
+ NSString* port_name = [NSString stringWithUTF8String:receive_port_name];
+ [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
+}
+
+//==============================================================================
+// create a new mach port for receiving messages
+ReceivePort::ReceivePort() {
+ mach_port_t current_task = mach_task_self();
+
+ init_result_ = mach_port_allocate(current_task, MACH_PORT_RIGHT_RECEIVE, &port_);
+
+ if (init_result_ != KERN_SUCCESS) return;
+
+ init_result_ = mach_port_insert_right(current_task, port_, port_, MACH_MSG_TYPE_MAKE_SEND);
+}
+
+//==============================================================================
+// Given an already existing mach port, use it. We take ownership of the
+// port and deallocate it in our destructor.
+ReceivePort::ReceivePort(mach_port_t receive_port)
+ : port_(receive_port), init_result_(KERN_SUCCESS) {}
+
+//==============================================================================
+ReceivePort::~ReceivePort() {
+ if (init_result_ == KERN_SUCCESS) mach_port_deallocate(mach_task_self(), port_);
+}
+
+//==============================================================================
+kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage* out_message,
+ mach_msg_timeout_t timeout) {
+ if (!out_message) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ // return any error condition encountered in constructor
+ if (init_result_ != KERN_SUCCESS) return init_result_;
+
+ out_message->Head()->msgh_bits = 0;
+ out_message->Head()->msgh_local_port = port_;
+ out_message->Head()->msgh_remote_port = MACH_PORT_NULL;
+ out_message->Head()->msgh_reserved = 0;
+ out_message->Head()->msgh_id = 0;
+
+ kern_return_t result = mach_msg(
+ out_message->Head(), MACH_RCV_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_RCV_TIMEOUT),
+ 0, out_message->MaxSize(), port_,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
+
+//==============================================================================
+// send a message to this port
+kern_return_t ReceivePort::SendMessageToSelf(MachSendMessage& message, mach_msg_timeout_t timeout) {
+ if (message.Head()->msgh_size == 0) {
+ NOTREACHED();
+ return KERN_INVALID_VALUE; // just for safety -- never should occur
+ };
+
+ if (init_result_ != KERN_SUCCESS) return init_result_;
+
+ message.Head()->msgh_remote_port = port_;
+ message.Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ kern_return_t result = mach_msg(
+ message.Head(), MACH_SEND_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_SEND_TIMEOUT),
+ message.Head()->msgh_size, 0, MACH_PORT_NULL,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
+
+#pragma mark -
+
+//==============================================================================
+// get a port with send rights corresponding to a named registered service
+MachPortSender::MachPortSender(const char* receive_port_name) {
+ mach_port_t bootstrap_port = 0;
+ init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
+
+ if (init_result_ != KERN_SUCCESS) return;
+
+ init_result_ =
+ bootstrap_look_up(bootstrap_port, const_cast<char*>(receive_port_name), &send_port_);
+}
+
+//==============================================================================
+MachPortSender::MachPortSender(mach_port_t send_port)
+ : send_port_(send_port), init_result_(KERN_SUCCESS) {}
+
+//==============================================================================
+kern_return_t MachPortSender::SendMessage(MachSendMessage& message, mach_msg_timeout_t timeout) {
+ if (message.Head()->msgh_size == 0) {
+ NOTREACHED();
+ return KERN_INVALID_VALUE; // just for safety -- never should occur
+ };
+
+ if (init_result_ != KERN_SUCCESS) return init_result_;
+
+ message.Head()->msgh_remote_port = send_port_;
+
+ kern_return_t result = mach_msg(
+ message.Head(), MACH_SEND_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_SEND_TIMEOUT),
+ message.Head()->msgh_size, 0, MACH_PORT_NULL,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
diff --git a/ipc/chromium/src/chrome/common/mach_message_source_mac.cc b/ipc/chromium/src/chrome/common/mach_message_source_mac.cc
new file mode 100644
index 0000000000..d3be7efdb5
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/mach_message_source_mac.cc
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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/mach_message_source_mac.h"
+
+#include "base/logging.h"
+
+MachMessageSource::MachMessageSource(mach_port_t port,
+ MachPortListener* msg_listener,
+ bool* success) {
+ DCHECK(msg_listener);
+ DCHECK(success);
+ DCHECK(port != MACH_PORT_NULL);
+
+ CFMachPortContext port_context = {0};
+ port_context.info = msg_listener;
+
+ scoped_cftyperef<CFMachPortRef> cf_mach_port_ref(CFMachPortCreateWithPort(
+ kCFAllocatorDefault, port, MachMessageSource::OnReceiveMachMessage,
+ &port_context, NULL));
+
+ if (cf_mach_port_ref.get() == NULL) {
+ CHROMIUM_LOG(WARNING) << "CFMachPortCreate failed";
+ *success = false;
+ return;
+ }
+
+ // Create a RL source.
+ machport_runloop_ref_.reset(CFMachPortCreateRunLoopSource(
+ kCFAllocatorDefault, cf_mach_port_ref.get(), 0));
+
+ if (machport_runloop_ref_.get() == NULL) {
+ CHROMIUM_LOG(WARNING) << "CFMachPortCreateRunLoopSource failed";
+ *success = false;
+ return;
+ }
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), machport_runloop_ref_.get(),
+ kCFRunLoopCommonModes);
+ *success = true;
+}
+
+MachMessageSource::~MachMessageSource() {
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), machport_runloop_ref_.get(),
+ kCFRunLoopCommonModes);
+}
+
+// static
+void MachMessageSource::OnReceiveMachMessage(CFMachPortRef port, void* msg,
+ CFIndex size, void* closure) {
+ MachPortListener* msg_listener = static_cast<MachPortListener*>(closure);
+ size_t msg_size = (size < 0) ? 0 : static_cast<size_t>(size);
+ DCHECK(msg && msg_size > 0); // this should never happen!
+
+ if (msg_listener && msg && msg_size > 0) {
+ msg_listener->OnMachMessageReceived(msg, msg_size);
+ }
+}
diff --git a/ipc/chromium/src/chrome/common/mach_message_source_mac.h b/ipc/chromium/src/chrome/common/mach_message_source_mac.h
new file mode 100644
index 0000000000..92ce0a73d4
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/mach_message_source_mac.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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.
+
+#ifndef CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_
+#define CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_
+
+#include <CoreServices/CoreServices.h>
+
+#include "base/scoped_cftyperef.h"
+
+// Handles registering and cleaning up after a CFRunloopSource for a Mach port.
+// Messages received on the port are piped through to a delegate.
+//
+// Example:
+// class MyListener : public MachMessageSource::MachPortListener {
+// public:
+// void OnMachMessageReceived(void* mach_msg, size_t size) {
+// printf("received message on Mach port\n");
+// }
+// };
+//
+// mach_port_t a_port = ...;
+// MyListener listener;
+// bool success = false;
+// MachMessageSource message_source(port, listener, &success);
+//
+// if (!success) {
+// exit(1); // Couldn't register mach runloop source.
+// }
+//
+// CFRunLoopRun(); // Process messages on runloop...
+class MachMessageSource {
+ public:
+ // Classes that want to listen on a Mach port can implement
+ // OnMachMessageReceived, |mach_msg| is a pointer to the raw message data and
+ // |size| is the buffer size;
+ class MachPortListener {
+ public:
+ virtual void OnMachMessageReceived(void* mach_msg, size_t size) = 0;
+ };
+
+ // |listener| is a week reference passed to CF, it needs to remain in
+ // existence till this object is destroeyd.
+ MachMessageSource(mach_port_t port, MachPortListener* listener,
+ bool* success);
+ ~MachMessageSource();
+
+ private:
+ // Called by CF when a new message arrives on the Mach port.
+ static void OnReceiveMachMessage(CFMachPortRef port, void* msg, CFIndex size,
+ void* closure);
+
+ scoped_cftyperef<CFRunLoopSourceRef> machport_runloop_ref_;
+ DISALLOW_COPY_AND_ASSIGN(MachMessageSource);
+};
+
+#endif // CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_
diff --git a/ipc/chromium/src/chrome/common/process_watcher.h b/ipc/chromium/src/chrome/common/process_watcher.h
new file mode 100644
index 0000000000..f1ccae6310
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/process_watcher.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_PROCESS_WATCHER_H_
+#define CHROME_COMMON_PROCESS_WATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/process_util.h"
+
+class ProcessWatcher {
+ public:
+ // This method ensures that the specified process eventually terminates, and
+ // then it closes the given process handle.
+ //
+ // It assumes that the process has already been signalled to exit, and it
+ // begins by waiting a small amount of time for it to exit. If the process
+ // does not appear to have exited, then this function starts to become
+ // aggressive about ensuring that the process terminates.
+ //
+ // This method does not block the calling thread.
+ //
+ // NOTE: The process handle must have been opened with the PROCESS_TERMINATE
+ // and SYNCHRONIZE permissions.
+ //
+ static void EnsureProcessTerminated(base::ProcessHandle process_handle,
+ bool force = true);
+
+ private:
+ // Do not instantiate this class.
+ ProcessWatcher();
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessWatcher);
+};
+
+#endif // CHROME_COMMON_PROCESS_WATCHER_H_
diff --git a/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc b/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc
new file mode 100644
index 0000000000..c3c7b512b1
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+
+#include "chrome/common/process_watcher.h"
+
+// Maximum amount of time (in milliseconds) to wait for the process to exit.
+// XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc
+static const int kMaxWaitMs = 2000;
+
+namespace {
+
+bool IsProcessDead(pid_t process) {
+ bool exited = false;
+ // don't care if the process crashed, just if it exited
+ base::DidProcessCrash(&exited, process);
+ return exited;
+}
+
+class ChildReaper : public base::MessagePumpLibevent::SignalEvent,
+ public base::MessagePumpLibevent::SignalWatcher {
+ public:
+ explicit ChildReaper(pid_t process) : process_(process) {}
+
+ virtual ~ChildReaper() {
+ // subclasses should have cleaned up |process_| already
+ DCHECK(!process_);
+
+ // StopCatching() is implicit
+ }
+
+ virtual void OnSignal(int sig) override {
+ DCHECK(SIGCHLD == sig);
+ DCHECK(process_);
+
+ // this may be the SIGCHLD for a process other than |process_|
+ if (IsProcessDead(process_)) {
+ process_ = 0;
+ StopCatching();
+ }
+ }
+
+ protected:
+ void WaitForChildExit() {
+ DCHECK(process_);
+ HANDLE_EINTR(waitpid(process_, NULL, 0));
+ }
+
+ pid_t process_;
+
+ private:
+ ChildReaper(const ChildReaper&) = delete;
+
+ const ChildReaper& operator=(const ChildReaper&) = delete;
+};
+
+// Fear the reaper
+class ChildGrimReaper : public ChildReaper, public mozilla::Runnable {
+ public:
+ explicit ChildGrimReaper(pid_t process)
+ : ChildReaper(process), mozilla::Runnable("ChildGrimReaper") {}
+
+ virtual ~ChildGrimReaper() {
+ if (process_) KillProcess();
+ }
+
+ NS_IMETHOD Run() override {
+ // we may have already been signaled by the time this runs
+ if (process_) KillProcess();
+
+ return NS_OK;
+ }
+
+ private:
+ void KillProcess() {
+ DCHECK(process_);
+
+ if (IsProcessDead(process_)) {
+ process_ = 0;
+ return;
+ }
+
+ if (0 == kill(process_, SIGKILL)) {
+ // XXX this will block for whatever amount of time it takes the
+ // XXX OS to tear down the process's resources. might need to
+ // XXX rethink this if it proves expensive
+ WaitForChildExit();
+ } else {
+ CHROMIUM_LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!"
+ << "(" << errno << ").";
+ }
+ process_ = 0;
+ }
+
+ ChildGrimReaper(const ChildGrimReaper&) = delete;
+
+ const ChildGrimReaper& operator=(const ChildGrimReaper&) = delete;
+};
+
+class ChildLaxReaper : public ChildReaper,
+ public MessageLoop::DestructionObserver {
+ public:
+ explicit ChildLaxReaper(pid_t process) : ChildReaper(process) {}
+
+ virtual ~ChildLaxReaper() {
+ // WillDestroyCurrentMessageLoop() should have reaped process_ already
+ DCHECK(!process_);
+ }
+
+ virtual void OnSignal(int sig) override {
+ ChildReaper::OnSignal(sig);
+
+ if (!process_) {
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
+ }
+ }
+
+ virtual void WillDestroyCurrentMessageLoop() override {
+ DCHECK(process_);
+
+ WaitForChildExit();
+ process_ = 0;
+
+ // XXX don't think this is necessary, since destruction can only
+ // be observed once, but can't hurt
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
+ }
+
+ private:
+ ChildLaxReaper(const ChildLaxReaper&) = delete;
+
+ const ChildLaxReaper& operator=(const ChildLaxReaper&) = delete;
+};
+
+} // namespace
+
+/**
+ * Do everything possible to ensure that |process| has been reaped
+ * before this process exits.
+ *
+ * |grim| decides how strict to be with the child's shutdown.
+ *
+ * | child exit timeout | upon parent shutdown:
+ * +--------------------+----------------------------------
+ * force=true | 2 seconds | kill(child, SIGKILL)
+ * force=false | infinite | waitpid(child)
+ *
+ * If a child process doesn't shut down properly, and |grim=false|
+ * used, then the parent will wait on the child forever. So,
+ * |force=false| is expected to be used when an external entity can be
+ * responsible for terminating hung processes, e.g. automated test
+ * harnesses.
+ */
+void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process,
+ bool force) {
+ DCHECK(process != base::GetCurrentProcId());
+ DCHECK(process > 0);
+
+ if (IsProcessDead(process)) return;
+
+ MessageLoopForIO* loop = MessageLoopForIO::current();
+ if (force) {
+ RefPtr<ChildGrimReaper> reaper = new ChildGrimReaper(process);
+
+ loop->CatchSignal(SIGCHLD, reaper, reaper);
+ // |loop| takes ownership of |reaper|
+ loop->PostDelayedTask(reaper.forget(), kMaxWaitMs);
+ } else {
+ ChildLaxReaper* reaper = new ChildLaxReaper(process);
+
+ loop->CatchSignal(SIGCHLD, reaper, reaper);
+ // |reaper| destroys itself after destruction notification
+ loop->AddDestructionObserver(reaper);
+ }
+}
diff --git a/ipc/chromium/src/chrome/common/process_watcher_win.cc b/ipc/chromium/src/chrome/common/process_watcher_win.cc
new file mode 100644
index 0000000000..29b1b39fbd
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/process_watcher_win.cc
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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/process_watcher.h"
+
+#include "base/message_loop.h"
+#include "base/object_watcher.h"
+
+// Maximum amount of time (in milliseconds) to wait for the process to exit.
+static const int kWaitInterval = 2000;
+
+namespace {
+
+class ChildReaper : public mozilla::Runnable,
+ public base::ObjectWatcher::Delegate,
+ public MessageLoop::DestructionObserver {
+ public:
+ explicit ChildReaper(base::ProcessHandle process, bool force)
+ : mozilla::Runnable("ChildReaper"), process_(process), force_(force) {
+ watcher_.StartWatching(process_, this);
+ }
+
+ virtual ~ChildReaper() {
+ if (process_) {
+ KillProcess();
+ DCHECK(!process_) << "Make sure to close the handle.";
+ }
+ }
+
+ // MessageLoop::DestructionObserver -----------------------------------------
+
+ virtual void WillDestroyCurrentMessageLoop() {
+ MOZ_ASSERT(!force_);
+ if (process_) {
+ WaitForSingleObject(process_, INFINITE);
+ base::CloseProcessHandle(process_);
+ process_ = 0;
+
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
+ }
+ }
+
+ // Task ---------------------------------------------------------------------
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(force_);
+ if (process_) {
+ KillProcess();
+ }
+ return NS_OK;
+ }
+
+ // MessageLoop::Watcher -----------------------------------------------------
+
+ virtual void OnObjectSignaled(HANDLE object) {
+ // When we're called from KillProcess, the ObjectWatcher may still be
+ // watching. the process handle, so make sure it has stopped.
+ watcher_.StopWatching();
+
+ base::CloseProcessHandle(process_);
+ process_ = 0;
+
+ if (!force_) {
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
+ }
+ }
+
+ private:
+ void KillProcess() {
+ MOZ_ASSERT(force_);
+
+ // OK, time to get frisky. We don't actually care when the process
+ // terminates. We just care that it eventually terminates, and that's what
+ // TerminateProcess should do for us. Don't check for the result code since
+ // it fails quite often. This should be investigated eventually.
+ TerminateProcess(process_, base::PROCESS_END_PROCESS_WAS_HUNG);
+
+ // Now, just cleanup as if the process exited normally.
+ OnObjectSignaled(process_);
+ }
+
+ // The process that we are watching.
+ base::ProcessHandle process_;
+
+ base::ObjectWatcher watcher_;
+
+ bool force_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ChildReaper);
+};
+
+} // namespace
+
+// static
+void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process,
+ bool force) {
+ DCHECK(process != GetCurrentProcess());
+
+ // If already signaled, then we are done!
+ if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
+ base::CloseProcessHandle(process);
+ return;
+ }
+
+ MessageLoopForIO* loop = MessageLoopForIO::current();
+ if (force) {
+ RefPtr<mozilla::Runnable> task = new ChildReaper(process, force);
+ loop->PostDelayedTask(task.forget(), kWaitInterval);
+ } else {
+ loop->AddDestructionObserver(new ChildReaper(process, force));
+ }
+}