summaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-22 23:57:21 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-22 23:57:21 +0000
commit946d1b2c806795351598aeb9faaed797284a8ee3 (patch)
treed8d2695f73a56ec33ab068f9070fe93cb7c0e4a3 /ipc
parent00fceac62015db950f3dde84f5aeeacb82f1b2c6 (diff)
downloadchromium_src-946d1b2c806795351598aeb9faaed797284a8ee3.zip
chromium_src-946d1b2c806795351598aeb9faaed797284a8ee3.tar.gz
chromium_src-946d1b2c806795351598aeb9faaed797284a8ee3.tar.bz2
Split the IPC code into ipc/
This splits the ipc code from the common project. The 'common' project pulls in all of webkit, the v8 bindings, skia, googleurl, and a number of other projects which makes it very difficult to deal with especially for external projects wanting just to use some of Chromium's infrastructure. This puts the ipc code into its top-level ipc/ directory with a dependency only on base. The common project depends on the new ipc/ipc.gyp:ipc target so that all projects currently pulling common in to get the IPC code still have it available. This mostly follows agl's pre-gyp attempt to do this which was r13062. Known issues: - Currently a number of projects depend on chrome/chrome.gyp:common in order to use the IPC infrastructure. Rather than fixing all of these dependencies I have made common depend on ipc/ipc.gyp:ipc and added "ipc" to the include_rules section of DEPS so that checkdeps.py doesn't complain. Over time projects that need IPC should depend on the IPC project themselves and dependencies on common removed, although I don't think many projects that need IPC will be able to get away without common currently. - ipc/ipc_message_macros.h still has #include "chrome/common/..." inside of a ipc/ should not refer to files in chrome/... now. I'm not sure how to resolve this since it's really an IDE bug - the named pipe name (windows+linux) and the logging event name (all) + env variable (posix) refer explicitly to 'Chrome' which somewhat hurts the illusion of ipc/ being an independent library. I think this should be examined in a subsequent, much smaller patch. - I've eliminated the IPC.SendMsgCount counter since it was implemented in a way to create a dependency from ipc/ to chrome/common/chrome_counters. This is the same approach that r13062 took. http://codereview.chromium.org/155905 (Patch from James Robinson) git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21342 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ipc')
-rw-r--r--ipc/file_descriptor_set_posix.cc119
-rw-r--r--ipc/file_descriptor_set_posix.h108
-rw-r--r--ipc/file_descriptor_set_unittest.cc178
-rw-r--r--ipc/ipc.gyp108
-rw-r--r--ipc/ipc_channel.h118
-rw-r--r--ipc/ipc_channel_handle.h45
-rw-r--r--ipc/ipc_channel_posix.cc837
-rw-r--r--ipc/ipc_channel_posix.h135
-rw-r--r--ipc/ipc_channel_proxy.cc288
-rw-r--r--ipc/ipc_channel_proxy.h209
-rw-r--r--ipc/ipc_channel_win.cc442
-rw-r--r--ipc/ipc_channel_win.h85
-rw-r--r--ipc/ipc_descriptors.h14
-rw-r--r--ipc/ipc_fuzzing_tests.cc430
-rw-r--r--ipc/ipc_logging.cc309
-rw-r--r--ipc/ipc_logging.h112
-rw-r--r--ipc/ipc_message.cc124
-rw-r--r--ipc/ipc_message.h279
-rw-r--r--ipc/ipc_message_macros.h1150
-rw-r--r--ipc/ipc_message_unittest.cc67
-rw-r--r--ipc/ipc_message_utils.cc235
-rw-r--r--ipc/ipc_message_utils.h1221
-rw-r--r--ipc/ipc_send_fds_test.cc187
-rw-r--r--ipc/ipc_switches.cc28
-rw-r--r--ipc/ipc_switches.h20
-rw-r--r--ipc/ipc_sync_channel.cc453
-rw-r--r--ipc/ipc_sync_channel.h162
-rw-r--r--ipc/ipc_sync_channel_unittest.cc1009
-rw-r--r--ipc/ipc_sync_message.cc126
-rw-r--r--ipc/ipc_sync_message.h96
-rw-r--r--ipc/ipc_sync_message_unittest.cc249
-rw-r--r--ipc/ipc_sync_message_unittest.h98
-rw-r--r--ipc/ipc_tests.cc476
-rw-r--r--ipc/ipc_tests.h47
34 files changed, 9564 insertions, 0 deletions
diff --git a/ipc/file_descriptor_set_posix.cc b/ipc/file_descriptor_set_posix.cc
new file mode 100644
index 0000000..519e6e7
--- /dev/null
+++ b/ipc/file_descriptor_set_posix.cc
@@ -0,0 +1,119 @@
+// 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 "ipc/file_descriptor_set_posix.h"
+
+#include "base/eintr_wrapper.h"
+#include "base/logging.h"
+
+FileDescriptorSet::FileDescriptorSet()
+ : consumed_descriptor_highwater_(0) {
+}
+
+FileDescriptorSet::~FileDescriptorSet() {
+ if (consumed_descriptor_highwater_ == descriptors_.size())
+ return;
+
+ 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)
+ HANDLE_EINTR(close(descriptors_[i].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)
+ HANDLE_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/file_descriptor_set_posix.h b/ipc/file_descriptor_set_posix.h
new file mode 100644
index 0000000..c3d26ba8
--- /dev/null
+++ b/ipc/file_descriptor_set_posix.h
@@ -0,0 +1,108 @@
+// 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 IPC_FILE_DESCRIPTOR_SET_POSIX_H_
+#define IPC_FILE_DESCRIPTOR_SET_POSIX_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_descriptor_posix.h"
+#include "base/ref_counted.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 base::RefCountedThreadSafe<FileDescriptorSet> {
+ public:
+ FileDescriptorSet();
+ ~FileDescriptorSet();
+
+ // This is the maximum number of descriptors per message. We need to know this
+ // because the control message kernel interface has to be given a buffer which
+ // is large enough to store all the descriptor numbers. Otherwise the kernel
+ // tells us that it truncated the control data and the extra descriptors are
+ // lost.
+ //
+ // In debugging mode, it's a fatal error to try and add more than this number
+ // of descriptors to a FileDescriptorSet.
+ enum {
+ MAX_DESCRIPTORS_PER_MESSAGE = 4,
+ };
+
+ // ---------------------------------------------------------------------------
+ // 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:
+ // 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 // IPC_FILE_DESCRIPTOR_SET_POSIX_H_
diff --git a/ipc/file_descriptor_set_unittest.cc b/ipc/file_descriptor_set_unittest.cc
new file mode 100644
index 0000000..243d29e
--- /dev/null
+++ b/ipc/file_descriptor_set_unittest.cc
@@ -0,0 +1,178 @@
+// 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.
+
+// This test is POSIX only.
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "base/basictypes.h"
+#include "base/eintr_wrapper.h"
+#include "ipc/file_descriptor_set_posix.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Get a safe file descriptor for test purposes.
+int GetSafeFd() {
+ return open("/dev/null", O_RDONLY);
+}
+
+// Returns true if fd was already closed. Closes fd if not closed.
+bool VerifyClosed(int fd) {
+ const int duped = dup(fd);
+ if (duped != -1) {
+ HANDLE_EINTR(close(duped));
+ HANDLE_EINTR(close(fd));
+ return false;
+ }
+ return true;
+}
+
+// The FileDescriptorSet will try and close some of the descriptor numbers
+// which we given it. This is the base descriptor value. It's great enough such
+// that no real descriptor will accidently be closed.
+static const int kFDBase = 50000;
+
+TEST(FileDescriptorSet, BasicAdd) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ ASSERT_EQ(set->size(), 0u);
+ ASSERT_TRUE(set->empty());
+ ASSERT_TRUE(set->Add(kFDBase));
+ ASSERT_EQ(set->size(), 1u);
+ ASSERT_TRUE(!set->empty());
+
+ // Empties the set and stops a warning about deleting a set with unconsumed
+ // descriptors
+ set->CommitAll();
+}
+
+TEST(FileDescriptorSet, BasicAddAndClose) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ ASSERT_EQ(set->size(), 0u);
+ ASSERT_TRUE(set->empty());
+ const int fd = GetSafeFd();
+ ASSERT_TRUE(set->AddAndAutoClose(fd));
+ ASSERT_EQ(set->size(), 1u);
+ ASSERT_TRUE(!set->empty());
+
+ set->CommitAll();
+
+ ASSERT_TRUE(VerifyClosed(fd));
+}
+TEST(FileDescriptorSet, MaxSize) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ for (unsigned i = 0;
+ i < FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE; ++i) {
+ ASSERT_TRUE(set->Add(kFDBase + 1 + i));
+ }
+
+ ASSERT_TRUE(!set->Add(kFDBase));
+
+ set->CommitAll();
+}
+
+TEST(FileDescriptorSet, SetDescriptors) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ ASSERT_TRUE(set->empty());
+ set->SetDescriptors(NULL, 0);
+ ASSERT_TRUE(set->empty());
+
+ const int fd = GetSafeFd();
+ static const int fds[] = {fd};
+ set->SetDescriptors(fds, 1);
+ ASSERT_TRUE(!set->empty());
+ ASSERT_EQ(set->size(), 1u);
+
+ set->CommitAll();
+
+ ASSERT_TRUE(VerifyClosed(fd));
+}
+
+TEST(FileDescriptorSet, GetDescriptors) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ set->GetDescriptors(NULL);
+ ASSERT_TRUE(set->Add(kFDBase));
+
+ int fds[1];
+ fds[0] = 0;
+ set->GetDescriptors(fds);
+ ASSERT_EQ(fds[0], kFDBase);
+ set->CommitAll();
+ ASSERT_TRUE(set->empty());
+}
+
+TEST(FileDescriptorSet, WalkInOrder) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ ASSERT_TRUE(set->Add(kFDBase));
+ ASSERT_TRUE(set->Add(kFDBase + 1));
+ ASSERT_TRUE(set->Add(kFDBase + 2));
+
+ ASSERT_EQ(set->GetDescriptorAt(0), kFDBase);
+ ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1);
+ ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2);
+
+ set->CommitAll();
+}
+
+TEST(FileDescriptorSet, WalkWrongOrder) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ ASSERT_TRUE(set->Add(kFDBase));
+ ASSERT_TRUE(set->Add(kFDBase + 1));
+ ASSERT_TRUE(set->Add(kFDBase + 2));
+
+ ASSERT_EQ(set->GetDescriptorAt(0), kFDBase);
+ ASSERT_EQ(set->GetDescriptorAt(2), -1);
+
+ set->CommitAll();
+}
+
+TEST(FileDescriptorSet, WalkCycle) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ ASSERT_TRUE(set->Add(kFDBase));
+ ASSERT_TRUE(set->Add(kFDBase + 1));
+ ASSERT_TRUE(set->Add(kFDBase + 2));
+
+ ASSERT_EQ(set->GetDescriptorAt(0), kFDBase);
+ ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1);
+ ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2);
+ ASSERT_EQ(set->GetDescriptorAt(0), kFDBase);
+ ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1);
+ ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2);
+ ASSERT_EQ(set->GetDescriptorAt(0), kFDBase);
+ ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1);
+ ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2);
+
+ set->CommitAll();
+}
+
+TEST(FileDescriptorSet, DontClose) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ const int fd = GetSafeFd();
+ ASSERT_TRUE(set->Add(fd));
+ set->CommitAll();
+
+ ASSERT_FALSE(VerifyClosed(fd));
+}
+
+TEST(FileDescriptorSet, DoClose) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ const int fd = GetSafeFd();
+ ASSERT_TRUE(set->AddAndAutoClose(fd));
+ set->CommitAll();
+
+ ASSERT_TRUE(VerifyClosed(fd));
+}
+
+} // namespace
diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp
new file mode 100644
index 0000000..2371ece
--- /dev/null
+++ b/ipc/ipc.gyp
@@ -0,0 +1,108 @@
+# 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.
+
+{
+ 'includes': [
+ '../build/common.gypi',
+ ],
+ 'target_defaults': {
+ 'sources/': [
+ ['exclude', '/win/'],
+ ['exclude', '_(posix|win)(_unittest)?\\.(cc|mm?)$'],
+ ['exclude', '/win_[^/]*\\.cc$'],
+ ],
+ 'conditions': [
+ ['OS=="linux"', {'sources/': [
+ ['include', '_posix(_unittest)?\\.cc$'],
+ ]}],
+ ['OS=="mac"', {'sources/': [
+ ['include', '_posix(_unittest)?\\.(cc|mm?)$'],
+ ]}],
+ ['OS=="win"', {'sources/': [
+ ['include', '_win(_unittest)?\\.cc$'],
+ ['include', '/win/'],
+ ['include', '/win_[^/]*\\.cc$'],
+ ]}],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'ipc',
+ 'type': '<(library)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'file_descriptor_set_posix.cc',
+ 'file_descriptor_set_posix.h',
+ 'ipc_channel.h',
+ 'ipc_channel_handle.h',
+ 'ipc_channel_posix.cc',
+ 'ipc_channel_posix.h',
+ 'ipc_channel_proxy.cc',
+ 'ipc_channel_proxy.h',
+ 'ipc_channel_win.cc',
+ 'ipc_channel_win.h',
+ 'ipc_descriptors.h',
+ 'ipc_logging.cc',
+ 'ipc_logging.h',
+ 'ipc_message.cc',
+ 'ipc_message.h',
+ 'ipc_message_macros.h',
+ 'ipc_message_utils.cc',
+ 'ipc_message_utils.h',
+ 'ipc_switches.cc',
+ 'ipc_switches.h',
+ 'ipc_sync_channel.cc',
+ 'ipc_sync_channel.h',
+ 'ipc_sync_message.cc',
+ 'ipc_sync_message.h',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ },
+ {
+ 'target_name': 'ipc_tests',
+ 'type': 'executable',
+ 'msvs_guid': 'B92AE829-E1CD-4781-824A-DCB1603A1672',
+ 'dependencies': [
+ 'ipc',
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '..'
+ ],
+ 'sources': [
+ 'file_descriptor_set_unittest.cc',
+ 'ipc_fuzzing_tests.cc',
+ 'ipc_message_unittest.cc',
+ 'ipc_send_fds_test.cc',
+ 'ipc_sync_channel_unittest.cc',
+ 'ipc_sync_message_unittest.cc',
+ 'ipc_sync_message_unittest.h',
+ 'ipc_tests.cc',
+ 'ipc_tests.h',
+ ],
+ 'conditions': [
+ ['OS=="linux"', {
+ 'dependencies': [
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ['OS=="linux" and toolkit_views==1', {
+ 'dependencies': [
+ '../views/views.gyp:views',
+ ],
+ }],
+ ],
+ },
+ ]
+}
diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h
new file mode 100644
index 0000000..a7a9a34
--- /dev/null
+++ b/ipc/ipc_channel.h
@@ -0,0 +1,118 @@
+// 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 IPC_IPC_CHANNEL_H_
+#define IPC_IPC_CHANNEL_H_
+
+#include "ipc/ipc_message.h"
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+class Channel : public Message::Sender {
+ // Security tests need access to the pipe handle.
+ friend class ChannelTest;
+
+ public:
+ // Implemented by consumers of a Channel to receive messages.
+ class Listener {
+ public:
+ virtual ~Listener() {}
+
+ // Called when a message is received.
+ virtual void OnMessageReceived(const Message& message) = 0;
+
+ // Called when the channel is connected and we have received the internal
+ // Hello message from the peer.
+ virtual void OnChannelConnected(int32 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() {}
+ };
+
+ 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
+ };
+
+ // 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 std::string& channel_id, Mode mode, Listener* listener);
+
+ ~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.
+ void 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.
+ //
+ // FIXME bug 551500: the channel does not notice failures, so if the
+ // renderer crashes, it will silently succeed, leaking the parameter.
+ // At least the leak will be fixed by...
+ //
+ virtual bool Send(Message* message);
+
+#if defined(OS_POSIX)
+ // On POSIX an IPC::Channel wraps a socketpair(), this method returns the
+ // FD # for the client end of the socket.
+ // 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.
+ int GetClientFileDescriptor() const;
+#endif // defined(OS_POSIX)
+
+ private:
+ // PIMPL to which all channel calls are delegated.
+ class ChannelImpl;
+ ChannelImpl *channel_impl_;
+
+ // 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).
+ enum {
+ HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type (uint16),
+ // to avoid conflicting with normal
+ // message types, which are enumeration
+ // constants starting from 0.
+ };
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_CHANNEL_H_
diff --git a/ipc/ipc_channel_handle.h b/ipc/ipc_channel_handle.h
new file mode 100644
index 0000000..e3b2f3e
--- /dev/null
+++ b/ipc/ipc_channel_handle.h
@@ -0,0 +1,45 @@
+// 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 IPC_IPC_CHANNEL_HANDLE_H_
+#define IPC_IPC_CHANNEL_HANDLE_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
+// On Windows, any process can create an IPC channel and others can fetch
+// it by name. We pass around the channel names over IPC.
+// On POSIX, we instead pass around handles to channel endpoints via IPC.
+// When it's time to IPC a new channel endpoint around, we send both the
+// channel name as well as a base::FileDescriptor, which is itself a special
+// type that knows how to copy a socket endpoint over IPC.
+//
+// In sum, when passing a handle to a channel over IPC, use this data structure
+// to work on both Windows and POSIX.
+
+namespace IPC {
+
+struct ChannelHandle {
+ // Note that serialization for this object is defined in the ParamTraits
+ // template specialization in ipc_message_utils.h.
+ std::string name;
+#if defined(OS_POSIX)
+ base::FileDescriptor socket;
+#endif
+
+ ChannelHandle() {}
+#if defined(OS_POSIX)
+ ChannelHandle(const std::string& n, const base::FileDescriptor& s)
+ : name(n), socket(s) {}
+#else
+ ChannelHandle(const std::string& n) : name(n) {}
+#endif
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_CHANNEL_HANDLE_H_
diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc
new file mode 100644
index 0000000..22b9241
--- /dev/null
+++ b/ipc/ipc_channel_posix.cc
@@ -0,0 +1,837 @@
+// 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 "ipc/ipc_channel_posix.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <string>
+#include <map>
+
+#include "base/command_line.h"
+#include "base/eintr_wrapper.h"
+#include "base/global_descriptors_posix.h"
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/singleton.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "ipc/ipc_descriptors.h"
+#include "ipc/ipc_switches.h"
+#include "ipc/file_descriptor_set_posix.h"
+#include "ipc/ipc_logging.h"
+#include "ipc/ipc_message_utils.h"
+
+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 kPrimaryIPCChannel in the child. Future
+// connections (file descriptors) can then be passed via that
+// connection via sendmsg().
+
+//------------------------------------------------------------------------------
+namespace {
+
+// The PipeMap class works around this quirk related to unit tests:
+//
+// When running as a server, we install the client socket in a
+// specific file descriptor number (@kPrimaryIPCChannel). However, we
+// also have to support the case where we are running unittests in the
+// same process. (We do not support forking without execing.)
+//
+// Case 1: normal running
+// The IPC server object will install a mapping in PipeMap from the
+// name which it was given to the client pipe. When forking the client, the
+// GetClientFileDescriptorMapping will ensure that the socket is installed in
+// the magic slot (@kPrimaryIPCChannel). The client will search for the
+// mapping, but it won't find any since we are in a new process. Thus the
+// magic fd number is returned. Once the client connects, the server will
+// close its copy of the client socket and remove the mapping.
+//
+// Case 2: unittests - client and server in the same process
+// The IPC server will install a mapping as before. The client will search
+// for a mapping and find out. It duplicates the file descriptor and
+// connects. Once the client connects, the server will close the original
+// copy of the client socket and remove the mapping. Thus, when the client
+// object closes, it will close the only remaining copy of the client socket
+// in the fd table and the server will see EOF on its side.
+//
+// TODO(port): a client process cannot connect to multiple IPC channels with
+// this scheme.
+
+class PipeMap {
+ public:
+ // Lookup a given channel id. Return -1 if not found.
+ int Lookup(const std::string& channel_id) {
+ AutoLock locked(lock_);
+
+ ChannelToFDMap::const_iterator i = map_.find(channel_id);
+ if (i == map_.end())
+ return -1;
+ return i->second;
+ }
+
+ // Remove the mapping for the given channel id. No error is signaled if the
+ // channel_id doesn't exist
+ void RemoveAndClose(const std::string& channel_id) {
+ AutoLock locked(lock_);
+
+ ChannelToFDMap::iterator i = map_.find(channel_id);
+ if (i != map_.end()) {
+ HANDLE_EINTR(close(i->second));
+ map_.erase(i);
+ }
+ }
+
+ // Insert a mapping from @channel_id to @fd. It's a fatal error to insert a
+ // mapping if one already exists for the given channel_id
+ void Insert(const std::string& channel_id, int fd) {
+ AutoLock locked(lock_);
+ DCHECK(fd != -1);
+
+ ChannelToFDMap::const_iterator i = map_.find(channel_id);
+ CHECK(i == map_.end()) << "Creating second IPC server (fd " << fd << ") "
+ << "for '" << channel_id << "' while first "
+ << "(fd " << i->second << ") still exists";
+ map_[channel_id] = fd;
+ }
+
+ private:
+ Lock lock_;
+ typedef std::map<std::string, int> ChannelToFDMap;
+ ChannelToFDMap map_;
+};
+
+// Used to map a channel name to the equivalent FD # in the current process.
+// Returns -1 if the channel is unknown.
+int ChannelNameToFD(const std::string& channel_id) {
+ // See the large block comment above PipeMap for the reasoning here.
+ const int fd = Singleton<PipeMap>()->Lookup(channel_id);
+
+ if (fd != -1) {
+ int dup_fd = dup(fd);
+ if (dup_fd < 0)
+ LOG(FATAL) << "dup(" << fd << "): " << strerror(errno);
+ return dup_fd;
+ }
+
+ return fd;
+}
+
+//------------------------------------------------------------------------------
+sockaddr_un sizecheck;
+const size_t kMaxPipeNameLength = sizeof(sizecheck.sun_path);
+
+// Creates a Fifo with the specified name ready to listen on.
+bool CreateServerFifo(const std::string& pipe_name, int* server_listen_fd) {
+ DCHECK(server_listen_fd);
+ DCHECK_GT(pipe_name.length(), 0u);
+ DCHECK_LT(pipe_name.length(), kMaxPipeNameLength);
+
+ if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) {
+ return false;
+ }
+
+ // Create socket.
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ return false;
+ }
+
+ // Make socket non-blocking
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ HANDLE_EINTR(close(fd));
+ return false;
+ }
+
+ // Delete any old FS instances.
+ unlink(pipe_name.c_str());
+
+ // Create unix_addr structure
+ struct sockaddr_un unix_addr;
+ memset(&unix_addr, 0, sizeof(unix_addr));
+ unix_addr.sun_family = AF_UNIX;
+ snprintf(unix_addr.sun_path, kMaxPipeNameLength, "%s", pipe_name.c_str());
+ size_t unix_addr_len = offsetof(struct sockaddr_un, sun_path) +
+ strlen(unix_addr.sun_path) + 1;
+
+ // Bind the socket.
+ if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr),
+ unix_addr_len) != 0) {
+ HANDLE_EINTR(close(fd));
+ return false;
+ }
+
+ // Start listening on the socket.
+ const int listen_queue_length = 1;
+ if (listen(fd, listen_queue_length) != 0) {
+ HANDLE_EINTR(close(fd));
+ return false;
+ }
+
+ *server_listen_fd = fd;
+ return true;
+}
+
+// Accept a connection on a fifo.
+bool ServerAcceptFifoConnection(int server_listen_fd, int* server_socket) {
+ DCHECK(server_socket);
+
+ int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0));
+ if (accept_fd < 0)
+ return false;
+ if (fcntl(accept_fd, F_SETFL, O_NONBLOCK) == -1) {
+ HANDLE_EINTR(close(accept_fd));
+ return false;
+ }
+
+ *server_socket = accept_fd;
+ return true;
+}
+
+bool ClientConnectToFifo(const std::string &pipe_name, int* client_socket) {
+ DCHECK(client_socket);
+ DCHECK_LT(pipe_name.length(), kMaxPipeNameLength);
+
+ // Create socket.
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ LOG(ERROR) << "fd is invalid";
+ return false;
+ }
+
+ // Make socket non-blocking
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ LOG(ERROR) << "fcntl failed";
+ HANDLE_EINTR(close(fd));
+ return false;
+ }
+
+ // Create server side of socket.
+ struct sockaddr_un server_unix_addr;
+ memset(&server_unix_addr, 0, sizeof(server_unix_addr));
+ server_unix_addr.sun_family = AF_UNIX;
+ snprintf(server_unix_addr.sun_path, kMaxPipeNameLength, "%s",
+ pipe_name.c_str());
+ size_t server_unix_addr_len = offsetof(struct sockaddr_un, sun_path) +
+ strlen(server_unix_addr.sun_path) + 1;
+
+ if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&server_unix_addr),
+ server_unix_addr_len)) != 0) {
+ HANDLE_EINTR(close(fd));
+ return false;
+ }
+
+ *client_socket = fd;
+ return true;
+}
+
+} // namespace
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::ChannelImpl(const std::string& channel_id, Mode mode,
+ Listener* listener)
+ : mode_(mode),
+ is_blocked_on_write_(false),
+ message_send_bytes_written_(0),
+ uses_fifo_(CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kIPCUseFIFO)),
+ server_listen_pipe_(-1),
+ pipe_(-1),
+ client_pipe_(-1),
+ listener_(listener),
+ waiting_connect_(true),
+ processing_incoming_(false),
+ factory_(this) {
+ if (!CreatePipe(channel_id, mode)) {
+ // The pipe may have been closed already.
+ LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
+ "\" in " << (mode == MODE_SERVER ? "server" : "client") <<
+ " mode error(" << strerror(errno) << ").";
+ }
+}
+
+// static
+void AddChannelSocket(const std::string& name, int socket) {
+ Singleton<PipeMap>()->Insert(name, socket);
+}
+
+// static
+void RemoveAndCloseChannelSocket(const std::string& name) {
+ Singleton<PipeMap>()->RemoveAndClose(name);
+}
+
+// static
+bool SocketPair(int* fd1, int* fd2) {
+ int pipe_fds[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) {
+ LOG(ERROR) << "socketpair(): " << strerror(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) {
+ LOG(ERROR) << "fcntl(O_NONBLOCK): " << strerror(errno);
+ HANDLE_EINTR(close(pipe_fds[0]));
+ HANDLE_EINTR(close(pipe_fds[1]));
+ return false;
+ }
+
+ *fd1 = pipe_fds[0];
+ *fd2 = pipe_fds[1];
+
+ return true;
+}
+
+bool Channel::ChannelImpl::CreatePipe(const std::string& channel_id,
+ Mode mode) {
+ DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
+
+ if (uses_fifo_) {
+ // This only happens in unit tests; see the comment above PipeMap.
+ // TODO(playmobil): We shouldn't need to create fifos on disk.
+ // TODO(playmobil): If we do, they should be in the user data directory.
+ // TODO(playmobil): Cleanup any stale fifos.
+ pipe_name_ = "/var/tmp/chrome_" + channel_id;
+ if (mode == MODE_SERVER) {
+ if (!CreateServerFifo(pipe_name_, &server_listen_pipe_)) {
+ return false;
+ }
+ } else {
+ if (!ClientConnectToFifo(pipe_name_, &pipe_)) {
+ return false;
+ }
+ waiting_connect_ = false;
+ }
+ } else {
+ // This is the normal (non-unit-test) case, where we're using sockets.
+ // Three possible cases:
+ // 1) It's for a channel we already have a pipe for; reuse it.
+ // 2) It's the initial IPC channel:
+ // 2a) Server side: create the pipe.
+ // 2b) Client side: Pull the pipe out of the GlobalDescriptors set.
+ pipe_name_ = channel_id;
+ pipe_ = ChannelNameToFD(pipe_name_);
+ if (pipe_ < 0) {
+ // Initial IPC channel.
+ if (mode == MODE_SERVER) {
+ if (!SocketPair(&pipe_, &client_pipe_))
+ return false;
+ AddChannelSocket(pipe_name_, client_pipe_);
+ } else {
+ pipe_ = Singleton<base::GlobalDescriptors>()->Get(kPrimaryIPCChannel);
+ }
+ } else {
+ waiting_connect_ = false;
+ }
+ }
+
+ // Create the Hello message to be sent when Connect is called
+ scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
+ HELLO_MESSAGE_TYPE,
+ IPC::Message::PRIORITY_NORMAL));
+ if (!msg->WriteInt(base::GetCurrentProcId())) {
+ Close();
+ return false;
+ }
+
+ output_queue_.push(msg.release());
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+ if (mode_ == MODE_SERVER && uses_fifo_) {
+ if (server_listen_pipe_ == -1) {
+ return false;
+ }
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ server_listen_pipe_,
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &server_listen_connection_watcher_,
+ this);
+ } else {
+ 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() {
+ ssize_t bytes_read = 0;
+
+ struct msghdr msg = {0};
+ struct iovec iov = {input_buf_, Channel::kReadBufferSize};
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = input_cmsg_buf_;
+
+ for (;;) {
+ msg.msg_controllen = sizeof(input_cmsg_buf_);
+
+ if (bytes_read == 0) {
+ if (pipe_ == -1)
+ return false;
+
+ // Read from pipe.
+ // recvmsg() returns 0 if the connection has closed or EAGAIN if no data
+ // is waiting on the pipe.
+ bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
+
+ if (bytes_read < 0) {
+ if (errno == EAGAIN) {
+ return true;
+#if defined(OS_MACOSX)
+ } else if (errno == EPERM) {
+ // On OSX, reading from a pipe with no listener returns EPERM
+ // treat this as a special case to prevent spurious error messages
+ // to the console.
+ return false;
+#endif // defined(OS_MACOSX)
+ } else {
+ LOG(ERROR) << "pipe error (" << pipe_ << "): " << strerror(errno);
+ return false;
+ }
+ } else if (bytes_read == 0) {
+ // The pipe has closed...
+ Close();
+ return false;
+ }
+ }
+ DCHECK(bytes_read);
+
+ if (client_pipe_ != -1) {
+ Singleton<PipeMap>()->RemoveAndClose(pipe_name_);
+ client_pipe_ = -1;
+ }
+
+ // 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) {
+ LOG(ERROR) << "SCM_RIGHTS message was truncated"
+ << " cmsg_len:" << cmsg->cmsg_len
+ << " fd:" << pipe_;
+ for (unsigned i = 0; i < num_wire_fds; ++i)
+ HANDLE_EINTR(close(wire_fds[i]));
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ // Process messages from input buffer.
+ const char *p;
+ const char *end;
+ if (input_overflow_buf_.empty()) {
+ p = input_buf_;
+ end = p + bytes_read;
+ } else {
+ if (input_overflow_buf_.size() >
+ static_cast<size_t>(kMaximumMessageSize - bytes_read)) {
+ input_overflow_buf_.clear();
+ LOG(ERROR) << "IPC message is too big";
+ return false;
+ }
+ input_overflow_buf_.append(input_buf_, bytes_read);
+ p = input_overflow_buf_.data();
+ end = p + input_overflow_buf_.size();
+ }
+
+ // 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 {
+ 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();
+ }
+
+ while (p < end) {
+ const char* message_tail = Message::FindNext(p, end);
+ if (message_tail) {
+ int len = static_cast<int>(message_tail - p);
+ Message m(p, len);
+ 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) {
+ 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)
+ HANDLE_EINTR(close(fds[i]));
+ input_overflow_fds_.clear();
+ // abort the connection
+ return false;
+ }
+
+ m.file_descriptor_set()->SetDescriptors(
+ &fds[fds_i], m.header()->num_fds);
+ fds_i += m.header()->num_fds;
+ }
+#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.
+ listener_->OnChannelConnected(MessageIterator(m).NextInt());
+ } else {
+ listener_->OnMessageReceived(m);
+ }
+ p = message_tail;
+ } else {
+ // Last message is partial.
+ break;
+ }
+ }
+ input_overflow_buf_.assign(p, end - p);
+ 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 (input_overflow_buf_.empty() && !input_overflow_fds_.empty()) {
+ // We close these descriptors in Close()
+ return false;
+ }
+
+ bytes_read = 0; // Get more data.
+ }
+
+ 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_.empty())
+ 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_.empty()) {
+ Message* msg = output_queue_.front();
+
+ size_t amt_to_write = msg->size() - message_send_bytes_written_;
+ DCHECK(amt_to_write != 0);
+ const char *out_bytes = reinterpret_cast<const char*>(msg->data()) +
+ message_send_bytes_written_;
+
+ struct msghdr msgh = {0};
+ struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write};
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ char buf[CMSG_SPACE(
+ sizeof(int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]))];
+
+ if (message_send_bytes_written_ == 0 &&
+ !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();
+
+ DCHECK_LE(num_fds, FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE);
+
+ 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;
+ }
+
+ ssize_t bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT));
+ if (bytes_written > 0)
+ msg->file_descriptor_set()->CommitAll();
+
+ if (bytes_written < 0 && errno != EAGAIN) {
+#if defined(OS_MACOSX)
+ // On OSX writing to a pipe with no listener returns EPERM.
+ if (errno == EPERM) {
+ Close();
+ return false;
+ }
+#endif // OS_MACOSX
+ LOG(ERROR) << "pipe error on " << pipe_ << ": " << strerror(errno);
+ return false;
+ }
+
+ if (static_cast<size_t>(bytes_written) != amt_to_write) {
+ if (bytes_written > 0) {
+ // If write() fails with EAGAIN then bytes_written will be -1.
+ message_send_bytes_written_ += bytes_written;
+ }
+
+ // 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 {
+ message_send_bytes_written_ = 0;
+
+ // Message sent OK!
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent message @" << msg << " on channel @" << this <<
+ " with type " << msg->type();
+#endif
+ output_queue_.pop();
+ delete msg;
+ }
+ }
+ return true;
+}
+
+bool Channel::ChannelImpl::Send(Message* message) {
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sending message @" << message << " on channel @" << this
+ << " with type " << message->type()
+ << " (" << output_queue_.size() << " in queue)";
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::current()->OnSendMessage(message, "");
+#endif
+
+ output_queue_.push(message);
+ if (!waiting_connect_) {
+ if (!is_blocked_on_write_) {
+ if (!ProcessOutgoingMessages())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int Channel::ChannelImpl::GetClientFileDescriptor() const {
+ return client_pipe_;
+}
+
+// Called by libevent when we can read from th pipe without blocking.
+void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
+ bool send_server_hello_msg = false;
+ if (waiting_connect_ && mode_ == MODE_SERVER) {
+ // In the case of a socketpair() the server starts listening on its end
+ // of the pipe in Connect().
+ DCHECK(uses_fifo_);
+
+ if (!ServerAcceptFifoConnection(server_listen_pipe_, &pipe_)) {
+ Close();
+ }
+
+ // No need to watch the listening socket any longer since only one client
+ // can connect. So unregister with libevent.
+ server_listen_connection_watcher_.StopWatchingFileDescriptor();
+
+ // Start watching our end of the socket.
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_,
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &read_watcher_,
+ this);
+
+ waiting_connect_ = false;
+ send_server_hello_msg = true;
+ }
+
+ if (!waiting_connect_ && fd == pipe_) {
+ if (!ProcessIncomingMessages()) {
+ Close();
+ listener_->OnChannelError();
+ }
+ }
+
+ // If we're a server and handshaking, then we want to make sure that we
+ // only send our handshake message after we've processed the client's.
+ // This gives us a chance to kill the client if the incoming handshake
+ // is invalid.
+ if (send_server_hello_msg) {
+ // This should be our first write so there's no chance we can block here...
+ DCHECK(is_blocked_on_write_ == false);
+ ProcessOutgoingMessages();
+ }
+}
+
+// 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 time, 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) {
+ HANDLE_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) {
+ HANDLE_EINTR(close(pipe_));
+ pipe_ = -1;
+ }
+ if (client_pipe_ != -1) {
+ Singleton<PipeMap>()->RemoveAndClose(pipe_name_);
+ client_pipe_ = -1;
+ }
+
+ if (uses_fifo_) {
+ // Unlink the FIFO
+ unlink(pipe_name_.c_str());
+ }
+
+ while (!output_queue_.empty()) {
+ Message* m = output_queue_.front();
+ output_queue_.pop();
+ delete m;
+ }
+
+ // Close any outstanding, received file descriptors
+ for (std::vector<int>::iterator
+ i = input_overflow_fds_.begin(); i != input_overflow_fds_.end(); ++i) {
+ HANDLE_EINTR(close(*i));
+ }
+ input_overflow_fds_.clear();
+}
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const std::string& channel_id, Mode mode,
+ Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
+}
+
+Channel::~Channel() {
+ delete channel_impl_;
+}
+
+bool Channel::Connect() {
+ return channel_impl_->Connect();
+}
+
+void Channel::Close() {
+ channel_impl_->Close();
+}
+
+void Channel::set_listener(Listener* listener) {
+ channel_impl_->set_listener(listener);
+}
+
+bool Channel::Send(Message* message) {
+ return channel_impl_->Send(message);
+}
+
+int Channel::GetClientFileDescriptor() const {
+ return channel_impl_->GetClientFileDescriptor();
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_channel_posix.h b/ipc/ipc_channel_posix.h
new file mode 100644
index 0000000..aa69d4f
--- /dev/null
+++ b/ipc/ipc_channel_posix.h
@@ -0,0 +1,135 @@
+// 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 IPC_IPC_CHANNEL_POSIX_H_
+#define IPC_IPC_CHANNEL_POSIX_H_
+
+#include "ipc/ipc_channel.h"
+
+#include <sys/socket.h> // for CMSG macros
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/message_loop.h"
+#include "ipc/file_descriptor_set_posix.h"
+
+namespace IPC {
+
+// Store that channel name |name| is available via socket |socket|.
+// Used when the channel has been precreated by another process on
+// our behalf and they've just shipped us the socket.
+void AddChannelSocket(const std::string& name, int socket);
+
+// Remove the channel name mapping, and close the corresponding socket.
+void RemoveAndCloseChannelSocket(const std::string& name);
+
+// Construct a socket pair appropriate for IPC: UNIX domain, nonblocking.
+// Returns false on error.
+bool SocketPair(int* fd1, int* fd2);
+
+// 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:
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const std::string& channel_id, Mode mode, Listener* listener);
+ ~ChannelImpl() { Close(); }
+ bool Connect();
+ void Close();
+ void set_listener(Listener* listener) { listener_ = listener; }
+ bool Send(Message* message);
+ int GetClientFileDescriptor() const;
+
+ private:
+ bool CreatePipe(const std::string& channel_id, Mode mode);
+
+ bool ProcessIncomingMessages();
+ bool ProcessOutgoingMessages();
+
+ // MessageLoopForIO::Watcher implementation.
+ virtual void OnFileCanReadWithoutBlocking(int fd);
+ virtual void OnFileCanWriteWithoutBlocking(int fd);
+
+ 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 variable
+ // to keep track of where we are.
+ size_t message_send_bytes_written_;
+
+ // If the kTestingChannelID flag is specified, we use a FIFO instead of
+ // a socketpair().
+ bool uses_fifo_;
+
+ // File descriptor we're listening on for new connections in the FIFO case;
+ // unused otherwise.
+ int server_listen_pipe_;
+
+ // The pipe used for communication.
+ int pipe_;
+
+ // For a server, the client end of our socketpair() -- the other end of our
+ // pipe_ that is passed to the client.
+ int client_pipe_;
+
+ // The "name" of our pipe. On Windows this is the global identifier for
+ // the pipe. On POSIX it's used as a key in a local map of file descriptors.
+ std::string pipe_name_;
+
+ Listener* listener_;
+
+ // Messages to be sent are queued here.
+ std::queue<Message*> output_queue_;
+
+ // We read from the pipe into this buffer
+ char input_buf_[Channel::kReadBufferSize];
+
+ enum {
+ // We assume a worst case: kReadBufferSize bytes of messages, where each
+ // message has no payload and a full complement of descriptors.
+ MAX_READ_FDS = (Channel::kReadBufferSize / sizeof(IPC::Message::Header)) *
+ FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE,
+ };
+
+ // This is a control message buffer large enough to hold kMaxReadFDs
+#if defined(OS_MACOSX)
+ // TODO(agl): OSX appears to have non-constant CMSG macros!
+ char input_cmsg_buf_[1024];
+#else
+ char input_cmsg_buf_[CMSG_SPACE(sizeof(int) * MAX_READ_FDS)];
+#endif
+
+ // Large messages that span multiple pipe buffers, get built-up using
+ // this buffer.
+ std::string input_overflow_buf_;
+ 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_;
+
+ ScopedRunnableMethodFactory<ChannelImpl> factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_CHANNEL_POSIX_H_
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc
new file mode 100644
index 0000000..d96e413
--- /dev/null
+++ b/ipc/ipc_channel_proxy.cc
@@ -0,0 +1,288 @@
+// 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 "base/message_loop.h"
+#include "base/thread.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_logging.h"
+#include "ipc/ipc_message_utils.h"
+
+namespace IPC {
+
+//-----------------------------------------------------------------------------
+
+ChannelProxy::Context::Context(Channel::Listener* listener,
+ MessageFilter* filter,
+ MessageLoop* ipc_message_loop)
+ : listener_message_loop_(MessageLoop::current()),
+ listener_(listener),
+ ipc_message_loop_(ipc_message_loop),
+ channel_(NULL),
+ peer_pid_(0),
+ channel_connected_called_(false) {
+ if (filter)
+ filters_.push_back(filter);
+}
+
+void ChannelProxy::Context::CreateChannel(const std::string& id,
+ const Channel::Mode& mode) {
+ DCHECK(channel_ == NULL);
+ channel_id_ = id;
+ channel_ = new Channel(id, mode, this);
+}
+
+bool ChannelProxy::Context::TryFilters(const Message& message) {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging* logger = Logging::current();
+ if (logger->Enabled())
+ logger->OnPreDispatchMessage(message);
+#endif
+
+ for (size_t i = 0; i < filters_.size(); ++i) {
+ if (filters_[i]->OnMessageReceived(message)) {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (logger->Enabled())
+ logger->OnPostDispatchMessage(message, channel_id_);
+#endif
+ return true;
+ }
+ }
+ return false;
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnMessageReceived(const Message& message) {
+ // First give a chance to the filters to process this message.
+ if (!TryFilters(message))
+ OnMessageReceivedNoFilter(message);
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) {
+ // NOTE: This code relies on the listener's message loop not going away while
+ // this thread is active. That should be a reasonable assumption, but it
+ // feels risky. We may want to invent some more indirect way of referring to
+ // a MessageLoop if this becomes a problem.
+ listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Context::OnDispatchMessage, message));
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) {
+ peer_pid_ = peer_pid;
+ for (size_t i = 0; i < filters_.size(); ++i)
+ filters_[i]->OnChannelConnected(peer_pid);
+
+ // See above comment about using listener_message_loop_ here.
+ listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Context::OnDispatchConnected));
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelError() {
+ for (size_t i = 0; i < filters_.size(); ++i)
+ filters_[i]->OnChannelError();
+
+ // See above comment about using listener_message_loop_ here.
+ listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Context::OnDispatchError));
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelOpened() {
+ DCHECK(channel_ != NULL);
+
+ // Assume a reference to ourselves on behalf of this thread. This reference
+ // will be released when we are closed.
+ AddRef();
+
+ if (!channel_->Connect()) {
+ OnChannelError();
+ return;
+ }
+
+ for (size_t i = 0; i < filters_.size(); ++i)
+ filters_[i]->OnFilterAdded(channel_);
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelClosed() {
+ // It's okay for IPC::ChannelProxy::Close to be called more than once, which
+ // would result in this branch being taken.
+ if (!channel_)
+ return;
+
+ for (size_t i = 0; i < filters_.size(); ++i) {
+ filters_[i]->OnChannelClosing();
+ filters_[i]->OnFilterRemoved();
+ }
+
+ // We don't need the filters anymore.
+ filters_.clear();
+
+ delete channel_;
+ channel_ = NULL;
+
+ // Balance with the reference taken during startup. This may result in
+ // self-destruction.
+ Release();
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnSendMessage(Message* message) {
+ if (!channel_->Send(message))
+ OnChannelError();
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnAddFilter(MessageFilter* filter) {
+ filters_.push_back(filter);
+
+ // If the channel has already been created, then we need to send this message
+ // so that the filter gets access to the Channel.
+ if (channel_)
+ filter->OnFilterAdded(channel_);
+
+ // Balances the AddRef in ChannelProxy::AddFilter.
+ filter->Release();
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) {
+ for (size_t i = 0; i < filters_.size(); ++i) {
+ if (filters_[i].get() == filter) {
+ filter->OnFilterRemoved();
+ filters_.erase(filters_.begin() + i);
+ return;
+ }
+ }
+
+ NOTREACHED() << "filter to be removed not found";
+}
+
+// Called on the listener's thread
+void ChannelProxy::Context::OnDispatchMessage(const Message& message) {
+ if (!listener_)
+ return;
+
+ OnDispatchConnected();
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging* logger = Logging::current();
+ if (message.type() == IPC_LOGGING_ID) {
+ logger->OnReceivedLoggingMessage(message);
+ return;
+ }
+
+ if (logger->Enabled())
+ logger->OnPreDispatchMessage(message);
+#endif
+
+ listener_->OnMessageReceived(message);
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (logger->Enabled())
+ logger->OnPostDispatchMessage(message, channel_id_);
+#endif
+}
+
+// Called on the listener's thread
+void ChannelProxy::Context::OnDispatchConnected() {
+ if (channel_connected_called_)
+ return;
+
+ channel_connected_called_ = true;
+ if (listener_)
+ listener_->OnChannelConnected(peer_pid_);
+}
+
+// Called on the listener's thread
+void ChannelProxy::Context::OnDispatchError() {
+ if (listener_)
+ listener_->OnChannelError();
+}
+
+//-----------------------------------------------------------------------------
+
+ChannelProxy::ChannelProxy(const std::string& channel_id, Channel::Mode mode,
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_thread)
+ : context_(new Context(listener, filter, ipc_thread)) {
+ Init(channel_id, mode, ipc_thread, true);
+}
+
+ChannelProxy::ChannelProxy(const std::string& channel_id, Channel::Mode mode,
+ MessageLoop* ipc_thread, Context* context,
+ bool create_pipe_now)
+ : context_(context) {
+ Init(channel_id, mode, ipc_thread, create_pipe_now);
+}
+
+void ChannelProxy::Init(const std::string& channel_id, Channel::Mode mode,
+ MessageLoop* ipc_thread_loop, bool create_pipe_now) {
+ if (create_pipe_now) {
+ // Create the channel immediately. This effectively sets up the
+ // low-level pipe so that the client can connect. Without creating
+ // the pipe immediately, it is possible for a listener to attempt
+ // to connect and get an error since the pipe doesn't exist yet.
+ context_->CreateChannel(channel_id, mode);
+ } else {
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::CreateChannel, channel_id, mode));
+ }
+
+ // complete initialization on the background thread
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnChannelOpened));
+}
+
+void ChannelProxy::Close() {
+ // Clear the backpointer to the listener so that any pending calls to
+ // Context::OnDispatchMessage or OnDispatchError will be ignored. It is
+ // possible that the channel could be closed while it is receiving messages!
+ context_->Clear();
+
+ if (context_->ipc_message_loop()) {
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnChannelClosed));
+ }
+}
+
+bool ChannelProxy::Send(Message* message) {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::current()->OnSendMessage(message, context_->channel_id());
+#endif
+
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnSendMessage, message));
+ return true;
+}
+
+void ChannelProxy::AddFilter(MessageFilter* filter) {
+ // We want to addref the filter to prevent it from
+ // being destroyed before the OnAddFilter call is invoked.
+ filter->AddRef();
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnAddFilter, filter));
+}
+
+void ChannelProxy::RemoveFilter(MessageFilter* filter) {
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnRemoveFilter, filter));
+}
+
+#if defined(OS_POSIX)
+// See the TODO regarding lazy initialization of the channel in
+// ChannelProxy::Init().
+// We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe.
+int ChannelProxy::GetClientFileDescriptor() const {
+ Channel *channel = context_.get()->channel_;
+ DCHECK(channel); // Channel must have been created first.
+ return channel->GetClientFileDescriptor();
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
+} // namespace IPC
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
new file mode 100644
index 0000000..1aed33e
--- /dev/null
+++ b/ipc/ipc_channel_proxy.h
@@ -0,0 +1,209 @@
+// 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 IPC_IPC_CHANNEL_PROXY_H__
+#define IPC_IPC_CHANNEL_PROXY_H__
+
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "ipc/ipc_channel.h"
+
+class MessageLoop;
+
+namespace IPC {
+
+//-----------------------------------------------------------------------------
+// IPC::ChannelProxy
+//
+// This class is a helper class that is useful when you wish to run an IPC
+// channel on a background thread. It provides you with the option of either
+// handling IPC messages on that background thread or having them dispatched to
+// your main thread (the thread on which the IPC::ChannelProxy is created).
+//
+// The API for an IPC::ChannelProxy is very similar to that of an IPC::Channel.
+// When you send a message to an IPC::ChannelProxy, the message is routed to
+// the background thread, where it is then passed to the IPC::Channel's Send
+// method. This means that you can send a message from your thread and your
+// message will be sent over the IPC channel when possible instead of being
+// delayed until your thread returns to its message loop. (Often IPC messages
+// will queue up on the IPC::Channel when there is a lot of traffic, and the
+// channel will not get cycles to flush its message queue until the thread, on
+// which it is running, returns to its message loop.)
+//
+// An IPC::ChannelProxy can have a MessageFilter associated with it, which will
+// be notified of incoming messages on the IPC::Channel's thread. This gives
+// the consumer of IPC::ChannelProxy the ability to respond to incoming
+// messages on this background thread instead of on their own thread, which may
+// be bogged down with other processing. The result can be greatly improved
+// latency for messages that can be handled on a background thread.
+//
+// The consumer of IPC::ChannelProxy is responsible for allocating the Thread
+// instance where the IPC::Channel will be created and operated.
+//
+class ChannelProxy : public Message::Sender {
+ public:
+ // A class that receives messages on the thread where the IPC channel is
+ // running. It can choose to prevent the default action for an IPC message.
+ class MessageFilter : public base::RefCountedThreadSafe<MessageFilter> {
+ public:
+ virtual ~MessageFilter() {}
+
+ // Called on the background thread to provide the filter with access to the
+ // channel. Called when the IPC channel is initialized or when AddFilter
+ // is called if the channel is already initialized.
+ virtual void OnFilterAdded(Channel* channel) {}
+
+ // Called on the background thread when the filter has been removed from
+ // the ChannelProxy and when the Channel is closing. After a filter is
+ // removed, it will not be called again.
+ virtual void OnFilterRemoved() {}
+
+ // Called to inform the filter that the IPC channel is connected and we
+ // have received the internal Hello message from the peer.
+ virtual void OnChannelConnected(int32 peer_pid) {}
+
+ // Called when there is an error on the channel, typically that the channel
+ // has been closed.
+ virtual void OnChannelError() {}
+
+ // Called to inform the filter that the IPC channel will be destroyed.
+ // OnFilterRemoved is called immediately after this.
+ virtual void OnChannelClosing() {}
+
+ // Return true to indicate that the message was handled, or false to let
+ // the message be handled in the default way.
+ virtual bool OnMessageReceived(const Message& message) {
+ return false;
+ }
+ };
+
+ // Initializes a channel proxy. The channel_id and mode parameters are
+ // passed directly to the underlying IPC::Channel. The listener is called on
+ // the thread that creates the ChannelProxy. The filter's OnMessageReceived
+ // method is called on the thread where the IPC::Channel is running. The
+ // filter may be null if the consumer is not interested in handling messages
+ // on the background thread. Any message not handled by the filter will be
+ // dispatched to the listener. The given message loop indicates where the
+ // IPC::Channel should be created.
+ ChannelProxy(const std::string& channel_id, Channel::Mode mode,
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_thread_loop);
+
+ ~ChannelProxy() {
+ Close();
+ }
+
+ // Close the IPC::Channel. This operation completes asynchronously, once the
+ // background thread processes the command to close the channel. It is ok to
+ // call this method multiple times. Redundant calls are ignored.
+ //
+ // WARNING: The MessageFilter object held by the ChannelProxy is also
+ // released asynchronously, and it may in fact have its final reference
+ // released on the background thread. The caller should be careful to deal
+ // with / allow for this possibility.
+ void Close();
+
+ // Send a message asynchronously. The message is routed to the background
+ // thread where it is passed to the IPC::Channel's Send method.
+ virtual bool Send(Message* message);
+
+ // Used to intercept messages as they are received on the background thread.
+ //
+ // Ordinarily, messages sent to the ChannelProxy are routed to the matching
+ // listener on the worker thread. This API allows code to intercept messages
+ // before they are sent to the worker thread.
+ void AddFilter(MessageFilter* filter);
+ void RemoveFilter(MessageFilter* filter);
+
+#if defined(OS_POSIX)
+ // Calls through to the underlying channel's methods.
+ // TODO(playmobil): For now this is only implemented in the case of
+ // create_pipe_now = true, we need to figure this out for the latter case.
+ int GetClientFileDescriptor() const;
+#endif // defined(OS_POSIX)
+
+ protected:
+ class Context;
+ // A subclass uses this constructor if it needs to add more information
+ // to the internal state. If create_pipe_now is true, the pipe is created
+ // immediately. Otherwise it's created on the IO thread.
+ ChannelProxy(const std::string& channel_id, Channel::Mode mode,
+ MessageLoop* ipc_thread_loop, Context* context,
+ bool create_pipe_now);
+
+ // Used internally to hold state that is referenced on the IPC thread.
+ class Context : public base::RefCountedThreadSafe<Context>,
+ public Channel::Listener {
+ public:
+ Context(Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_thread);
+ virtual ~Context() { }
+ MessageLoop* ipc_message_loop() const { return ipc_message_loop_; }
+ const std::string& channel_id() const { return channel_id_; }
+
+ // Dispatches a message on the listener thread.
+ void OnDispatchMessage(const Message& message);
+
+ protected:
+ // IPC::Channel::Listener methods:
+ virtual void OnMessageReceived(const Message& message);
+ virtual void OnChannelConnected(int32 peer_pid);
+ virtual void OnChannelError();
+
+ // Like OnMessageReceived but doesn't try the filters.
+ void OnMessageReceivedNoFilter(const Message& message);
+
+ // Gives the filters a chance at processing |message|.
+ // Returns true if the message was processed, false otherwise.
+ bool TryFilters(const Message& message);
+
+ // Like Open and Close, but called on the IPC thread.
+ virtual void OnChannelOpened();
+ virtual void OnChannelClosed();
+
+ // Called on the consumers thread when the ChannelProxy is closed. At that
+ // point the consumer is telling us that they don't want to receive any
+ // more messages, so we honor that wish by forgetting them!
+ virtual void Clear() { listener_ = NULL; }
+
+ private:
+ friend class ChannelProxy;
+ // Create the Channel
+ void CreateChannel(const std::string& id, const Channel::Mode& mode);
+
+ // Methods called via InvokeLater:
+ void OnSendMessage(Message* message_ptr);
+ void OnAddFilter(MessageFilter* filter);
+ void OnRemoveFilter(MessageFilter* filter);
+ void OnDispatchConnected();
+ void OnDispatchError();
+
+ MessageLoop* listener_message_loop_;
+ Channel::Listener* listener_;
+
+ // List of filters. This is only accessed on the IPC thread.
+ std::vector<scoped_refptr<MessageFilter> > filters_;
+ MessageLoop* ipc_message_loop_;
+ Channel* channel_;
+ std::string channel_id_;
+ int peer_pid_;
+ bool channel_connected_called_;
+ };
+
+ Context* context() { return context_; }
+
+ private:
+ void Init(const std::string& channel_id, Channel::Mode mode,
+ MessageLoop* ipc_thread_loop, bool create_pipe_now);
+
+ // By maintaining this indirection (ref-counted) to our internal state, we
+ // can safely be destroyed while the background thread continues to do stuff
+ // that involves this data.
+ scoped_refptr<Context> context_;
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_CHANNEL_PROXY_H__
diff --git a/ipc/ipc_channel_win.cc b/ipc/ipc_channel_win.cc
new file mode 100644
index 0000000..9296ea4
--- /dev/null
+++ b/ipc/ipc_channel_win.cc
@@ -0,0 +1,442 @@
+// 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 "ipc/ipc_channel_win.h"
+
+#include <windows.h>
+#include <sstream>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/non_thread_safe.h"
+#include "base/stats_counters.h"
+#include "base/win_util.h"
+#include "ipc/ipc_logging.h"
+#include "ipc/ipc_message_utils.h"
+
+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 std::string& channel_id, Mode mode,
+ Listener* listener)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
+ pipe_(INVALID_HANDLE_VALUE),
+ listener_(listener),
+ waiting_connect_(mode == MODE_SERVER),
+ processing_incoming_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+ if (!CreatePipe(channel_id, mode)) {
+ // The pipe may have been closed already.
+ LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
+ "\" in " << (mode == 0 ? "server" : "client") << " mode.";
+ }
+}
+
+void Channel::ChannelImpl::Close() {
+ if (thread_check_.get()) {
+ DCHECK(thread_check_->CalledOnValidThread());
+ }
+
+ 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;
+ }
+
+ // Make sure all IO has completed.
+ base::Time start = base::Time::Now();
+ while (input_state_.is_pending || output_state_.is_pending) {
+ MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
+ }
+ if (waited) {
+ // We want to see if we block the message loop for too long.
+ UMA_HISTOGRAM_TIMES("AsyncIO.IPCChannelClose", base::Time::Now() - start);
+ }
+
+ while (!output_queue_.empty()) {
+ Message* m = output_queue_.front();
+ output_queue_.pop();
+ delete m;
+ }
+}
+
+bool Channel::ChannelImpl::Send(Message* message) {
+ DCHECK(thread_check_->CalledOnValidThread());
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sending message @" << message << " on channel @" << this
+ << " with type " << message->type()
+ << " (" << output_queue_.size() << " in queue)";
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::current()->OnSendMessage(message, "");
+#endif
+
+ output_queue_.push(message);
+ // ensure waiting to write
+ if (!waiting_connect_) {
+ if (!output_state_.is_pending) {
+ if (!ProcessOutgoingMessages(NULL, 0))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+const std::wstring Channel::ChannelImpl::PipeName(
+ const std::string& channel_id) const {
+ std::wostringstream ss;
+ // XXX(darin): get application name from somewhere else
+ ss << L"\\\\.\\pipe\\chrome." << ASCIIToWide(channel_id);
+ return ss.str();
+}
+
+bool Channel::ChannelImpl::CreatePipe(const std::string& channel_id,
+ Mode mode) {
+ DCHECK(pipe_ == INVALID_HANDLE_VALUE);
+ const std::wstring pipe_name = PipeName(channel_id);
+ if (mode == MODE_SERVER) {
+ SECURITY_ATTRIBUTES security_attributes = {0};
+ security_attributes.bInheritHandle = FALSE;
+ security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ if (!win_util::GetLogonSessionOnlyDACL(
+ reinterpret_cast<SECURITY_DESCRIPTOR**>(
+ &security_attributes.lpSecurityDescriptor))) {
+ NOTREACHED();
+ }
+
+ 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)
+ &security_attributes);
+ LocalFree(security_attributes.lpSecurityDescriptor);
+ } 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.
+ LOG(WARNING) << "failed to create pipe: " << GetLastError();
+ return false;
+ }
+
+ // Create the Hello message to be sent when Connect is called
+ scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
+ HELLO_MESSAGE_TYPE,
+ IPC::Message::PRIORITY_NORMAL));
+ if (!m->WriteInt(GetCurrentProcessId())) {
+ CloseHandle(pipe_);
+ pipe_ = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ output_queue_.push(m.release());
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+ DLOG(WARNING) << "Connect called twice";
+
+ if (!thread_check_.get())
+ thread_check_.reset(new NonThreadSafe());
+
+ 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_)
+ ProcessConnection();
+
+ 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(FROM_HERE, factory_.NewRunnableMethod(
+ &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
+ }
+
+ if (!waiting_connect_)
+ ProcessOutgoingMessages(NULL, 0);
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessConnection() {
+ DCHECK(thread_check_->CalledOnValidThread());
+ 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) {
+ DCHECK(thread_check_->CalledOnValidThread());
+ 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_,
+ Channel::kReadBufferSize,
+ &bytes_read,
+ &input_state_.context.overlapped);
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ input_state_.is_pending = true;
+ return true;
+ }
+ 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, *end;
+ if (input_overflow_buf_.empty()) {
+ p = input_buf_;
+ end = p + bytes_read;
+ } else {
+ if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
+ input_overflow_buf_.clear();
+ LOG(ERROR) << "IPC message is too big";
+ return false;
+ }
+ input_overflow_buf_.append(input_buf_, bytes_read);
+ p = input_overflow_buf_.data();
+ end = p + input_overflow_buf_.size();
+ }
+
+ while (p < end) {
+ const char* message_tail = Message::FindNext(p, end);
+ if (message_tail) {
+ int len = static_cast<int>(message_tail - p);
+ const Message m(p, len);
+#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.
+ listener_->OnChannelConnected(MessageIterator(m).NextInt());
+ } else {
+ listener_->OnMessageReceived(m);
+ }
+ p = message_tail;
+ } else {
+ // Last message is partial.
+ break;
+ }
+ }
+ input_overflow_buf_.assign(p, end - p);
+
+ 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?
+ DCHECK(thread_check_->CalledOnValidThread());
+
+ if (output_state_.is_pending) {
+ DCHECK(context);
+ output_state_.is_pending = false;
+ if (!context || bytes_written == 0) {
+ DWORD err = GetLastError();
+ LOG(ERROR) << "pipe error: " << err;
+ return false;
+ }
+ // Message was sent.
+ DCHECK(!output_queue_.empty());
+ Message* m = output_queue_.front();
+ output_queue_.pop();
+ delete m;
+ }
+
+ if (output_queue_.empty())
+ return true;
+
+ if (INVALID_HANDLE_VALUE == pipe_)
+ return false;
+
+ // Write to pipe...
+ Message* m = output_queue_.front();
+ BOOL ok = WriteFile(pipe_,
+ m->data(),
+ m->size(),
+ &bytes_written,
+ &output_state_.context.overlapped);
+ 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;
+ }
+ 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;
+ DCHECK(thread_check_->CalledOnValidThread());
+ if (context == &input_state_.context) {
+ if (waiting_connect_) {
+ if (!ProcessConnection())
+ return;
+ // We may have some messages queued up to send...
+ if (!output_queue_.empty() && !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();
+ }
+}
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const std::string& channel_id, Mode mode,
+ Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
+}
+
+Channel::~Channel() {
+ delete channel_impl_;
+}
+
+bool Channel::Connect() {
+ return channel_impl_->Connect();
+}
+
+void Channel::Close() {
+ channel_impl_->Close();
+}
+
+void Channel::set_listener(Listener* listener) {
+ channel_impl_->set_listener(listener);
+}
+
+bool Channel::Send(Message* message) {
+ return channel_impl_->Send(message);
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_channel_win.h b/ipc/ipc_channel_win.h
new file mode 100644
index 0000000..7610d02
--- /dev/null
+++ b/ipc/ipc_channel_win.h
@@ -0,0 +1,85 @@
+// 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 IPC_IPC_CHANNEL_WIN_H_
+#define IPC_IPC_CHANNEL_WIN_H_
+
+#include "ipc/ipc_channel.h"
+
+#include <queue>
+#include <string>
+
+#include "base/message_loop.h"
+
+class NonThreadSafe;
+
+namespace IPC {
+
+class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
+ public:
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const std::string& channel_id, Mode mode, Listener* listener);
+ ~ChannelImpl() { Close(); }
+ bool Connect();
+ void Close();
+ void set_listener(Listener* listener) { listener_ = listener; }
+ bool Send(Message* message);
+ private:
+ const std::wstring PipeName(const std::string& channel_id) const;
+ bool CreatePipe(const std::string& channel_id, Mode mode);
+
+ 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.
+ std::queue<Message*> output_queue_;
+
+ // We read from the pipe into this buffer
+ char input_buf_[Channel::kReadBufferSize];
+
+ // Large messages that span multiple pipe buffers, get built-up using
+ // this buffer.
+ std::string input_overflow_buf_;
+
+ // 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_;
+
+ ScopedRunnableMethodFactory<ChannelImpl> factory_;
+
+ scoped_ptr<NonThreadSafe> thread_check_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_CHANNEL_WIN_H_
diff --git a/ipc/ipc_descriptors.h b/ipc/ipc_descriptors.h
new file mode 100644
index 0000000..5717aa4
--- /dev/null
+++ b/ipc/ipc_descriptors.h
@@ -0,0 +1,14 @@
+// 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 IPC_IPC_DESCRIPTORS_H_
+#define IPC_IPC_DESCRIPTORS_H_
+
+// This is a list of global descriptor keys to be used with the
+// base::GlobalDescriptors object (see base/global_descriptors_posix.h)
+enum {
+ kPrimaryIPCChannel = 0,
+};
+
+#endif // IPC_IPC_DESCRIPTORS_H_
diff --git a/ipc/ipc_fuzzing_tests.cc b/ipc/ipc_fuzzing_tests.cc
new file mode 100644
index 0000000..c79d05a
--- /dev/null
+++ b/ipc/ipc_fuzzing_tests.cc
@@ -0,0 +1,430 @@
+// 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 <stdio.h>
+#include <iostream>
+#include <string>
+#include <sstream>
+
+#include "base/message_loop.h"
+#include "base/platform_thread.h"
+#include "base/process_util.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_message_utils.h"
+#include "ipc/ipc_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+TEST(IPCMessageIntegrity, ReadBeyondBufferStr) {
+ //This was BUG 984408.
+ uint32 v1 = kuint32max - 1;
+ int v2 = 666;
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(v1));
+ EXPECT_TRUE(m.WriteInt(v2));
+
+ void* iter = NULL;
+ std::string vs;
+ EXPECT_FALSE(m.ReadString(&iter, &vs));
+}
+
+TEST(IPCMessageIntegrity, ReadBeyondBufferWStr) {
+ //This was BUG 984408.
+ uint32 v1 = kuint32max - 1;
+ int v2 = 777;
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(v1));
+ EXPECT_TRUE(m.WriteInt(v2));
+
+ void* iter = NULL;
+ std::wstring vs;
+ EXPECT_FALSE(m.ReadWString(&iter, &vs));
+}
+
+TEST(IPCMessageIntegrity, ReadBytesBadIterator) {
+ // This was BUG 1035467.
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(1));
+ EXPECT_TRUE(m.WriteInt(2));
+
+ void* iter = NULL;
+ const char* data = NULL;
+ EXPECT_FALSE(m.ReadBytes(&iter, &data, sizeof(int)));
+}
+
+TEST(IPCMessageIntegrity, ReadVectorNegativeSize) {
+ // A slight variation of BUG 984408. Note that the pickling of vector<char>
+ // has a specialized template which is not vulnerable to this bug. So here
+ // try to hit the non-specialized case vector<P>.
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(-1)); // This is the count of elements.
+ EXPECT_TRUE(m.WriteInt(1));
+ EXPECT_TRUE(m.WriteInt(2));
+ EXPECT_TRUE(m.WriteInt(3));
+
+ std::vector<double> vec;
+ void* iter = 0;
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec));
+}
+
+TEST(IPCMessageIntegrity, ReadVectorTooLarge1) {
+ // This was BUG 1006367. This is the large but positive length case. Again
+ // we try to hit the non-specialized case vector<P>.
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(0x21000003)); // This is the count of elements.
+ EXPECT_TRUE(m.WriteInt64(1));
+ EXPECT_TRUE(m.WriteInt64(2));
+
+ std::vector<int64> vec;
+ void* iter = 0;
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec));
+}
+
+TEST(IPCMessageIntegrity, ReadVectorTooLarge2) {
+ // This was BUG 1006367. This is the large but positive with an additional
+ // integer overflow when computing the actual byte size. Again we try to hit
+ // the non-specialized case vector<P>.
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(0x71000000)); // This is the count of elements.
+ EXPECT_TRUE(m.WriteInt64(1));
+ EXPECT_TRUE(m.WriteInt64(2));
+
+ std::vector<int64> vec;
+ void* iter = 0;
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec));
+}
+
+// We don't actually use the messages defined in this file, but we do this
+// to get to the IPC macros.
+#define MESSAGES_INTERNAL_FILE "ipc/ipc_sync_message_unittest.h"
+#include "ipc/ipc_message_macros.h"
+
+enum IPCMessageIds {
+ UNUSED_IPC_TYPE,
+ SERVER_FIRST_IPC_TYPE, // 1st Test message tag.
+ SERVER_SECOND_IPC_TYPE, // 2nd Test message tag.
+ SERVER_THIRD_IPC_TYPE, // 3rd Test message tag.
+ CLIENT_MALFORMED_IPC, // Sent to client if server detects bad message.
+ CLIENT_UNHANDLED_IPC // Sent to client if server detects unhanded IPC.
+};
+
+// Generic message class that is an int followed by a wstring.
+class MsgClassIS : public IPC::MessageWithTuple< Tuple2<int, std::wstring> > {
+ public:
+ enum { ID = SERVER_FIRST_IPC_TYPE };
+ MsgClassIS(const int& arg1, const std::wstring& arg2)
+ : IPC::MessageWithTuple< Tuple2<int, std::wstring> >(
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1, arg2)) {}
+};
+
+// Generic message class that is a wstring followed by an int.
+class MsgClassSI : public IPC::MessageWithTuple< Tuple2<std::wstring, int> > {
+ public:
+ enum { ID = SERVER_SECOND_IPC_TYPE };
+ MsgClassSI(const std::wstring& arg1, const int& arg2)
+ : IPC::MessageWithTuple< Tuple2<std::wstring, int> >(
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1, arg2)) {}
+};
+
+// Message to create a mutex in the IPC server, using the received name.
+class MsgDoMutex : public IPC::MessageWithTuple< Tuple2<std::wstring, int> > {
+ public:
+ enum { ID = SERVER_THIRD_IPC_TYPE };
+ MsgDoMutex(const std::wstring& mutex_name, const int& unused)
+ : IPC::MessageWithTuple< Tuple2<std::wstring, int> >(
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(mutex_name, unused)) {}
+};
+
+class SimpleListener : public IPC::Channel::Listener {
+ public:
+ SimpleListener() : other_(NULL) {
+ }
+ void Init(IPC::Message::Sender* s) {
+ other_ = s;
+ }
+ protected:
+ IPC::Message::Sender* other_;
+};
+
+enum {
+ FUZZER_ROUTING_ID = 5
+};
+
+// The fuzzer server class. It runs in a child process and expects
+// only two IPC calls; after that it exits the message loop which
+// terminates the child process.
+class FuzzerServerListener : public SimpleListener {
+ public:
+ FuzzerServerListener() : message_count_(2), pending_messages_(0) {
+ }
+ virtual void OnMessageReceived(const IPC::Message& msg) {
+ if (msg.routing_id() == MSG_ROUTING_CONTROL) {
+ ++pending_messages_;
+ IPC_BEGIN_MESSAGE_MAP(FuzzerServerListener, msg)
+ IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage)
+ IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage)
+ IPC_END_MESSAGE_MAP()
+ if (pending_messages_) {
+ // Probably a problem de-serializing the message.
+ ReplyMsgNotHandled(msg.type());
+ }
+ }
+ }
+
+ private:
+ void OnMsgClassISMessage(int value, const std::wstring& text) {
+ UseData(MsgClassIS::ID, value, text);
+ RoundtripAckReply(FUZZER_ROUTING_ID, MsgClassIS::ID, value);
+ Cleanup();
+ }
+
+ void OnMsgClassSIMessage(const std::wstring& text, int value) {
+ UseData(MsgClassSI::ID, value, text);
+ RoundtripAckReply(FUZZER_ROUTING_ID, MsgClassSI::ID, value);
+ Cleanup();
+ }
+
+ bool RoundtripAckReply(int routing, int type_id, int reply) {
+ IPC::Message* message = new IPC::Message(routing, type_id,
+ IPC::Message::PRIORITY_NORMAL);
+ message->WriteInt(reply + 1);
+ message->WriteInt(reply);
+ return other_->Send(message);
+ }
+
+ void Cleanup() {
+ --message_count_;
+ --pending_messages_;
+ if (0 == message_count_)
+ MessageLoop::current()->Quit();
+ }
+
+ void ReplyMsgNotHandled(int type_id) {
+ RoundtripAckReply(FUZZER_ROUTING_ID, CLIENT_UNHANDLED_IPC, type_id);
+ Cleanup();
+ }
+
+ void UseData(int caller, int value, const std::wstring& text) {
+ std::wostringstream wos;
+ wos << L"IPC fuzzer:" << caller << " [" << value << L" " << text << L"]\n";
+ std::wstring output = wos.str();
+ LOG(WARNING) << output.c_str();
+ };
+
+ int message_count_;
+ int pending_messages_;
+};
+
+class FuzzerClientListener : public SimpleListener {
+ public:
+ FuzzerClientListener() : last_msg_(NULL) {
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& msg) {
+ last_msg_ = new IPC::Message(msg);
+ MessageLoop::current()->Quit();
+ }
+
+ bool ExpectMessage(int value, int type_id) {
+ if (!MsgHandlerInternal(type_id))
+ return false;
+ int msg_value1 = 0;
+ int msg_value2 = 0;
+ void* iter = NULL;
+ if (!last_msg_->ReadInt(&iter, &msg_value1))
+ return false;
+ if (!last_msg_->ReadInt(&iter, &msg_value2))
+ return false;
+ if ((msg_value2 + 1) != msg_value1)
+ return false;
+ if (msg_value2 != value)
+ return false;
+
+ delete last_msg_;
+ last_msg_ = NULL;
+ return true;
+ }
+
+ bool ExpectMsgNotHandled(int type_id) {
+ return ExpectMessage(type_id, CLIENT_UNHANDLED_IPC);
+ }
+
+ private:
+ bool MsgHandlerInternal(int type_id) {
+ MessageLoop::current()->Run();
+ if (NULL == last_msg_)
+ return false;
+ if (FUZZER_ROUTING_ID != last_msg_->routing_id())
+ return false;
+ return (type_id == last_msg_->type());
+ };
+
+ IPC::Message* last_msg_;
+};
+
+// Runs the fuzzing server child mode. Returns when the preset number
+// of messages have been received.
+MULTIPROCESS_TEST_MAIN(RunFuzzServer) {
+ MessageLoopForIO main_message_loop;
+ FuzzerServerListener listener;
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_CLIENT, &listener);
+ chan.Connect();
+ listener.Init(&chan);
+ MessageLoop::current()->Run();
+ return 0;
+}
+
+class IPCFuzzingTest : public IPCChannelTest {
+};
+
+// This test makes sure that the FuzzerClientListener and FuzzerServerListener
+// are working properly by generating two well formed IPC calls.
+TEST_F(IPCFuzzingTest, SanityTest) {
+ FuzzerClientListener listener;
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan);
+ ASSERT_TRUE(server_process);
+ PlatformThread::Sleep(1000);
+ ASSERT_TRUE(chan.Connect());
+ listener.Init(&chan);
+
+ IPC::Message* msg = NULL;
+ int value = 43;
+ msg = new MsgClassIS(value, L"expect 43");
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(value, MsgClassIS::ID));
+
+ msg = new MsgClassSI(L"expect 44", ++value);
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(value, MsgClassSI::ID));
+
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000));
+ base::CloseProcessHandle(server_process);
+}
+
+// This test uses a payload that is smaller than expected.
+// This generates an error while unpacking the IPC buffer which in
+// In debug this triggers an assertion and in release it is ignored(!!). Right
+// after we generate another valid IPC to make sure framing is working
+// properly.
+#ifdef NDEBUG
+TEST_F(IPCFuzzingTest, MsgBadPayloadShort) {
+ FuzzerClientListener listener;
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan);
+ ASSERT_TRUE(server_process);
+ PlatformThread::Sleep(1000);
+ ASSERT_TRUE(chan.Connect());
+ listener.Init(&chan);
+
+ IPC::Message* msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(666);
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMsgNotHandled(MsgClassIS::ID));
+
+ msg = new MsgClassSI(L"expect one", 1);
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(1, MsgClassSI::ID));
+
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000));
+ base::CloseProcessHandle(server_process);
+}
+#endif // NDEBUG
+
+// This test uses a payload that has too many arguments, but so the payload
+// size is big enough so the unpacking routine does not generate an error as
+// in the case of MsgBadPayloadShort test.
+// This test does not pinpoint a flaw (per se) as by design we don't carry
+// type information on the IPC message.
+TEST_F(IPCFuzzingTest, MsgBadPayloadArgs) {
+ FuzzerClientListener listener;
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan);
+ ASSERT_TRUE(server_process);
+ PlatformThread::Sleep(1000);
+ ASSERT_TRUE(chan.Connect());
+ listener.Init(&chan);
+
+ IPC::Message* msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteWString(L"d");
+ msg->WriteInt(0);
+ msg->WriteInt(0x65); // Extra argument.
+
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(0, MsgClassSI::ID));
+
+ // Now send a well formed message to make sure the receiver wasn't
+ // thrown out of sync by the extra argument.
+ msg = new MsgClassIS(3, L"expect three");
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(3, MsgClassIS::ID));
+
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000));
+ base::CloseProcessHandle(server_process);
+}
+
+// This class is for testing the IPC_BEGIN_MESSAGE_MAP_EX macros.
+class ServerMacroExTest {
+ public:
+ ServerMacroExTest() : unhandled_msgs_(0) {
+ }
+ virtual bool OnMessageReceived(const IPC::Message& msg) {
+ bool msg_is_ok = false;
+ IPC_BEGIN_MESSAGE_MAP_EX(ServerMacroExTest, msg, msg_is_ok)
+ IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage)
+ IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage)
+ IPC_MESSAGE_UNHANDLED(++unhandled_msgs_)
+ IPC_END_MESSAGE_MAP_EX()
+ return msg_is_ok;
+ }
+
+ int unhandled_msgs() const {
+ return unhandled_msgs_;
+ }
+
+ private:
+ void OnMsgClassISMessage(int value, const std::wstring& text) {
+ }
+ void OnMsgClassSIMessage(const std::wstring& text, int value) {
+ }
+
+ int unhandled_msgs_;
+};
+
+TEST_F(IPCFuzzingTest, MsgMapExMacro) {
+ IPC::Message* msg = NULL;
+ ServerMacroExTest server;
+
+ // Test the regular messages.
+ msg = new MsgClassIS(3, L"text3");
+ EXPECT_TRUE(server.OnMessageReceived(*msg));
+ delete msg;
+ msg = new MsgClassSI(L"text2", 2);
+ EXPECT_TRUE(server.OnMessageReceived(*msg));
+ delete msg;
+
+#ifdef NDEBUG
+ // Test a bad message.
+ msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(2);
+ EXPECT_FALSE(server.OnMessageReceived(*msg));
+ delete msg;
+
+ msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(0x64);
+ msg->WriteInt(0x32);
+ EXPECT_FALSE(server.OnMessageReceived(*msg));
+ delete msg;
+
+ EXPECT_EQ(0, server.unhandled_msgs());
+#endif
+}
diff --git a/ipc/ipc_logging.cc b/ipc/ipc_logging.cc
new file mode 100644
index 0000000..4ecb7d9
--- /dev/null
+++ b/ipc/ipc_logging.cc
@@ -0,0 +1,309 @@
+// 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 "ipc/ipc_logging.h"
+
+#if defined(OS_POSIX)
+#ifdef IPC_MESSAGE_LOG_ENABLED
+// This will cause render_messages.h etc to define ViewMsgLog and friends.
+#define IPC_MESSAGE_MACROS_LOG_ENABLED
+#endif
+#endif
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/time.h"
+#include "base/waitable_event.h"
+#include "base/waitable_event_watcher.h"
+#include "ipc/ipc_switches.h"
+#include "ipc/ipc_sync_message.h"
+#include "ipc/ipc_message_utils.h"
+
+#if defined(OS_POSIX)
+#include "base/string_util.h"
+#include <unistd.h>
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+
+using base::Time;
+
+// IPC::Logging is allocated as a singleton, so we don't need any kind of
+// special retention program.
+template <>
+struct RunnableMethodTraits<IPC::Logging> {
+ static void RetainCallee(IPC::Logging*) {}
+ static void ReleaseCallee(IPC::Logging*) {}
+};
+
+namespace IPC {
+
+const wchar_t kLoggingEventName[] = L"ChromeIPCLog.%d";
+const int kLogSendDelayMs = 100;
+
+// We use a pointer to the function table to avoid any linker dependencies on
+// all the traits used as IPC message parameters.
+Logging::LogFunction *Logging::log_function_mapping_;
+
+Logging::Logging()
+ : logging_event_on_(NULL),
+ logging_event_off_(NULL),
+ enabled_(false),
+ queue_invoke_later_pending_(false),
+ sender_(NULL),
+ main_thread_(MessageLoop::current()),
+ consumer_(NULL) {
+ // Create an event for this browser instance that's set when logging is
+ // enabled, so child processes can know when logging is enabled.
+
+#if defined(OS_WIN)
+ // On Windows we have a couple of named events which switch logging on and
+ // off.
+ int browser_pid;
+ const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
+ std::wstring process_type =
+ parsed_command_line.GetSwitchValue(switches::kProcessType);
+ if (process_type.empty()) {
+ browser_pid = GetCurrentProcessId();
+ } else {
+ std::wstring channel_name =
+ parsed_command_line.GetSwitchValue(switches::kProcessChannelID);
+
+ browser_pid = _wtoi(channel_name.c_str());
+ DCHECK(browser_pid != 0);
+ }
+
+ std::wstring event_name = GetEventName(browser_pid, true);
+ logging_event_on_.reset(new base::WaitableEvent(
+ CreateEvent(NULL, TRUE, FALSE, event_name.c_str())));
+
+ event_name = GetEventName(browser_pid, false);
+ logging_event_off_.reset(new base::WaitableEvent(
+ CreateEvent(NULL, TRUE, FALSE, event_name.c_str())));
+
+ RegisterWaitForEvent(true);
+#elif defined(OS_POSIX)
+ if (getenv("CHROME_IPC_LOGGING"))
+ enabled_ = true;
+#endif
+
+ MessageLoop::current()->AddDestructionObserver(this);
+}
+
+Logging::~Logging() {
+}
+
+Logging* Logging::current() {
+ return Singleton<Logging>::get();
+}
+
+void Logging::RegisterWaitForEvent(bool enabled) {
+ watcher_.StopWatching();
+ watcher_.StartWatching(
+ enabled ? logging_event_on_.get() : logging_event_off_.get(), this);
+}
+
+void Logging::OnWaitableEventSignaled(base::WaitableEvent* event) {
+ enabled_ = event == logging_event_on_.get();
+ RegisterWaitForEvent(!enabled_);
+}
+
+void Logging::WillDestroyCurrentMessageLoop() {
+ watcher_.StopWatching();
+}
+
+void Logging::SetLoggerFunctions(LogFunction *functions) {
+ log_function_mapping_ = functions;
+}
+
+#if defined(OS_WIN)
+std::wstring Logging::GetEventName(bool enabled) {
+ return current()->GetEventName(GetCurrentProcessId(), enabled);
+}
+#endif
+
+std::wstring Logging::GetEventName(int browser_pid, bool enabled) {
+ std::wstring result = StringPrintf(kLoggingEventName, browser_pid);
+ result += enabled ? L"on" : L"off";
+ return result;
+}
+
+void Logging::SetConsumer(Consumer* consumer) {
+ consumer_ = consumer;
+}
+
+void Logging::Enable() {
+ logging_event_off_->Reset();
+ logging_event_on_->Signal();
+}
+
+void Logging::Disable() {
+ logging_event_on_->Reset();
+ logging_event_off_->Signal();
+}
+
+void Logging::OnSendLogs() {
+ queue_invoke_later_pending_ = false;
+ if (!sender_)
+ return;
+
+ Message* msg = new Message(
+ MSG_ROUTING_CONTROL, IPC_LOGGING_ID, Message::PRIORITY_NORMAL);
+ WriteParam(msg, queued_logs_);
+ queued_logs_.clear();
+ sender_->Send(msg);
+}
+
+void Logging::SetIPCSender(IPC::Message::Sender* sender) {
+ sender_ = sender;
+}
+
+void Logging::OnReceivedLoggingMessage(const Message& message) {
+ std::vector<LogData> data;
+ void* iter = NULL;
+ if (!ReadParam(&message, &iter, &data))
+ return;
+
+ for (size_t i = 0; i < data.size(); ++i) {
+ Log(data[i]);
+ }
+}
+
+void Logging::OnSendMessage(Message* message, const std::string& channel_id) {
+ if (!Enabled())
+ return;
+
+ if (message->is_reply()) {
+ LogData* data = message->sync_log_data();
+ if (!data)
+ return;
+
+ // This is actually the delayed reply to a sync message. Create a string
+ // of the output parameters, add it to the LogData that was earlier stashed
+ // with the reply, and log the result.
+ data->channel = channel_id;
+ GenerateLogData("", *message, data);
+ Log(*data);
+ delete data;
+ message->set_sync_log_data(NULL);
+ } else {
+ // If the time has already been set (i.e. by ChannelProxy), keep that time
+ // instead as it's more accurate.
+ if (!message->sent_time())
+ message->set_sent_time(Time::Now().ToInternalValue());
+ }
+}
+
+void Logging::OnPreDispatchMessage(const Message& message) {
+ message.set_received_time(Time::Now().ToInternalValue());
+}
+
+void Logging::OnPostDispatchMessage(const Message& message,
+ const std::string& channel_id) {
+ if (!Enabled() ||
+#if defined(OS_WIN)
+ !message.sent_time() ||
+#endif
+ message.dont_log())
+ return;
+
+ LogData data;
+ GenerateLogData(channel_id, message, &data);
+
+ if (MessageLoop::current() == main_thread_) {
+ Log(data);
+ } else {
+ main_thread_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Logging::Log, data));
+ }
+}
+
+void Logging::GetMessageText(uint16 type, std::wstring* name,
+ const Message* message,
+ std::wstring* params) {
+ if (!log_function_mapping_)
+ return;
+
+ int message_class = type >> 12;
+ if (log_function_mapping_[message_class] != NULL) {
+ log_function_mapping_[message_class](type, name, message, params);
+ } else {
+ DLOG(INFO) << "No logger function associated with message class " <<
+ message_class;
+ }
+}
+
+void Logging::Log(const LogData& data) {
+#if defined(OS_WIN)
+ if (consumer_) {
+ // We're in the browser process.
+ consumer_->Log(data);
+ } else {
+ // We're in the renderer or plugin processes.
+ if (sender_) {
+ queued_logs_.push_back(data);
+ if (!queue_invoke_later_pending_) {
+ queue_invoke_later_pending_ = true;
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(
+ this, &Logging::OnSendLogs), kLogSendDelayMs);
+ }
+ }
+ }
+#elif defined(OS_POSIX)
+ // On POSIX, for now, we just dump the log to stderr
+ fprintf(stderr, "ipc %s %d %d %s %s %s\n",
+ data.channel.c_str(),
+ data.routing_id,
+ data.type,
+ WideToUTF8(data.flags).c_str(),
+ WideToUTF8(data.message_name).c_str(),
+ WideToUTF8(data.params).c_str());
+#endif
+}
+
+void GenerateLogData(const std::string& channel, const Message& message,
+ LogData* data) {
+ if (message.is_reply()) {
+ // "data" should already be filled in.
+ std::wstring params;
+ Logging::GetMessageText(data->type, NULL, &message, &params);
+
+ if (!data->params.empty() && !params.empty())
+ data->params += L", ";
+
+ data->flags += L" DR";
+
+ data->params += params;
+ } else {
+ std::wstring flags;
+ if (message.is_sync())
+ flags = L"S";
+
+ if (message.is_reply())
+ flags += L"R";
+
+ if (message.is_reply_error())
+ flags += L"E";
+
+ std::wstring params, message_name;
+ Logging::GetMessageText(message.type(), &message_name, &message, &params);
+
+ data->channel = channel;
+ data->routing_id = message.routing_id();
+ data->type = message.type();
+ data->flags = flags;
+ data->sent = message.sent_time();
+ data->receive = message.received_time();
+ data->dispatch = Time::Now().ToInternalValue();
+ data->params = params;
+ data->message_name = message_name;
+ }
+}
+
+}
+
+#endif // IPC_MESSAGE_LOG_ENABLED
diff --git a/ipc/ipc_logging.h b/ipc/ipc_logging.h
new file mode 100644
index 0000000..364ad79
--- /dev/null
+++ b/ipc/ipc_logging.h
@@ -0,0 +1,112 @@
+// 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 IPC_IPC_LOGGING_H_
+#define IPC_IPC_LOGGING_H_
+
+#include "ipc/ipc_message.h" // For IPC_MESSAGE_LOG_ENABLED.
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+
+#include <vector>
+
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "base/singleton.h"
+#include "base/waitable_event_watcher.h"
+
+namespace IPC {
+
+class Message;
+
+// One instance per process. Needs to be created on the main thread (the UI
+// thread in the browser) but OnPreDispatchMessage/OnPostDispatchMessage
+// can be called on other threads.
+class Logging : public base::WaitableEventWatcher::Delegate,
+ public MessageLoop::DestructionObserver {
+ public:
+ // Implemented by consumers of log messages.
+ class Consumer {
+ public:
+ virtual void Log(const LogData& data) = 0;
+ };
+
+ void SetConsumer(Consumer* consumer);
+
+ ~Logging();
+ static Logging* current();
+
+ void Enable();
+ void Disable();
+ bool Enabled() const { return enabled_; }
+
+ // Called by child processes to give the logger object the channel to send
+ // logging data to the browser process.
+ void SetIPCSender(Message::Sender* sender);
+
+ // Called in the browser process when logging data from a child process is
+ // received.
+ void OnReceivedLoggingMessage(const Message& message);
+
+ void OnSendMessage(Message* message, const std::string& channel_id);
+ void OnPreDispatchMessage(const Message& message);
+ void OnPostDispatchMessage(const Message& message,
+ const std::string& channel_id);
+
+ // Returns the name of the logging enabled/disabled events so that the
+ // sandbox can add them to to the policy. If true, gets the name of the
+ // enabled event, if false, gets the name of the disabled event.
+ static std::wstring GetEventName(bool enabled);
+
+ // Like the *MsgLog functions declared for each message class, except this
+ // calls the correct one based on the message type automatically. Defined in
+ // ipc_logging.cc.
+ static void GetMessageText(uint16 type, std::wstring* name,
+ const Message* message, std::wstring* params);
+
+ // WaitableEventWatcher::Delegate implementation
+ void OnWaitableEventSignaled(base::WaitableEvent* event);
+
+ // MessageLoop::DestructionObserver implementation
+ void WillDestroyCurrentMessageLoop();
+
+ typedef void (*LogFunction)(uint16 type,
+ std::wstring* name,
+ const Message* msg,
+ std::wstring* params);
+
+ static void SetLoggerFunctions(LogFunction *functions);
+
+ private:
+ friend struct DefaultSingletonTraits<Logging>;
+ Logging();
+
+ std::wstring GetEventName(int browser_pid, bool enabled);
+ void OnSendLogs();
+ void Log(const LogData& data);
+
+ void RegisterWaitForEvent(bool enabled);
+
+ base::WaitableEventWatcher watcher_;
+
+ scoped_ptr<base::WaitableEvent> logging_event_on_;
+ scoped_ptr<base::WaitableEvent> logging_event_off_;
+ bool enabled_;
+
+ std::vector<LogData> queued_logs_;
+ bool queue_invoke_later_pending_;
+
+ Message::Sender* sender_;
+ MessageLoop* main_thread_;
+
+ Consumer* consumer_;
+
+ static LogFunction *log_function_mapping_;
+};
+
+} // namespace IPC
+
+#endif // IPC_MESSAGE_LOG_ENABLED
+
+#endif // IPC_IPC_LOGGING_H_
diff --git a/ipc/ipc_message.cc b/ipc/ipc_message.cc
new file mode 100644
index 0000000..84db00c
--- /dev/null
+++ b/ipc/ipc_message.cc
@@ -0,0 +1,124 @@
+// 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 "ipc/ipc_message.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include "ipc/file_descriptor_set_posix.h"
+#endif
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+Message::~Message() {
+}
+
+Message::Message()
+ : Pickle(sizeof(Header)) {
+ header()->routing = header()->type = header()->flags = 0;
+#if defined(OS_POSIX)
+ header()->num_fds = 0;
+#endif
+ InitLoggingVariables();
+}
+
+Message::Message(int32 routing_id, uint16 type, PriorityValue priority)
+ : Pickle(sizeof(Header)) {
+ header()->routing = routing_id;
+ header()->type = type;
+ header()->flags = priority;
+#if defined(OS_POSIX)
+ header()->num_fds = 0;
+#endif
+ InitLoggingVariables();
+}
+
+Message::Message(const char* data, int data_len) : Pickle(data, data_len) {
+ InitLoggingVariables();
+}
+
+Message::Message(const Message& other) : Pickle(other) {
+ InitLoggingVariables();
+#if defined(OS_POSIX)
+ file_descriptor_set_ = other.file_descriptor_set_;
+#endif
+}
+
+void Message::InitLoggingVariables() {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ received_time_ = 0;
+ dont_log_ = false;
+ log_data_ = NULL;
+#endif
+}
+
+Message& Message::operator=(const Message& other) {
+ *static_cast<Pickle*>(this) = other;
+#if defined(OS_POSIX)
+ file_descriptor_set_ = other.file_descriptor_set_;
+#endif
+ return *this;
+}
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+void Message::set_sent_time(int64 time) {
+ DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0);
+ header()->flags |= HAS_SENT_TIME_BIT;
+ WriteInt64(time);
+}
+
+int64 Message::sent_time() const {
+ if ((header()->flags & HAS_SENT_TIME_BIT) == 0)
+ return 0;
+
+ const char* data = end_of_payload();
+ data -= sizeof(int64);
+ return *(reinterpret_cast<const int64*>(data));
+}
+
+void Message::set_received_time(int64 time) const {
+ received_time_ = time;
+}
+#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.
+ 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(void** 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 = true;
+
+ return descriptor->fd >= 0;
+}
+
+void Message::EnsureFileDescriptorSet() {
+ if (file_descriptor_set_.get() == NULL)
+ file_descriptor_set_ = new FileDescriptorSet;
+}
+
+#endif
+
+} // namespace IPC
diff --git a/ipc/ipc_message.h b/ipc/ipc_message.h
new file mode 100644
index 0000000..6357c0b
--- /dev/null
+++ b/ipc/ipc_message.h
@@ -0,0 +1,279 @@
+// 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 IPC_IPC_MESSAGE_H__
+#define IPC_IPC_MESSAGE_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/pickle.h"
+
+#ifndef NDEBUG
+#define IPC_MESSAGE_LOG_ENABLED
+#endif
+
+#if defined(OS_POSIX)
+#include "base/ref_counted.h"
+#endif
+
+namespace base {
+class FileDescriptor;
+}
+
+class FileDescriptorSet;
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+class Channel;
+class Message;
+struct LogData;
+
+class Message : public Pickle {
+ public:
+ // Implemented by objects that can send IPC messages across a channel.
+ class Sender {
+ public:
+ virtual ~Sender() {}
+
+ // Sends the given IPC message. The implementor takes ownership of the
+ // given Message regardless of whether or not this method succeeds. This
+ // is done to make this method easier to use. Returns true on success and
+ // false otherwise.
+ virtual bool Send(Message* msg) = 0;
+ };
+
+ enum PriorityValue {
+ PRIORITY_LOW = 1,
+ PRIORITY_NORMAL,
+ PRIORITY_HIGH
+ };
+
+ virtual ~Message();
+
+ Message();
+
+ // Initialize a message with a user-defined type, priority value, and
+ // destination WebView ID.
+ Message(int32 routing_id, uint16 type, PriorityValue priority);
+
+ // Initializes a message from a const block of data. The data is not copied;
+ // instead the data is merely referenced by this message. Only const methods
+ // should be used on the message when initialized this way.
+ Message(const char* data, int data_len);
+
+ Message(const Message& other);
+ Message& operator=(const Message& other);
+
+ PriorityValue priority() const {
+ return static_cast<PriorityValue>(header()->flags & PRIORITY_MASK);
+ }
+
+ // True if this is a synchronous message.
+ bool is_sync() const {
+ return (header()->flags & SYNC_BIT) != 0;
+ }
+
+ // Set this on a reply to a synchronous message.
+ void set_reply() {
+ header()->flags |= REPLY_BIT;
+ }
+
+ bool is_reply() const {
+ return (header()->flags & REPLY_BIT) != 0;
+ }
+
+ // Set this on a reply to a synchronous message to indicate that no receiver
+ // was found.
+ void set_reply_error() {
+ header()->flags |= REPLY_ERROR_BIT;
+ }
+
+ bool is_reply_error() const {
+ return (header()->flags & REPLY_ERROR_BIT) != 0;
+ }
+
+ // Normally when a receiver gets a message and they're blocked on a
+ // synchronous message Send, they buffer a message. Setting this flag causes
+ // the receiver to be unblocked and the message to be dispatched immediately.
+ void set_unblock(bool unblock) {
+ if (unblock) {
+ header()->flags |= UNBLOCK_BIT;
+ } else {
+ header()->flags &= ~UNBLOCK_BIT;
+ }
+ }
+
+ bool should_unblock() const {
+ return (header()->flags & UNBLOCK_BIT) != 0;
+ }
+
+ // Tells the receiver that the caller is pumping messages while waiting
+ // for the result.
+ bool is_caller_pumping_messages() const {
+ return (header()->flags & PUMPING_MSGS_BIT) != 0;
+ }
+
+ uint16 type() const {
+ return header()->type;
+ }
+
+ int32 routing_id() const {
+ return header()->routing;
+ }
+
+ void set_routing_id(int32 new_id) {
+ header()->routing = new_id;
+ }
+
+ 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;
+ }
+
+ // Used for async messages with no parameters.
+ static void Log(const Message* msg, std::wstring* l) {
+ }
+
+ // Find the end of the message data that starts at range_start. Returns NULL
+ // if the entire message is not found in the given data range.
+ static const char* FindNext(const char* range_start, const char* range_end) {
+ return Pickle::FindNext(sizeof(Header), 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(void** iter, base::FileDescriptor* descriptor) const;
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ // Adds the outgoing time from Time::Now() at the end of the message and sets
+ // a bit to indicate that it's been added.
+ void set_sent_time(int64 time);
+ int64 sent_time() const;
+
+ void set_received_time(int64 time) const;
+ int64 received_time() const { return received_time_; }
+ void set_output_params(const std::wstring& op) const { output_params_ = op; }
+ const std::wstring& output_params() const { return output_params_; }
+ // The following four functions are needed so we can log sync messages with
+ // delayed replies. We stick the log data from the sent message into the
+ // reply message, so that when it's sent and we have the output parameters
+ // we can log it. As such, we set a flag on the sent message to not log it.
+ void set_sync_log_data(LogData* data) const { log_data_ = data; }
+ LogData* sync_log_data() const { return log_data_; }
+ void set_dont_log() const { dont_log_ = true; }
+ bool dont_log() const { return dont_log_; }
+#endif
+
+ protected:
+ friend class Channel;
+ friend class MessageReplyDeserializer;
+ friend class SyncMessage;
+
+ void set_sync() {
+ header()->flags |= SYNC_BIT;
+ }
+
+ // flags
+ enum {
+ PRIORITY_MASK = 0x0003,
+ SYNC_BIT = 0x0004,
+ REPLY_BIT = 0x0008,
+ REPLY_ERROR_BIT = 0x0010,
+ UNBLOCK_BIT = 0x0020,
+ PUMPING_MSGS_BIT= 0x0040,
+ HAS_SENT_TIME_BIT = 0x0080,
+ };
+
+#pragma pack(push, 2)
+ struct Header : Pickle::Header {
+ int32 routing; // ID of the view that this message is destined for
+ uint16 type; // specifies the user-defined message type
+ uint16 flags; // specifies control flags for the message
+#if defined(OS_POSIX)
+ uint32 num_fds; // the number of descriptors included with this message
+#endif
+ };
+#pragma pack(pop)
+
+ Header* header() {
+ return headerT<Header>();
+ }
+ const Header* header() const {
+ return headerT<Header>();
+ }
+
+ void InitLoggingVariables();
+
+#if defined(OS_POSIX)
+ // The set of file descriptors associated with this message.
+ scoped_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
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ // Used for logging.
+ mutable int64 received_time_;
+ mutable std::wstring output_params_;
+ mutable LogData* log_data_;
+ mutable bool dont_log_;
+#endif
+};
+
+//------------------------------------------------------------------------------
+
+} // namespace IPC
+
+enum SpecialRoutingIDs {
+ // indicates that we don't have a routing ID yet.
+ MSG_ROUTING_NONE = -2,
+
+ // 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 // IPC_IPC_MESSAGE_H__
diff --git a/ipc/ipc_message_macros.h b/ipc/ipc_message_macros.h
new file mode 100644
index 0000000..ba2a10a
--- /dev/null
+++ b/ipc/ipc_message_macros.h
@@ -0,0 +1,1150 @@
+// 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.
+
+// This header is meant to be included in multiple passes, hence no traditional
+// header guard.
+//
+// In the first pass, IPC_MESSAGE_MACROS_ENUMS should be defined, which will
+// create enums for each of the messages defined with the IPC_MESSAGE_* macros.
+//
+// In the second pass, either IPC_MESSAGE_MACROS_DEBUGSTRINGS or
+// IPC_MESSAGE_MACROS_CLASSES should be defined (if both, DEBUGSTRINGS takes
+// precedence). Only one .cc file should have DEBUGSTRINGS defined, as this
+// will create helper functions mapping message types to strings. Having
+// CLASSES defined will create classes for each of the messages defined with
+// the IPC_MESSAGE_* macros.
+//
+// "Sync" messages are just synchronous calls, the Send() call doesn't return
+// until a reply comes back. Input parameters are first (const TYPE&), and
+// To declare a sync message, use the IPC_SYNC_ macros. The numbers at the
+// end show how many input/output parameters there are (i.e. 1_2 is 1 in, 2
+// out). The caller does a Send([route id, ], in1, &out1, &out2).
+// The receiver's handler function will be
+// void OnSyncMessageName(const type1& in1, type2* out1, type3* out2)
+//
+//
+// A caller can also send a synchronous message, while the receiver can respond
+// at a later time. This is transparent from the sender's size. The receiver
+// needs to use a different handler that takes in a IPC::Message* as the output
+// type, stash the message, and when it has the data it can Send the message.
+//
+// Use the IPC_MESSAGE_HANDLER_DELAY_REPLY macro instead of IPC_MESSAGE_HANDLER
+// IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_SyncMessageName,
+// OnSyncMessageName)
+//
+// The handler function will look like:
+// void OnSyncMessageName(const type1& in1, IPC::Message* reply_msg);
+//
+// Receiver stashes the IPC::Message* pointer, and when it's ready, it does:
+// ViewHostMsg_SyncMessageName::WriteReplyParams(reply_msg, out1, out2);
+// Send(reply_msg);
+
+#include "ipc/ipc_message_utils.h"
+
+
+#ifndef MESSAGES_INTERNAL_FILE
+#error This file should only be included by X_messages.h, which needs to define MESSAGES_INTERNAL_FILE first.
+#endif
+
+// Trick scons and xcode into seeing the possible real dependencies since they
+// don't understand #include MESSAGES_INTERNAL_FILE. See http://crbug.com/7828
+#if 0
+#include "ipc/ipc_sync_message_unittest.h"
+#include "chrome/common/plugin_messages_internal.h"
+#include "chrome/common/render_messages_internal.h"
+#include "chrome/common/devtools_messages_internal.h"
+#include "chrome/test/automation/automation_messages_internal.h"
+#include "chrome/common/worker_messages_internal.h"
+#endif
+
+#ifndef IPC_MESSAGE_MACROS_INCLUDE_BLOCK
+#define IPC_MESSAGE_MACROS_INCLUDE_BLOCK
+
+// Multi-pass include of X_messages_internal.h. Preprocessor magic allows
+// us to use 1 header to define the enums and classes for our render messages.
+#define IPC_MESSAGE_MACROS_ENUMS
+#include MESSAGES_INTERNAL_FILE
+
+#define IPC_MESSAGE_MACROS_CLASSES
+#include MESSAGES_INTERNAL_FILE
+
+#ifdef IPC_MESSAGE_MACROS_LOG_ENABLED
+#define IPC_MESSAGE_MACROS_LOG
+#include MESSAGES_INTERNAL_FILE
+#endif
+
+#undef MESSAGES_INTERNAL_FILE
+#undef IPC_MESSAGE_MACROS_INCLUDE_BLOCK
+
+#endif
+
+
+// Undefine the macros from the previous pass (if any).
+#undef IPC_BEGIN_MESSAGES
+#undef IPC_END_MESSAGES
+#undef IPC_MESSAGE_CONTROL0
+#undef IPC_MESSAGE_CONTROL1
+#undef IPC_MESSAGE_CONTROL2
+#undef IPC_MESSAGE_CONTROL3
+#undef IPC_MESSAGE_CONTROL4
+#undef IPC_MESSAGE_CONTROL5
+#undef IPC_MESSAGE_ROUTED0
+#undef IPC_MESSAGE_ROUTED1
+#undef IPC_MESSAGE_ROUTED2
+#undef IPC_MESSAGE_ROUTED3
+#undef IPC_MESSAGE_ROUTED4
+#undef IPC_MESSAGE_ROUTED5
+#undef IPC_SYNC_MESSAGE_CONTROL0_0
+#undef IPC_SYNC_MESSAGE_CONTROL0_1
+#undef IPC_SYNC_MESSAGE_CONTROL0_2
+#undef IPC_SYNC_MESSAGE_CONTROL0_3
+#undef IPC_SYNC_MESSAGE_CONTROL1_0
+#undef IPC_SYNC_MESSAGE_CONTROL1_1
+#undef IPC_SYNC_MESSAGE_CONTROL1_2
+#undef IPC_SYNC_MESSAGE_CONTROL1_3
+#undef IPC_SYNC_MESSAGE_CONTROL2_0
+#undef IPC_SYNC_MESSAGE_CONTROL2_1
+#undef IPC_SYNC_MESSAGE_CONTROL2_2
+#undef IPC_SYNC_MESSAGE_CONTROL2_3
+#undef IPC_SYNC_MESSAGE_CONTROL3_1
+#undef IPC_SYNC_MESSAGE_CONTROL3_2
+#undef IPC_SYNC_MESSAGE_CONTROL3_3
+#undef IPC_SYNC_MESSAGE_CONTROL4_1
+#undef IPC_SYNC_MESSAGE_CONTROL4_2
+#undef IPC_SYNC_MESSAGE_ROUTED0_0
+#undef IPC_SYNC_MESSAGE_ROUTED0_1
+#undef IPC_SYNC_MESSAGE_ROUTED0_2
+#undef IPC_SYNC_MESSAGE_ROUTED0_3
+#undef IPC_SYNC_MESSAGE_ROUTED1_0
+#undef IPC_SYNC_MESSAGE_ROUTED1_1
+#undef IPC_SYNC_MESSAGE_ROUTED1_2
+#undef IPC_SYNC_MESSAGE_ROUTED1_3
+#undef IPC_SYNC_MESSAGE_ROUTED1_4
+#undef IPC_SYNC_MESSAGE_ROUTED2_0
+#undef IPC_SYNC_MESSAGE_ROUTED2_1
+#undef IPC_SYNC_MESSAGE_ROUTED2_2
+#undef IPC_SYNC_MESSAGE_ROUTED2_3
+#undef IPC_SYNC_MESSAGE_ROUTED3_0
+#undef IPC_SYNC_MESSAGE_ROUTED3_1
+#undef IPC_SYNC_MESSAGE_ROUTED3_2
+#undef IPC_SYNC_MESSAGE_ROUTED3_3
+#undef IPC_SYNC_MESSAGE_ROUTED4_0
+#undef IPC_SYNC_MESSAGE_ROUTED4_1
+#undef IPC_SYNC_MESSAGE_ROUTED4_2
+#undef IPC_SYNC_MESSAGE_ROUTED4_3
+
+#if defined(IPC_MESSAGE_MACROS_ENUMS)
+#undef IPC_MESSAGE_MACROS_ENUMS
+
+// TODO(jabdelmalek): we're using the lowest 12 bits of type for the message
+// id, and the highest 4 bits for the channel type. This constrains us to
+// 16 channel types (currently using 8) and 4K messages per type. Should
+// really make type be 32 bits, but then we break automation with older Chrome
+// builds..
+// Do label##PreStart so that automation messages keep the same id as before.
+#define IPC_BEGIN_MESSAGES(label) \
+ enum label##MsgType { \
+ label##Start = label##MsgStart << 12, \
+ label##PreStart = (label##MsgStart << 12) - 1,
+
+#define IPC_END_MESSAGES(label) \
+ label##End \
+ };
+
+#define IPC_MESSAGE_CONTROL0(msg_class) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL1(msg_class, type1) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED0(msg_class) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED1(msg_class, type1) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+// Message crackers and handlers.
+// Prefer to use the IPC_BEGIN_MESSAGE_MAP_EX to the older macros since they
+// allow you to detect when a message could not be de-serialized. Usage:
+//
+// void MyClass::OnMessageReceived(const IPC::Message& msg) {
+// bool msg_is_good = false;
+// IPC_BEGIN_MESSAGE_MAP_EX(MyClass, msg, msg_is_good)
+// IPC_MESSAGE_HANDLER(MsgClassOne, OnMsgClassOne)
+// ...more handlers here ...
+// IPC_MESSAGE_HANDLER(MsgClassTen, OnMsgClassTen)
+// IPC_END_MESSAGE_MAP_EX()
+// if (!msg_is_good) {
+// // Signal error here or terminate offending process.
+// }
+// }
+
+#define IPC_DEFINE_MESSAGE_MAP(class_name) \
+void class_name::OnMessageReceived(const IPC::Message& msg) \
+ IPC_BEGIN_MESSAGE_MAP(class_name, msg)
+
+#define IPC_BEGIN_MESSAGE_MAP_EX(class_name, msg, msg_is_ok) \
+ { \
+ typedef class_name _IpcMessageHandlerClass; \
+ const IPC::Message& ipc_message__ = msg; \
+ bool& msg_is_ok__ = msg_is_ok; \
+ switch (ipc_message__.type()) { \
+
+#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \
+ { \
+ typedef class_name _IpcMessageHandlerClass; \
+ const IPC::Message& ipc_message__ = msg; \
+ bool msg_is_ok__ = true; \
+ switch (ipc_message__.type()) { \
+
+#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func) \
+ case msg_class::ID: \
+ msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, &member_func); \
+ break;
+
+#define IPC_MESSAGE_HANDLER(msg_class, member_func) \
+ IPC_MESSAGE_FORWARD(msg_class, this, _IpcMessageHandlerClass::member_func)
+
+#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \
+ case msg_class::ID: \
+ msg_class::DispatchDelayReply(&ipc_message__, obj, &member_func); \
+ break;
+
+#define IPC_MESSAGE_HANDLER_DELAY_REPLY(msg_class, member_func) \
+ IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, this, _IpcMessageHandlerClass::member_func)
+
+#define IPC_MESSAGE_HANDLER_GENERIC(msg_class, code) \
+ case msg_class::ID: \
+ code; \
+ break;
+
+#define IPC_REPLY_HANDLER(func) \
+ case IPC_REPLY_ID: \
+ func(ipc_message__); \
+ break;
+
+
+#define IPC_MESSAGE_UNHANDLED(code) \
+ default: \
+ code; \
+ break;
+
+#define IPC_MESSAGE_UNHANDLED_ERROR() \
+ IPC_MESSAGE_UNHANDLED(NOTREACHED() << \
+ "Invalid message with type = " << \
+ ipc_message__.type())
+
+#define IPC_END_MESSAGE_MAP() \
+ DCHECK(msg_is_ok__); \
+ } \
+}
+
+#define IPC_END_MESSAGE_MAP_EX() \
+ } \
+}
+
+#elif defined(IPC_MESSAGE_MACROS_LOG)
+#undef IPC_MESSAGE_MACROS_LOG
+
+#ifndef IPC_LOG_TABLE_CREATED
+#define IPC_LOG_TABLE_CREATED
+typedef void (*LogFunction)(uint16 type,
+ std::wstring* name,
+ const IPC::Message* msg,
+ std::wstring* params);
+
+LogFunction g_log_function_mapping[LastMsgIndex];
+#endif
+
+
+#define IPC_BEGIN_MESSAGES(label) \
+ void label##MsgLog(uint16 type, std::wstring* name, const IPC::Message* msg, std::wstring* params) { \
+ switch (type) {
+
+#define IPC_END_MESSAGES(label) \
+ default: \
+ if (name) \
+ *name = L"[UNKNOWN " L ## #label L" MSG"; \
+ } \
+ } \
+ class LoggerRegisterHelper##label { \
+ public: \
+ LoggerRegisterHelper##label() { \
+ g_log_function_mapping[label##MsgStart] = label##MsgLog; \
+ } \
+ }; \
+ LoggerRegisterHelper##label g_LoggerRegisterHelper##label;
+
+#define IPC_MESSAGE_LOG(msg_class) \
+ case msg_class##__ID: \
+ if (name) \
+ *name = L ## #msg_class; \
+ if (msg && params) \
+ msg_class::Log(msg, params); \
+ break;
+
+#define IPC_MESSAGE_CONTROL0(msg_class) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL1(msg_class, type1) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED0(msg_class) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED1(msg_class, type1) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#elif defined(IPC_MESSAGE_MACROS_CLASSES)
+#undef IPC_MESSAGE_MACROS_CLASSES
+
+#define IPC_BEGIN_MESSAGES(label)
+#define IPC_END_MESSAGES(label)
+
+#define IPC_MESSAGE_CONTROL0(msg_class) \
+ class msg_class : public IPC::Message { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class() \
+ : IPC::Message(MSG_ROUTING_CONTROL, \
+ ID, \
+ PRIORITY_NORMAL) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL1(msg_class, type1) \
+ class msg_class : public IPC::MessageWithTuple< Tuple1<type1> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1) \
+ : IPC::MessageWithTuple< Tuple1<type1> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeRefTuple(arg1)) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \
+ class msg_class : public IPC::MessageWithTuple< Tuple2<type1, type2> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1, const type2& arg2) \
+ : IPC::MessageWithTuple< Tuple2<type1, type2> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeRefTuple(arg1, arg2)) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple3<type1, type2, type3> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1, const type2& arg2, const type3& arg3) \
+ : IPC::MessageWithTuple< Tuple3<type1, type2, type3> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeRefTuple(arg1, arg2, arg3)) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1, const type2& arg2, const type3& arg3, \
+ const type4& arg4) \
+ : IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4)) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1, const type2& arg2, \
+ const type3& arg3, const type4& arg4, const type5& arg5) \
+ : IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4, arg5)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED0(msg_class) \
+ class msg_class : public IPC::Message { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id) \
+ : IPC::Message(routing_id, ID, PRIORITY_NORMAL) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED1(msg_class, type1) \
+ class msg_class : public IPC::MessageWithTuple< Tuple1<type1> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1) \
+ : IPC::MessageWithTuple< Tuple1<type1> >(routing_id, ID, \
+ MakeRefTuple(arg1)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \
+ class msg_class : public IPC::MessageWithTuple< Tuple2<type1, type2> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2) \
+ : IPC::MessageWithTuple< Tuple2<type1, type2> >( \
+ routing_id, ID, MakeRefTuple(arg1, arg2)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple3<type1, type2, type3> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \
+ const type3& arg3) \
+ : IPC::MessageWithTuple< Tuple3<type1, type2, type3> >( \
+ routing_id, ID, MakeRefTuple(arg1, arg2, arg3)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \
+ const type3& arg3, const type4& arg4) \
+ : IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> >( \
+ routing_id, ID, MakeRefTuple(arg1, arg2, arg3, arg4)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \
+ const type3& arg3, const type4& arg4, const type5& arg5) \
+ : IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> >( \
+ routing_id, ID, MakeRefTuple(arg1, arg2, arg3, arg4, arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \
+ class msg_class : public IPC::MessageWithReply<Tuple0, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class() \
+ : IPC::MessageWithReply<Tuple0, Tuple0 >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \
+ class msg_class : public IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(type1_out* arg1) \
+ : IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(), MakeRefTuple(*arg1)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(type1_out* arg1, type2_out* arg2) \
+ : IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(), MakeRefTuple(*arg1, *arg2)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple0, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(type1_out* arg1, type2_out* arg2, type3_out* arg3) \
+ : IPC::MessageWithReply<Tuple0, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(), MakeRefTuple(*arg1, *arg2, *arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, Tuple0 >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, type1_out* arg2) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, Tuple1<type1_out&> >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1), MakeRefTuple(*arg2)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, type1_out* arg2, type2_out* arg3) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, Tuple2<type1_out&, type2_out&> >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1), MakeRefTuple(*arg2, *arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeRefTuple(arg1), MakeRefTuple(*arg2, *arg3, *arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1, arg2), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1, arg2), MakeRefTuple(*arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4, type3_out* arg5) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeRefTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple1<type1_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1, arg2, arg3), MakeRefTuple(*arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5, type3_out* arg6) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeRefTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5, *arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg6) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple1<type1_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg5, type2_out* arg6) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \
+ class msg_class : public IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, type1_out* arg1) \
+ : IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> >( \
+ routing_id, ID, \
+ MakeTuple(), MakeRefTuple(*arg1)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \
+ class msg_class : public IPC::MessageWithReply<Tuple0, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id) \
+ : IPC::MessageWithReply<Tuple0, Tuple0 >( \
+ routing_id, ID, \
+ MakeTuple(), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, type1_out* arg1, type2_out* arg2) \
+ : IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> >( \
+ routing_id, ID, \
+ MakeTuple(), MakeRefTuple(*arg1, *arg2)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple0, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, type1_out* arg1, type2_out* arg2, type3_out* arg3) \
+ : IPC::MessageWithReply<Tuple0, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeTuple(), MakeRefTuple(*arg1, *arg2, *arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, Tuple0 >( \
+ routing_id, ID, \
+ MakeRefTuple(arg1), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, type1_out* arg2) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, Tuple1<type1_out&> >( \
+ routing_id, ID, \
+ MakeRefTuple(arg1), MakeRefTuple(*arg2)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, Tuple2<type1_out&, type2_out&> >( \
+ routing_id, ID, \
+ MakeRefTuple(arg1), MakeRefTuple(*arg2, *arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1), MakeRefTuple(*arg2, *arg3, *arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple1<type1_in>, \
+ Tuple4<type1_out&, type2_out&, type3_out&, type4_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4, type4_out* arg5) \
+ : IPC::MessageWithReply<Tuple1<type1_in>, \
+ Tuple4<type1_out&, type2_out&, type3_out&, type4_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1), MakeRefTuple(*arg2, *arg3, *arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 >( \
+ routing_id, ID, \
+ MakeRefTuple(arg1, arg2), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> >( \
+ routing_id, ID, \
+ MakeRefTuple(arg1, arg2), MakeRefTuple(*arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple2<type1_out&, type2_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4, type3_out* arg5) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, Tuple0>( \
+ routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple1<type1_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3), MakeRefTuple(*arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple2<type1_out&, type2_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5, type3_out* arg6) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5, *arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple0 >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg6) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple1<type1_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg5, type2_out* arg6) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple2<type1_out&, type2_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg5, type2_out* arg6, type3_out* arg7) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6, *arg7)) {} \
+ };
+
+#endif // #if defined()
diff --git a/ipc/ipc_message_unittest.cc b/ipc/ipc_message_unittest.cc
new file mode 100644
index 0000000..36a3229
--- /dev/null
+++ b/ipc/ipc_message_unittest.cc
@@ -0,0 +1,67 @@
+// 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 <string.h>
+
+#include "base/scoped_ptr.h"
+#include "base/values.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_message_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(IPCMessageTest, ListValue) {
+ ListValue input;
+ input.Set(0, Value::CreateRealValue(42.42));
+ input.Set(1, Value::CreateStringValue("forty"));
+ input.Set(2, Value::CreateNullValue());
+
+ IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
+ IPC::WriteParam(&msg, input);
+
+ ListValue output;
+ void* iter = NULL;
+ EXPECT_TRUE(IPC::ReadParam(&msg, &iter, &output));
+
+ EXPECT_TRUE(input.Equals(&output));
+
+ // Also test the corrupt case.
+ IPC::Message bad_msg(1, 2, IPC::Message::PRIORITY_NORMAL);
+ bad_msg.WriteInt(99);
+ iter = NULL;
+ EXPECT_FALSE(IPC::ReadParam(&bad_msg, &iter, &output));
+}
+
+TEST(IPCMessageTest, DictionaryValue) {
+ DictionaryValue input;
+ input.Set(L"null", Value::CreateNullValue());
+ input.Set(L"bool", Value::CreateBooleanValue(true));
+ input.Set(L"int", Value::CreateIntegerValue(42));
+
+ scoped_ptr<DictionaryValue> subdict(new DictionaryValue());
+ subdict->Set(L"str", Value::CreateStringValue("forty two"));
+ subdict->Set(L"bool", Value::CreateBooleanValue(false));
+
+ scoped_ptr<ListValue> sublist(new ListValue());
+ sublist->Set(0, Value::CreateRealValue(42.42));
+ sublist->Set(1, Value::CreateStringValue("forty"));
+ sublist->Set(2, Value::CreateStringValue("two"));
+ subdict->Set(L"list", sublist.release());
+
+ input.Set(L"dict", subdict.release());
+
+ IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL);
+ IPC::WriteParam(&msg, input);
+
+ DictionaryValue output;
+ void* iter = NULL;
+ EXPECT_TRUE(IPC::ReadParam(&msg, &iter, &output));
+
+ EXPECT_TRUE(input.Equals(&output));
+
+ // Also test the corrupt case.
+ IPC::Message bad_msg(1, 2, IPC::Message::PRIORITY_NORMAL);
+ bad_msg.WriteInt(99);
+ iter = NULL;
+ EXPECT_FALSE(IPC::ReadParam(&bad_msg, &iter, &output));
+}
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
new file mode 100644
index 0000000..ac188bf
--- /dev/null
+++ b/ipc/ipc_message_utils.cc
@@ -0,0 +1,235 @@
+// 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 "ipc/ipc_message_utils.h"
+
+#include "base/json_writer.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "base/values.h"
+
+namespace IPC {
+
+const int kMaxRecursionDepth = 100;
+
+// Value serialization
+
+static bool ReadValue(const Message* m, void** iter, Value** value,
+ int recursion);
+
+static void WriteValue(Message* m, const Value* value, int recursion) {
+ if (recursion > kMaxRecursionDepth) {
+ LOG(WARNING) << "Max recursion depth hit in WriteValue.";
+ return;
+ }
+
+ m->WriteInt(value->GetType());
+
+ switch (value->GetType()) {
+ case Value::TYPE_NULL:
+ break;
+ case Value::TYPE_BOOLEAN: {
+ bool val;
+ value->GetAsBoolean(&val);
+ WriteParam(m, val);
+ break;
+ }
+ case Value::TYPE_INTEGER: {
+ int val;
+ value->GetAsInteger(&val);
+ WriteParam(m, val);
+ break;
+ }
+ case Value::TYPE_REAL: {
+ double val;
+ value->GetAsReal(&val);
+ WriteParam(m, val);
+ break;
+ }
+ case Value::TYPE_STRING: {
+ std::string val;
+ value->GetAsString(&val);
+ WriteParam(m, val);
+ break;
+ }
+ case Value::TYPE_BINARY: {
+ NOTREACHED() << "Don't send BinaryValues over IPC.";
+ }
+ case Value::TYPE_DICTIONARY: {
+ const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
+
+ WriteParam(m, static_cast<int>(dict->GetSize()));
+
+ for (DictionaryValue::key_iterator it = dict->begin_keys();
+ it != dict->end_keys(); ++it) {
+ Value* subval;
+ if (dict->Get(*it, &subval)) {
+ WriteParam(m, *it);
+ WriteValue(m, subval, recursion + 1);
+ } else {
+ NOTREACHED() << "DictionaryValue iterators are filthy liars.";
+ }
+ }
+ break;
+ }
+ case Value::TYPE_LIST: {
+ const ListValue* list = static_cast<const ListValue*>(value);
+ WriteParam(m, static_cast<int>(list->GetSize()));
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ Value* subval;
+ if (list->Get(i, &subval)) {
+ WriteValue(m, subval, recursion + 1);
+ } else {
+ NOTREACHED() << "ListValue::GetSize is a filthy liar.";
+ }
+ }
+ break;
+ }
+ }
+}
+
+// Helper for ReadValue that reads a DictionaryValue into a pre-allocated
+// object.
+static bool ReadDictionaryValue(const Message* m, void** iter,
+ DictionaryValue* value, int recursion) {
+ int size;
+ if (!ReadParam(m, iter, &size))
+ return false;
+
+ for (int i = 0; i < size; ++i) {
+ std::wstring key;
+ Value* subval;
+ if (!ReadParam(m, iter, &key) ||
+ !ReadValue(m, iter, &subval, recursion + 1))
+ return false;
+ value->Set(key, subval);
+ }
+
+ return true;
+}
+
+// Helper for ReadValue that reads a ReadListValue into a pre-allocated
+// object.
+static bool ReadListValue(const Message* m, void** iter,
+ ListValue* value, int recursion) {
+ int size;
+ if (!ReadParam(m, iter, &size))
+ return false;
+
+ for (int i = 0; i < size; ++i) {
+ Value* subval;
+ if (!ReadValue(m, iter, &subval, recursion + 1))
+ return false;
+ value->Set(i, subval);
+ }
+
+ return true;
+}
+
+static bool ReadValue(const Message* m, void** iter, Value** value,
+ int recursion) {
+ if (recursion > kMaxRecursionDepth) {
+ LOG(WARNING) << "Max recursion depth hit in ReadValue.";
+ return false;
+ }
+
+ int type;
+ if (!ReadParam(m, iter, &type))
+ return false;
+
+ switch (type) {
+ case Value::TYPE_NULL:
+ *value = Value::CreateNullValue();
+ break;
+ case Value::TYPE_BOOLEAN: {
+ bool val;
+ if (!ReadParam(m, iter, &val))
+ return false;
+ *value = Value::CreateBooleanValue(val);
+ break;
+ }
+ case Value::TYPE_INTEGER: {
+ int val;
+ if (!ReadParam(m, iter, &val))
+ return false;
+ *value = Value::CreateIntegerValue(val);
+ break;
+ }
+ case Value::TYPE_REAL: {
+ double val;
+ if (!ReadParam(m, iter, &val))
+ return false;
+ *value = Value::CreateRealValue(val);
+ break;
+ }
+ case Value::TYPE_STRING: {
+ std::string val;
+ if (!ReadParam(m, iter, &val))
+ return false;
+ *value = Value::CreateStringValue(val);
+ break;
+ }
+ case Value::TYPE_BINARY: {
+ NOTREACHED() << "Don't send BinaryValues over IPC.";
+ break;
+ }
+ case Value::TYPE_DICTIONARY: {
+ scoped_ptr<DictionaryValue> val(new DictionaryValue());
+ if (!ReadDictionaryValue(m, iter, val.get(), recursion))
+ return false;
+ *value = val.release();
+ break;
+ }
+ case Value::TYPE_LIST: {
+ scoped_ptr<ListValue> val(new ListValue());
+ if (!ReadListValue(m, iter, val.get(), recursion))
+ return false;
+ *value = val.release();
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+void ParamTraits<DictionaryValue>::Write(Message* m, const param_type& p) {
+ WriteValue(m, &p, 0);
+}
+
+bool ParamTraits<DictionaryValue>::Read(
+ const Message* m, void** iter, param_type* r) {
+ int type;
+ if (!ReadParam(m, iter, &type) || type != Value::TYPE_DICTIONARY)
+ return false;
+
+ return ReadDictionaryValue(m, iter, r, 0);
+}
+
+void ParamTraits<DictionaryValue>::Log(const param_type& p, std::wstring* l) {
+ std::string json;
+ JSONWriter::Write(&p, false, &json);
+ l->append(UTF8ToWide(json));
+}
+
+void ParamTraits<ListValue>::Write(Message* m, const param_type& p) {
+ WriteValue(m, &p, 0);
+}
+
+bool ParamTraits<ListValue>::Read(
+ const Message* m, void** iter, param_type* r) {
+ int type;
+ if (!ReadParam(m, iter, &type) || type != Value::TYPE_LIST)
+ return false;
+
+ return ReadListValue(m, iter, r, 0);
+}
+
+void ParamTraits<ListValue>::Log(const param_type& p, std::wstring* l) {
+ std::string json;
+ JSONWriter::Write(&p, false, &json);
+ l->append(UTF8ToWide(json));
+}
+} // namespace IPC
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
new file mode 100644
index 0000000..3fd8123
--- /dev/null
+++ b/ipc/ipc_message_utils.h
@@ -0,0 +1,1221 @@
+// 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 IPC_IPC_MESSAGE_UTILS_H_
+#define IPC_IPC_MESSAGE_UTILS_H_
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "base/file_path.h"
+#include "base/format_macros.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/tuple.h"
+#include "base/values.h"
+#if defined(OS_POSIX)
+#include "ipc/file_descriptor_set_posix.h"
+#endif
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_sync_message.h"
+
+// Used by IPC_BEGIN_MESSAGES so that each message class starts from a unique
+// base. Messages have unique IDs across channels in order for the IPC logging
+// code to figure out the message class from its ID.
+enum IPCMessageStart {
+ // By using a start value of 0 for automation messages, we keep backward
+ // compatibility with old builds.
+ AutomationMsgStart = 0,
+ ViewMsgStart,
+ ViewHostMsgStart,
+ PluginProcessMsgStart,
+ PluginProcessHostMsgStart,
+ PluginMsgStart,
+ PluginHostMsgStart,
+ NPObjectMsgStart,
+ TestMsgStart,
+ DevToolsAgentMsgStart,
+ DevToolsClientMsgStart,
+ WorkerProcessMsgStart,
+ WorkerProcessHostMsgStart,
+ WorkerMsgStart,
+ WorkerHostMsgStart,
+ // NOTE: When you add a new message class, also update
+ // IPCStatusView::IPCStatusView to ensure logging works.
+ // NOTE: this enum is used by IPC_MESSAGE_MACRO to generate a unique message
+ // id. Only 4 bits are used for the message type, so if this enum needs more
+ // than 16 entries, that code needs to be updated.
+ LastMsgIndex
+};
+
+COMPILE_ASSERT(LastMsgIndex <= 16, need_to_update_IPC_MESSAGE_MACRO);
+
+
+namespace IPC {
+
+//-----------------------------------------------------------------------------
+// An iterator class for reading the fields contained within a Message.
+
+class MessageIterator {
+ public:
+ explicit MessageIterator(const Message& m) : msg_(m), iter_(NULL) {
+ }
+ 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;
+ }
+ const void NextData(const char** data, int* length) const {
+ if (!msg_.ReadData(&iter_, data, length)) {
+ NOTREACHED();
+ }
+ }
+ private:
+ const Message& msg_;
+ mutable void* iter_;
+};
+
+//-----------------------------------------------------------------------------
+// ParamTraits specializations, etc.
+
+template <class P> struct ParamTraits {};
+
+template <class P>
+static inline void WriteParam(Message* m, const P& p) {
+ ParamTraits<P>::Write(m, p);
+}
+
+template <class P>
+static inline bool WARN_UNUSED_RESULT ReadParam(const Message* m, void** iter,
+ P* p) {
+ return ParamTraits<P>::Read(m, iter, p);
+}
+
+template <class P>
+static inline void LogParam(const P& p, std::wstring* l) {
+ ParamTraits<P>::Log(p, l);
+}
+
+template <>
+struct ParamTraits<bool> {
+ typedef bool param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteBool(p);
+ }
+ static bool Read(const Message* m, void** 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 ParamTraits<int> {
+ typedef int param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt(p);
+ }
+ static bool Read(const Message* m, void** 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 ParamTraits<long> {
+ typedef long param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteLong(p);
+ }
+ static bool Read(const Message* m, void** 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));
+ }
+};
+
+#if defined(OS_LINUX) || defined(OS_WIN)
+// On Linux, unsigned long is used for serializing X window ids.
+// On Windows, it's used for serializing process ids.
+// On Mac, it conflicts with some other definition.
+template <>
+struct ParamTraits<unsigned long> {
+ typedef unsigned long param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteLong(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ long read_output;
+ if (!m->ReadLong(iter, &read_output))
+ return false;
+ *r = static_cast<unsigned long>(read_output);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%ul", p));
+ }
+};
+#endif
+
+template <>
+struct ParamTraits<size_t> {
+ typedef size_t param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteSize(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadSize(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%u", p));
+ }
+};
+
+#if defined(OS_MACOSX)
+// On Linux size_t & uint32 can be the same type.
+// TODO(playmobil): Fix compilation if this is not the case.
+template <>
+struct ParamTraits<uint32> {
+ typedef uint32 param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteUInt32(p);
+ }
+ static bool Read(const Message* m, void** 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));
+ }
+};
+#endif // defined(OS_MACOSX)
+
+template <>
+struct ParamTraits<int64> {
+ typedef int64 param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt64(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadInt64(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%" WidePRId64, p));
+ }
+};
+
+template <>
+struct ParamTraits<uint64> {
+ typedef uint64 param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt64(static_cast<int64>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadInt64(iter, reinterpret_cast<int64*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%" WidePRId64, p));
+ }
+};
+
+template <>
+struct ParamTraits<double> {
+ typedef double param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(param_type)) {
+ memcpy(r, data, sizeof(param_type));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"e", p));
+ }
+};
+
+template <>
+struct ParamTraits<wchar_t> {
+ typedef wchar_t param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(param_type)) {
+ memcpy(r, data, sizeof(param_type));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%lc", p));
+ }
+};
+
+template <>
+struct ParamTraits<base::Time> {
+ typedef base::Time param_type;
+ static void Write(Message* m, const param_type& p) {
+ ParamTraits<int64>::Write(m, p.ToInternalValue());
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int64 value;
+ if (!ParamTraits<int64>::Read(m, iter, &value))
+ return false;
+ *r = base::Time::FromInternalValue(value);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ ParamTraits<int64>::Log(p.ToInternalValue(), l);
+ }
+};
+
+#if defined(OS_WIN)
+template <>
+struct ParamTraits<LOGFONT> {
+ typedef LOGFONT param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(LOGFONT));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(LOGFONT)) {
+ memcpy(r, data, sizeof(LOGFONT));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"<LOGFONT>"));
+ }
+};
+
+template <>
+struct ParamTraits<MSG> {
+ typedef MSG param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(MSG));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(MSG)) {
+ memcpy(r, data, sizeof(MSG));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+};
+#endif // defined(OS_WIN)
+
+template <>
+struct ParamTraits<DictionaryValue> {
+ typedef DictionaryValue param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::wstring* l);
+};
+
+template <>
+struct ParamTraits<ListValue> {
+ typedef ListValue param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::wstring* l);
+};
+
+template <>
+struct ParamTraits<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, void** 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 ParamTraits<std::vector<unsigned char> > {
+ typedef std::vector<unsigned char> param_type;
+ static void Write(Message* m, const param_type& p) {
+ if (p.size() == 0) {
+ m->WriteData(NULL, 0);
+ } else {
+ m->WriteData(reinterpret_cast<const char*>(&p.front()),
+ static_cast<int>(p.size()));
+ }
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ if (!m->ReadData(iter, &data, &data_size) || data_size < 0)
+ return false;
+ r->resize(data_size);
+ if (data_size)
+ memcpy(&r->front(), data, data_size);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ for (size_t i = 0; i < p.size(); ++i)
+ l->push_back(p[i]);
+ }
+};
+
+template <>
+struct ParamTraits<std::vector<char> > {
+ typedef std::vector<char> param_type;
+ static void Write(Message* m, const param_type& p) {
+ if (p.size() == 0) {
+ m->WriteData(NULL, 0);
+ } else {
+ m->WriteData(&p.front(), static_cast<int>(p.size()));
+ }
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ if (!m->ReadData(iter, &data, &data_size) || data_size < 0)
+ return false;
+ r->resize(data_size);
+ if (data_size)
+ memcpy(&r->front(), data, data_size);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ for (size_t i = 0; i < p.size(); ++i)
+ l->push_back(p[i]);
+ }
+};
+
+template <class P>
+struct ParamTraits<std::vector<P> > {
+ typedef std::vector<P> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, static_cast<int>(p.size()));
+ for (size_t i = 0; i < p.size(); i++)
+ WriteParam(m, p[i]);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int size;
+ if (!m->ReadLength(iter, &size))
+ return false;
+ // Resizing beforehand is not safe, see BUG 1006367 for details.
+ if (m->IteratorHasRoomFor(*iter, size * sizeof(P))) {
+ r->resize(size);
+ for (int i = 0; i < size; i++) {
+ if (!ReadParam(m, iter, &(*r)[i]))
+ return false;
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ P element;
+ if (!ReadParam(m, iter, &element))
+ return false;
+ r->push_back(element);
+ }
+ }
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ for (size_t i = 0; i < p.size(); ++i) {
+ if (i != 0)
+ l->append(L" ");
+
+ LogParam((p[i]), l);
+ }
+ }
+};
+
+template <class K, class V>
+struct ParamTraits<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, void** 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>");
+ }
+};
+
+
+template <>
+struct ParamTraits<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, void** iter, param_type* r) {
+ return m->ReadWString(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(p);
+ }
+};
+
+// If WCHAR_T_IS_UTF16 is defined, then string16 is a std::wstring so we don't
+// need this trait.
+#if !defined(WCHAR_T_IS_UTF16)
+template <>
+struct ParamTraits<string16> {
+ typedef string16 param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteString16(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadString16(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(UTF16ToWide(p));
+ }
+};
+#endif
+
+// and, a few more useful types...
+#if defined(OS_WIN)
+template <>
+struct ParamTraits<HANDLE> {
+ typedef HANDLE param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(intptr_t));
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraits<HCURSOR> {
+ typedef HCURSOR param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(intptr_t));
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraits<HACCEL> {
+ typedef HACCEL param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(intptr_t));
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+};
+
+template <>
+struct ParamTraits<POINT> {
+ typedef POINT param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt(p.x);
+ m->WriteInt(p.y);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int x, y;
+ if (!m->ReadInt(iter, &x) || !m->ReadInt(iter, &y))
+ return false;
+ r->x = x;
+ r->y = y;
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"(%d, %d)", p.x, p.y));
+ }
+};
+#endif // defined(OS_WIN)
+
+template <>
+struct ParamTraits<FilePath> {
+ typedef FilePath param_type;
+ static void Write(Message* m, const param_type& p) {
+ ParamTraits<FilePath::StringType>::Write(m, p.value());
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ FilePath::StringType value;
+ if (!ParamTraits<FilePath::StringType>::Read(m, iter, &value))
+ return false;
+ *r = FilePath(value);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ ParamTraits<FilePath::StringType>::Log(p.value(), l);
+ }
+};
+
+#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 ParamTraits<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();
+ }
+ }
+ static bool Read(const Message* m, void** 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)
+
+// A ChannelHandle is basically a platform-inspecific wrapper around the
+// fact that IPC endpoints are handled specially on POSIX. See above comments
+// on FileDescriptor for more background.
+template<>
+struct ParamTraits<IPC::ChannelHandle> {
+ typedef ChannelHandle param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.name);
+#if defined(OS_POSIX)
+ WriteParam(m, p.socket);
+#endif
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return ReadParam(m, iter, &r->name)
+#if defined(OS_POSIX)
+ && ReadParam(m, iter, &r->socket)
+#endif
+ ;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(ASCIIToWide(StringPrintf("ChannelHandle(%s", p.name.c_str())));
+#if defined(OS_POSIX)
+ ParamTraits<base::FileDescriptor>::Log(p.socket, l);
+#endif
+ l->append(L")");
+ }
+};
+
+#if defined(OS_WIN)
+template <>
+struct ParamTraits<XFORM> {
+ typedef XFORM param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(XFORM));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(XFORM)) {
+ memcpy(r, data, sizeof(XFORM));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(L"<XFORM>");
+ }
+};
+#endif // defined(OS_WIN)
+
+struct LogData {
+ std::string channel;
+ int32 routing_id;
+ uint16 type;
+ std::wstring flags;
+ int64 sent; // Time that the message was sent (i.e. at Send()).
+ int64 receive; // Time before it was dispatched (i.e. before calling
+ // OnMessageReceived).
+ int64 dispatch; // Time after it was dispatched (i.e. after calling
+ // OnMessageReceived).
+ std::wstring message_name;
+ std::wstring params;
+};
+
+template <>
+struct ParamTraits<LogData> {
+ typedef LogData param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.channel);
+ WriteParam(m, p.routing_id);
+ WriteParam(m, static_cast<int>(p.type));
+ WriteParam(m, p.flags);
+ WriteParam(m, p.sent);
+ WriteParam(m, p.receive);
+ WriteParam(m, p.dispatch);
+ WriteParam(m, p.params);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int type;
+ bool result =
+ ReadParam(m, iter, &r->channel) &&
+ ReadParam(m, iter, &r->routing_id);
+ ReadParam(m, iter, &type) &&
+ ReadParam(m, iter, &r->flags) &&
+ ReadParam(m, iter, &r->sent) &&
+ ReadParam(m, iter, &r->receive) &&
+ ReadParam(m, iter, &r->dispatch) &&
+ ReadParam(m, iter, &r->params);
+ r->type = static_cast<uint16>(type);
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ // Doesn't make sense to implement this!
+ }
+};
+
+
+template <>
+struct ParamTraits<Message> {
+ static void Write(Message* m, const Message& p) {
+ m->WriteInt(p.size());
+ m->WriteData(reinterpret_cast<const char*>(p.data()), p.size());
+ }
+ static bool Read(const Message* m, void** iter, Message* r) {
+ int size;
+ if (!m->ReadInt(iter, &size))
+ return false;
+ const char* data;
+ if (!m->ReadData(iter, &data, &size))
+ return false;
+ *r = Message(data, size);
+ return true;
+ }
+ static void Log(const Message& p, std::wstring* l) {
+ l->append(L"<IPC::Message>");
+ }
+};
+
+template <>
+struct ParamTraits<Tuple0> {
+ typedef Tuple0 param_type;
+ static void Write(Message* m, const param_type& p) {
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ }
+};
+
+template <class A>
+struct ParamTraits< Tuple1<A> > {
+ typedef Tuple1<A> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return ReadParam(m, iter, &r->a);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ }
+};
+
+template <class A, class B>
+struct ParamTraits< Tuple2<A, B> > {
+ typedef Tuple2<A, B> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ WriteParam(m, p.b);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->a) &&
+ ReadParam(m, iter, &r->b));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ }
+};
+
+template <class A, class B, class C>
+struct ParamTraits< Tuple3<A, B, C> > {
+ typedef Tuple3<A, B, C> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ WriteParam(m, p.b);
+ WriteParam(m, p.c);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->a) &&
+ ReadParam(m, iter, &r->b) &&
+ ReadParam(m, iter, &r->c));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ l->append(L", ");
+ LogParam(p.c, l);
+ }
+};
+
+template <class A, class B, class C, class D>
+struct ParamTraits< Tuple4<A, B, C, D> > {
+ typedef Tuple4<A, B, C, D> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ WriteParam(m, p.b);
+ WriteParam(m, p.c);
+ WriteParam(m, p.d);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->a) &&
+ ReadParam(m, iter, &r->b) &&
+ ReadParam(m, iter, &r->c) &&
+ ReadParam(m, iter, &r->d));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ l->append(L", ");
+ LogParam(p.c, l);
+ l->append(L", ");
+ LogParam(p.d, l);
+ }
+};
+
+template <class A, class B, class C, class D, class E>
+struct ParamTraits< Tuple5<A, B, C, D, E> > {
+ typedef Tuple5<A, B, C, D, E> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ WriteParam(m, p.b);
+ WriteParam(m, p.c);
+ WriteParam(m, p.d);
+ WriteParam(m, p.e);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->a) &&
+ ReadParam(m, iter, &r->b) &&
+ ReadParam(m, iter, &r->c) &&
+ ReadParam(m, iter, &r->d) &&
+ ReadParam(m, iter, &r->e));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ l->append(L", ");
+ LogParam(p.c, l);
+ l->append(L", ");
+ LogParam(p.d, l);
+ l->append(L", ");
+ LogParam(p.e, l);
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Generic message subclasses
+
+// Used for asynchronous messages.
+template <class ParamType>
+class MessageWithTuple : public Message {
+ public:
+ typedef ParamType Param;
+ typedef typename ParamType::ParamTuple RefParam;
+
+ MessageWithTuple(int32 routing_id, uint16 type, const RefParam& p)
+ : Message(routing_id, type, PRIORITY_NORMAL) {
+ WriteParam(this, p);
+ }
+
+ static bool Read(const Message* msg, Param* p) {
+ void* iter = NULL;
+ if (ReadParam(msg, &iter, p))
+ return true;
+ NOTREACHED() << "Error deserializing message " << msg->type();
+ return false;
+ }
+
+ // Generic dispatcher. Should cover most cases.
+ template<class T, class Method>
+ static bool Dispatch(const Message* msg, T* obj, Method func) {
+ Param p;
+ if (Read(msg, &p)) {
+ DispatchToMethod(obj, func, p);
+ return true;
+ }
+ return false;
+ }
+
+ // The following dispatchers exist for the case where the callback function
+ // needs the message as well. They assume that "Param" is a type of Tuple
+ // (except the one arg case, as there is no Tuple1).
+ template<class T, typename TA>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, typename TA, typename TB>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA, TB)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a, p.b);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, typename TA, typename TB, typename TC>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA, TB, TC)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a, p.b, p.c);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, typename TA, typename TB, typename TC, typename TD>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA, TB, TC, TD)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a, p.b, p.c, p.d);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, typename TA, typename TB, typename TC, typename TD,
+ typename TE>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA, TB, TC, TD, TE)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a, p.b, p.c, p.d, p.e);
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const Message* msg, std::wstring* l) {
+ Param p;
+ if (Read(msg, &p))
+ LogParam(p, l);
+ }
+
+ // Functions used to do manual unpacking. Only used by the automation code,
+ // these should go away once that code uses SyncChannel.
+ template<typename TA, typename TB>
+ static bool Read(const IPC::Message* msg, TA* a, TB* b) {
+ ParamType params;
+ if (!Read(msg, &params))
+ return false;
+ *a = params.a;
+ *b = params.b;
+ return true;
+ }
+
+ template<typename TA, typename TB, typename TC>
+ static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c) {
+ ParamType params;
+ if (!Read(msg, &params))
+ return false;
+ *a = params.a;
+ *b = params.b;
+ *c = params.c;
+ return true;
+ }
+
+ template<typename TA, typename TB, typename TC, typename TD>
+ static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d) {
+ ParamType params;
+ if (!Read(msg, &params))
+ return false;
+ *a = params.a;
+ *b = params.b;
+ *c = params.c;
+ *d = params.d;
+ return true;
+ }
+
+ template<typename TA, typename TB, typename TC, typename TD, typename TE>
+ static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d, TE* e) {
+ ParamType params;
+ if (!Read(msg, &params))
+ return false;
+ *a = params.a;
+ *b = params.b;
+ *c = params.c;
+ *d = params.d;
+ *e = params.e;
+ return true;
+ }
+};
+
+// This class assumes that its template argument is a RefTuple (a Tuple with
+// reference elements).
+template <class RefTuple>
+class ParamDeserializer : public MessageReplyDeserializer {
+ public:
+ explicit ParamDeserializer(const RefTuple& out) : out_(out) { }
+
+ bool SerializeOutputParameters(const IPC::Message& msg, void* iter) {
+ return ReadParam(&msg, &iter, &out_);
+ }
+
+ RefTuple out_;
+};
+
+// defined in ipc_logging.cc
+void GenerateLogData(const std::string& channel, const Message& message,
+ LogData* data);
+
+// Used for synchronous messages.
+template <class SendParamType, class ReplyParamType>
+class MessageWithReply : public SyncMessage {
+ public:
+ typedef SendParamType SendParam;
+ typedef typename SendParam::ParamTuple RefSendParam;
+ typedef ReplyParamType ReplyParam;
+
+ MessageWithReply(int32 routing_id, uint16 type,
+ const RefSendParam& send, const ReplyParam& reply)
+ : SyncMessage(routing_id, type, PRIORITY_NORMAL,
+ new ParamDeserializer<ReplyParam>(reply)) {
+ WriteParam(this, send);
+ }
+
+ static void Log(const Message* msg, std::wstring* l) {
+ if (msg->is_sync()) {
+ SendParam p;
+ void* iter = SyncMessage::GetDataIterator(msg);
+ if (ReadParam(msg, &iter, &p))
+ LogParam(p, l);
+
+#if defined(IPC_MESSAGE_LOG_ENABLED)
+ const std::wstring& output_params = msg->output_params();
+ if (!l->empty() && !output_params.empty())
+ l->append(L", ");
+
+ l->append(output_params);
+#endif
+ } else {
+ // This is an outgoing reply. Now that we have the output parameters, we
+ // can finally log the message.
+ typename ReplyParam::ValueTuple p;
+ void* iter = SyncMessage::GetDataIterator(msg);
+ if (ReadParam(msg, &iter, &p))
+ LogParam(p, l);
+ }
+ }
+
+ template<class T, class Method>
+ static bool Dispatch(const Message* msg, T* obj, Method func) {
+ SendParam send_params;
+ void* iter = GetDataIterator(msg);
+ Message* reply = GenerateReply(msg);
+ bool error;
+ if (ReadParam(msg, &iter, &send_params)) {
+ typename ReplyParam::ValueTuple reply_params;
+ DispatchToMethod(obj, func, send_params, &reply_params);
+ WriteParam(reply, reply_params);
+ error = false;
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (msg->received_time() != 0) {
+ std::wstring output_params;
+ LogParam(reply_params, &output_params);
+ msg->set_output_params(output_params);
+ }
+#endif
+ } else {
+ NOTREACHED() << "Error deserializing message " << msg->type();
+ reply->set_reply_error();
+ error = true;
+ }
+
+ obj->Send(reply);
+ return !error;
+ }
+
+ template<class T, class Method>
+ static bool DispatchDelayReply(const Message* msg, T* obj, Method func) {
+ SendParam send_params;
+ void* iter = GetDataIterator(msg);
+ Message* reply = GenerateReply(msg);
+ bool error;
+ if (ReadParam(msg, &iter, &send_params)) {
+ Tuple1<Message&> t = MakeRefTuple(*reply);
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (msg->sent_time()) {
+ // Don't log the sync message after dispatch, as we don't have the
+ // output parameters at that point. Instead, save its data and log it
+ // with the outgoing reply message when it's sent.
+ LogData* data = new LogData;
+ GenerateLogData("", *msg, data);
+ msg->set_dont_log();
+ reply->set_sync_log_data(data);
+ }
+#endif
+ DispatchToMethod(obj, func, send_params, &t);
+ error = false;
+ } else {
+ NOTREACHED() << "Error deserializing message " << msg->type();
+ reply->set_reply_error();
+ obj->Send(reply);
+ error = true;
+ }
+ return !error;
+ }
+
+ template<typename TA>
+ static void WriteReplyParams(Message* reply, TA a) {
+ ReplyParam p(a);
+ WriteParam(reply, p);
+ }
+
+ template<typename TA, typename TB>
+ static void WriteReplyParams(Message* reply, TA a, TB b) {
+ ReplyParam p(a, b);
+ WriteParam(reply, p);
+ }
+
+ template<typename TA, typename TB, typename TC>
+ static void WriteReplyParams(Message* reply, TA a, TB b, TC c) {
+ ReplyParam p(a, b, c);
+ WriteParam(reply, p);
+ }
+
+ template<typename TA, typename TB, typename TC, typename TD>
+ static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d) {
+ ReplyParam p(a, b, c, d);
+ WriteParam(reply, p);
+ }
+
+ template<typename TA, typename TB, typename TC, typename TD, typename TE>
+ static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d, TE e) {
+ ReplyParam p(a, b, c, d, e);
+ WriteParam(reply, p);
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace IPC
+
+#endif // IPC_IPC_MESSAGE_UTILS_H_
diff --git a/ipc/ipc_send_fds_test.cc b/ipc/ipc_send_fds_test.cc
new file mode 100644
index 0000000..5f399da
--- /dev/null
+++ b/ipc/ipc_send_fds_test.cc
@@ -0,0 +1,187 @@
+// 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 "build/build_config.h"
+
+#include "ipc/ipc_tests.h"
+
+#if defined(OS_MACOSX)
+extern "C" {
+#include <sandbox.h>
+}
+#endif
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/message_loop.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_message_utils.h"
+
+#if defined(OS_POSIX)
+
+namespace {
+
+const unsigned kNumFDsToSend = 20;
+const char* kDevZeroPath = "/dev/zero";
+
+static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) {
+ // Check that we can read from the FD.
+ char buf;
+ ssize_t amt_read = read(fd, &buf, 1);
+ ASSERT_EQ(amt_read, 1);
+ ASSERT_EQ(buf, 0); // /dev/zero always reads NUL bytes.
+
+ struct stat st;
+ ASSERT_EQ(fstat(fd, &st), 0);
+
+ ASSERT_EQ(close(fd), 0);
+
+ // We compare iNode numbers to check that the file sent over the wire
+ // was actually the same physical file as the one we were expecting.
+ ASSERT_EQ(inode_num, st.st_ino);
+}
+
+class MyChannelDescriptorListener : public IPC::Channel::Listener {
+ public:
+ MyChannelDescriptorListener(ino_t expected_inode_num)
+ : expected_inode_num_(expected_inode_num),
+ num_fds_received_(0) {}
+
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ void* iter = NULL;
+
+ ++num_fds_received_;
+ base::FileDescriptor descriptor;
+
+ ASSERT_TRUE(
+ IPC::ParamTraits<base::FileDescriptor>::Read(
+ &message, &iter, &descriptor));
+
+ VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_);
+ if (num_fds_received_ == kNumFDsToSend) {
+ MessageLoop::current()->Quit();
+ }
+ }
+
+ virtual void OnChannelError() {
+ MessageLoop::current()->Quit();
+ }
+ private:
+ ino_t expected_inode_num_;
+ unsigned num_fds_received_;
+};
+
+void TestDescriptorServer(IPC::Channel &chan,
+ base::ProcessHandle process_handle) {
+ ASSERT_TRUE(process_handle);
+
+ for (unsigned i = 0; i < kNumFDsToSend; ++i) {
+ base::FileDescriptor descriptor;
+ const int fd = open(kDevZeroPath, O_RDONLY);
+ ASSERT_GE(fd, 0);
+ descriptor.auto_close = true;
+ descriptor.fd = fd;
+
+ IPC::Message* message = new IPC::Message(0, // routing_id
+ 3, // message type
+ IPC::Message::PRIORITY_NORMAL);
+ IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
+ chan.Send(message);
+ }
+
+ // Run message loop.
+ MessageLoop::current()->Run();
+
+ // Close Channel so client gets its OnChannelError() callback fired.
+ chan.Close();
+
+ // Cleanup child process.
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
+}
+
+int TestDescriptorClient(ino_t expected_inode_num) {
+ MessageLoopForIO main_message_loop;
+ MyChannelDescriptorListener listener(expected_inode_num);
+
+ // Setup IPC channel.
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT,
+ &listener);
+ chan.Connect();
+ MessageLoop::current()->Run();
+
+ return 0;
+}
+
+} // namespace
+
+// ---------------------------------------------------------------------------
+#if defined(OS_MACOSX)
+// TODO(port): Make this test cross-platform.
+MULTIPROCESS_TEST_MAIN(RunTestDescriptorClientSandboxed) {
+ struct stat st;
+ const int fd = open(kDevZeroPath, O_RDONLY);
+ fstat(fd, &st);
+ HANDLE_EINTR(close(fd));
+
+ // Enable the Sandbox.
+ char* error_buff = NULL;
+ int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
+ &error_buff);
+ bool success = (error == 0 && error_buff == NULL);
+ if (!success) {
+ return -1;
+ }
+
+ sandbox_free_error(error_buff);
+
+ // Make sure Sandbox is really enabled.
+ if (open(kDevZeroPath, O_RDONLY) != -1) {
+ LOG(ERROR) << "Sandbox wasn't properly enabled";
+ return -1;
+ }
+
+ // See if we can receive a file descriptor.
+ return TestDescriptorClient(st.st_ino);
+}
+
+// Test that FDs are correctly sent to a sandboxed process.
+TEST_F(IPCChannelTest, DescriptorTestSandboxed) {
+ // Setup IPC channel.
+ MyChannelDescriptorListener listener(-1);
+
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ chan.Connect();
+
+ base::ProcessHandle process_handle = SpawnChild(
+ TEST_DESCRIPTOR_CLIENT_SANDBOXED,
+ &chan);
+ TestDescriptorServer(chan, process_handle);
+}
+#endif // defined(OS_MACOSX)
+
+MULTIPROCESS_TEST_MAIN(RunTestDescriptorClient) {
+ struct stat st;
+ const int fd = open(kDevZeroPath, O_RDONLY);
+ fstat(fd, &st);
+ HANDLE_EINTR(close(fd));
+
+ return TestDescriptorClient(st.st_ino);
+}
+
+TEST_F(IPCChannelTest, DescriptorTest) {
+ // Setup IPC channel.
+ MyChannelDescriptorListener listener(-1);
+
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ chan.Connect();
+
+ base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT,
+ &chan);
+ TestDescriptorServer(chan, process_handle);
+}
+
+#endif // defined(OS_POSIX)
diff --git a/ipc/ipc_switches.cc b/ipc/ipc_switches.cc
new file mode 100644
index 0000000..18f7c27
--- /dev/null
+++ b/ipc/ipc_switches.cc
@@ -0,0 +1,28 @@
+// 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 "ipc/ipc_switches.h"
+
+#include "base/base_switches.h"
+
+namespace switches {
+
+// Can't find the switch you are looking for? try looking in
+// base/base_switches.cc instead.
+
+// On POSIX only: use FIFO for IPC channels so that "unrelated" process
+// can connect to a channel, provided it knows its name. For debugging purposes.
+const wchar_t kIPCUseFIFO[] = L"ipc-use-fifo";
+
+// 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";
+
+// Will add kDebugOnStart to every child processes. If a value is passed, it
+// will be used as a filter to determine if the child process should have the
+// kDebugOnStart flag passed on or not.
+const wchar_t kDebugChildren[] = L"debug-children";
+
+} // namespace switches
+
diff --git a/ipc/ipc_switches.h b/ipc/ipc_switches.h
new file mode 100644
index 0000000..1468432
--- /dev/null
+++ b/ipc/ipc_switches.h
@@ -0,0 +1,20 @@
+// 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 the IPC infrastructure.
+
+#ifndef IPC_IPC_SWITCHES_H_
+#define IPC_IPC_SWITCHES_H_
+
+#include "base/base_switches.h"
+
+namespace switches {
+
+extern const wchar_t kIPCUseFIFO[];
+extern const wchar_t kProcessChannelID[];
+extern const wchar_t kDebugChildren[];
+
+} // namespace switches
+
+#endif // IPC_IPC_SWITCHES_H_
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
new file mode 100644
index 0000000..de9b434a
--- /dev/null
+++ b/ipc/ipc_sync_channel.cc
@@ -0,0 +1,453 @@
+// 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 "ipc/ipc_sync_channel.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/thread_local.h"
+#include "base/message_loop.h"
+#include "base/waitable_event.h"
+#include "base/waitable_event_watcher.h"
+#include "ipc/ipc_sync_message.h"
+
+using base::TimeDelta;
+using base::TimeTicks;
+using base::WaitableEvent;
+
+namespace IPC {
+// When we're blocked in a Send(), we need to process incoming synchronous
+// messages right away because it could be blocking our reply (either
+// directly from the same object we're calling, or indirectly through one or
+// more other channels). That means that in SyncContext's OnMessageReceived,
+// we need to process sync message right away if we're blocked. However a
+// simple check isn't sufficient, because the listener thread can be in the
+// process of calling Send.
+// To work around this, when SyncChannel filters a sync message, it sets
+// an event that the listener thread waits on during its Send() call. This
+// allows us to dispatch incoming sync messages when blocked. The race
+// condition is handled because if Send is in the process of being called, it
+// will check the event. In case the listener thread isn't sending a message,
+// we queue a task on the listener thread to dispatch the received messages.
+// The messages are stored in this queue object that's shared among all
+// SyncChannel objects on the same thread (since one object can receive a
+// sync message while another one is blocked).
+
+class SyncChannel::ReceivedSyncMsgQueue :
+ public base::RefCountedThreadSafe<ReceivedSyncMsgQueue> {
+ public:
+ // Returns the ReceivedSyncMsgQueue instance for this thread, creating one
+ // if necessary. Call RemoveContext on the same thread when done.
+ static ReceivedSyncMsgQueue* AddContext() {
+ // We want one ReceivedSyncMsgQueue per listener thread (i.e. since multiple
+ // SyncChannel objects can block the same thread).
+ ReceivedSyncMsgQueue* rv = lazy_tls_ptr_.Pointer()->Get();
+ if (!rv) {
+ rv = new ReceivedSyncMsgQueue();
+ ReceivedSyncMsgQueue::lazy_tls_ptr_.Pointer()->Set(rv);
+ }
+ rv->listener_count_++;
+ return rv;
+ }
+
+ ~ReceivedSyncMsgQueue() {
+ }
+
+ // Called on IPC thread when a synchronous message or reply arrives.
+ void QueueMessage(const Message& msg, SyncChannel::SyncContext* context) {
+ bool was_task_pending;
+ {
+ AutoLock auto_lock(message_lock_);
+
+ was_task_pending = task_pending_;
+ task_pending_ = true;
+
+ // We set the event in case the listener thread is blocked (or is about
+ // to). In case it's not, the PostTask dispatches the messages.
+ message_queue_.push_back(QueuedMessage(new Message(msg), context));
+ }
+
+ dispatch_event_.Signal();
+ if (!was_task_pending) {
+ listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &ReceivedSyncMsgQueue::DispatchMessagesTask));
+ }
+ }
+
+ void QueueReply(const Message &msg, SyncChannel::SyncContext* context) {
+ received_replies_.push_back(QueuedMessage(new Message(msg), context));
+ }
+
+ // Called on the listener's thread to process any queues synchronous
+ // messages.
+ void DispatchMessagesTask() {
+ {
+ AutoLock auto_lock(message_lock_);
+ task_pending_ = false;
+ }
+ DispatchMessages();
+ }
+
+ void DispatchMessages() {
+ while (true) {
+ Message* message;
+ scoped_refptr<SyncChannel::SyncContext> context;
+ {
+ AutoLock auto_lock(message_lock_);
+ if (message_queue_.empty())
+ break;
+
+ message = message_queue_.front().message;
+ context = message_queue_.front().context;
+ message_queue_.pop_front();
+ }
+
+ context->OnDispatchMessage(*message);
+ delete message;
+ }
+ }
+
+ // SyncChannel calls this in its destructor.
+ void RemoveContext(SyncContext* context) {
+ AutoLock auto_lock(message_lock_);
+
+ SyncMessageQueue::iterator iter = message_queue_.begin();
+ while (iter != message_queue_.end()) {
+ if (iter->context == context) {
+ delete iter->message;
+ iter = message_queue_.erase(iter);
+ } else {
+ iter++;
+ }
+ }
+
+ if (--listener_count_ == 0) {
+ DCHECK(lazy_tls_ptr_.Pointer()->Get());
+ lazy_tls_ptr_.Pointer()->Set(NULL);
+ }
+ }
+
+ WaitableEvent* dispatch_event() { return &dispatch_event_; }
+ MessageLoop* listener_message_loop() { return listener_message_loop_; }
+
+ // Holds a pointer to the per-thread ReceivedSyncMsgQueue object.
+ static base::LazyInstance<base::ThreadLocalPointer<ReceivedSyncMsgQueue> >
+ lazy_tls_ptr_;
+
+ // Called on the ipc thread to check if we can unblock any current Send()
+ // calls based on a queued reply.
+ void DispatchReplies() {
+ for (size_t i = 0; i < received_replies_.size(); ++i) {
+ Message* message = received_replies_[i].message;
+ if (received_replies_[i].context->TryToUnblockListener(message)) {
+ delete message;
+ received_replies_.erase(received_replies_.begin() + i);
+ return;
+ }
+ }
+ }
+
+ private:
+ // See the comment in SyncChannel::SyncChannel for why this event is created
+ // as manual reset.
+ ReceivedSyncMsgQueue() :
+ dispatch_event_(true, false),
+ listener_message_loop_(MessageLoop::current()),
+ task_pending_(false),
+ listener_count_(0) {
+ }
+
+ // Holds information about a queued synchronous message or reply.
+ struct QueuedMessage {
+ QueuedMessage(Message* m, SyncContext* c) : message(m), context(c) { }
+ Message* message;
+ scoped_refptr<SyncChannel::SyncContext> context;
+ };
+
+ typedef std::deque<QueuedMessage> SyncMessageQueue;
+ SyncMessageQueue message_queue_;
+
+ std::vector<QueuedMessage> received_replies_;
+
+ // Set when we got a synchronous message that we must respond to as the
+ // sender needs its reply before it can reply to our original synchronous
+ // message.
+ WaitableEvent dispatch_event_;
+ MessageLoop* listener_message_loop_;
+ Lock message_lock_;
+ bool task_pending_;
+ int listener_count_;
+};
+
+base::LazyInstance<base::ThreadLocalPointer<SyncChannel::ReceivedSyncMsgQueue> >
+ SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_(base::LINKER_INITIALIZED);
+
+SyncChannel::SyncContext::SyncContext(
+ Channel::Listener* listener,
+ MessageFilter* filter,
+ MessageLoop* ipc_thread,
+ WaitableEvent* shutdown_event)
+ : ChannelProxy::Context(listener, filter, ipc_thread),
+ received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()),
+ shutdown_event_(shutdown_event) {
+}
+
+SyncChannel::SyncContext::~SyncContext() {
+ while (!deserializers_.empty())
+ Pop();
+}
+
+// Adds information about an outgoing sync message to the context so that
+// we know how to deserialize the reply. Returns a handle that's set when
+// the reply has arrived.
+void SyncChannel::SyncContext::Push(SyncMessage* sync_msg) {
+ // The event is created as manual reset because in between Signal and
+ // OnObjectSignalled, another Send can happen which would stop the watcher
+ // from being called. The event would get watched later, when the nested
+ // Send completes, so the event will need to remain set.
+ PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg),
+ sync_msg->GetReplyDeserializer(),
+ new WaitableEvent(true, false));
+ AutoLock auto_lock(deserializers_lock_);
+ deserializers_.push_back(pending);
+}
+
+bool SyncChannel::SyncContext::Pop() {
+ bool result;
+ {
+ AutoLock auto_lock(deserializers_lock_);
+ PendingSyncMsg msg = deserializers_.back();
+ delete msg.deserializer;
+ delete msg.done_event;
+ msg.done_event = NULL;
+ deserializers_.pop_back();
+ result = msg.send_result;
+ }
+
+ // We got a reply to a synchronous Send() call that's blocking the listener
+ // thread. However, further down the call stack there could be another
+ // blocking Send() call, whose reply we received after we made this last
+ // Send() call. So check if we have any queued replies available that
+ // can now unblock the listener thread.
+ ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ received_sync_msgs_.get(), &ReceivedSyncMsgQueue::DispatchReplies));
+
+ return result;
+}
+
+WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() {
+ AutoLock auto_lock(deserializers_lock_);
+ return deserializers_.back().done_event;
+}
+
+WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() {
+ return received_sync_msgs_->dispatch_event();
+}
+
+void SyncChannel::SyncContext::DispatchMessages() {
+ received_sync_msgs_->DispatchMessages();
+}
+
+bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) {
+ AutoLock auto_lock(deserializers_lock_);
+ if (deserializers_.empty() ||
+ !SyncMessage::IsMessageReplyTo(*msg, deserializers_.back().id)) {
+ return false;
+ }
+
+ if (!msg->is_reply_error()) {
+ deserializers_.back().send_result = deserializers_.back().deserializer->
+ SerializeOutputParameters(*msg);
+ }
+ deserializers_.back().done_event->Signal();
+
+ return true;
+}
+
+void SyncChannel::SyncContext::Clear() {
+ CancelPendingSends();
+ received_sync_msgs_->RemoveContext(this);
+
+ Context::Clear();
+}
+
+void SyncChannel::SyncContext::OnMessageReceived(const Message& msg) {
+ // Give the filters a chance at processing this message.
+ if (TryFilters(msg))
+ return;
+
+ if (TryToUnblockListener(&msg))
+ return;
+
+ if (msg.should_unblock()) {
+ received_sync_msgs_->QueueMessage(msg, this);
+ return;
+ }
+
+ if (msg.is_reply()) {
+ received_sync_msgs_->QueueReply(msg, this);
+ return;
+ }
+
+ return Context::OnMessageReceivedNoFilter(msg);
+}
+
+void SyncChannel::SyncContext::OnChannelError() {
+ CancelPendingSends();
+ shutdown_watcher_.StopWatching();
+ Context::OnChannelError();
+}
+
+void SyncChannel::SyncContext::OnChannelOpened() {
+ shutdown_watcher_.StartWatching(shutdown_event_, this);
+ Context::OnChannelOpened();
+}
+
+void SyncChannel::SyncContext::OnChannelClosed() {
+ shutdown_watcher_.StopWatching();
+ Context::OnChannelClosed();
+}
+
+void SyncChannel::SyncContext::OnSendTimeout(int message_id) {
+ AutoLock auto_lock(deserializers_lock_);
+ PendingSyncMessageQueue::iterator iter;
+ for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) {
+ if (iter->id == message_id) {
+ iter->done_event->Signal();
+ break;
+ }
+ }
+}
+
+void SyncChannel::SyncContext::CancelPendingSends() {
+ AutoLock auto_lock(deserializers_lock_);
+ PendingSyncMessageQueue::iterator iter;
+ for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++)
+ iter->done_event->Signal();
+}
+
+void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) {
+ DCHECK(event == shutdown_event_);
+ // Process shut down before we can get a reply to a synchronous message.
+ // Cancel pending Send calls, which will end up setting the send done event.
+ CancelPendingSends();
+}
+
+
+SyncChannel::SyncChannel(
+ const std::string& channel_id, Channel::Mode mode,
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_message_loop, bool create_pipe_now,
+ WaitableEvent* shutdown_event)
+ : ChannelProxy(
+ channel_id, mode, ipc_message_loop,
+ new SyncContext(listener, filter, ipc_message_loop, shutdown_event),
+ create_pipe_now),
+ sync_messages_with_no_timeout_allowed_(true) {
+ // Ideally we only want to watch this object when running a nested message
+ // loop. However, we don't know when it exits if there's another nested
+ // message loop running under it or not, so we wouldn't know whether to
+ // stop or keep watching. So we always watch it, and create the event as
+ // manual reset since the object watcher might otherwise reset the event
+ // when we're doing a WaitMany.
+ dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(), this);
+}
+
+SyncChannel::~SyncChannel() {
+}
+
+bool SyncChannel::Send(Message* message) {
+ return SendWithTimeout(message, base::kNoTimeout);
+}
+
+bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) {
+ if (!message->is_sync()) {
+ ChannelProxy::Send(message);
+ return true;
+ }
+
+ // *this* might get deleted in WaitForReply.
+ scoped_refptr<SyncContext> context(sync_context());
+ if (context->shutdown_event()->IsSignaled()) {
+ delete message;
+ return false;
+ }
+
+ DCHECK(sync_messages_with_no_timeout_allowed_ ||
+ timeout_ms != base::kNoTimeout);
+ SyncMessage* sync_msg = static_cast<SyncMessage*>(message);
+ context->Push(sync_msg);
+ int message_id = SyncMessage::GetMessageId(*sync_msg);
+ WaitableEvent* pump_messages_event = sync_msg->pump_messages_event();
+
+ ChannelProxy::Send(message);
+
+ if (timeout_ms != base::kNoTimeout) {
+ // We use the sync message id so that when a message times out, we don't
+ // confuse it with another send that is either above/below this Send in
+ // the call stack.
+ context->ipc_message_loop()->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(context.get(),
+ &SyncContext::OnSendTimeout, message_id), timeout_ms);
+ }
+
+ // Wait for reply, or for any other incoming synchronous messages.
+ WaitForReply(pump_messages_event);
+
+ return context->Pop();
+}
+
+void SyncChannel::WaitForReply(WaitableEvent* pump_messages_event) {
+ while (true) {
+ WaitableEvent* objects[] = {
+ sync_context()->GetDispatchEvent(),
+ sync_context()->GetSendDoneEvent(),
+ pump_messages_event
+ };
+
+ unsigned count = pump_messages_event ? 3: 2;
+ unsigned result = WaitableEvent::WaitMany(objects, count);
+ if (result == 0 /* dispatch event */) {
+ // We're waiting for a reply, but we received a blocking synchronous
+ // call. We must process it or otherwise a deadlock might occur.
+ sync_context()->GetDispatchEvent()->Reset();
+ sync_context()->DispatchMessages();
+ continue;
+ }
+
+ if (result == 2 /* pump_messages_event */)
+ WaitForReplyWithNestedMessageLoop(); // Start a nested message loop.
+
+ break;
+ }
+}
+
+void SyncChannel::WaitForReplyWithNestedMessageLoop() {
+ WaitableEvent* old_done_event = send_done_watcher_.GetWatchedEvent();
+ send_done_watcher_.StopWatching();
+ send_done_watcher_.StartWatching(sync_context()->GetSendDoneEvent(), this);
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+ if (old_done_event)
+ send_done_watcher_.StartWatching(old_done_event, this);
+}
+
+void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) {
+ WaitableEvent* dispatch_event = sync_context()->GetDispatchEvent();
+ if (event == dispatch_event) {
+ // The call to DispatchMessages might delete this object, so reregister
+ // the object watcher first.
+ dispatch_event->Reset();
+ dispatch_watcher_.StartWatching(dispatch_event, this);
+ sync_context()->DispatchMessages();
+ } else {
+ // We got the reply, timed out or the process shutdown.
+ DCHECK(event == sync_context()->GetSendDoneEvent());
+ MessageLoop::current()->Quit();
+ }
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_sync_channel.h b/ipc/ipc_sync_channel.h
new file mode 100644
index 0000000..f5d2add
--- /dev/null
+++ b/ipc/ipc_sync_channel.h
@@ -0,0 +1,162 @@
+// 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 IPC_IPC_SYNC_SENDER_H__
+#define IPC_IPC_SYNC_SENDER_H__
+
+#include <string>
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "base/waitable_event_watcher.h"
+#include "ipc/ipc_channel_proxy.h"
+
+namespace base {
+class WaitableEvent;
+};
+
+namespace IPC {
+
+class SyncMessage;
+class MessageReplyDeserializer;
+
+// This is similar to IPC::ChannelProxy, with the added feature of supporting
+// sending synchronous messages.
+// Note that care must be taken that the lifetime of the ipc_thread argument
+// is more than this object. If the message loop goes away while this object
+// is running and it's used to send a message, then it will use the invalid
+// message loop pointer to proxy it to the ipc thread.
+class SyncChannel : public ChannelProxy,
+ public base::WaitableEventWatcher::Delegate {
+ public:
+ SyncChannel(const std::string& channel_id, Channel::Mode mode,
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_message_loop, bool create_pipe_now,
+ base::WaitableEvent* shutdown_event);
+ ~SyncChannel();
+
+ virtual bool Send(Message* message);
+ virtual bool SendWithTimeout(Message* message, int timeout_ms);
+
+ // Whether we allow sending messages with no time-out.
+ void set_sync_messages_with_no_timeout_allowed(bool value) {
+ sync_messages_with_no_timeout_allowed_ = value;
+ }
+
+ protected:
+ class ReceivedSyncMsgQueue;
+ friend class ReceivedSyncMsgQueue;
+
+ // SyncContext holds the per object data for SyncChannel, so that SyncChannel
+ // can be deleted while it's being used in a different thread. See
+ // ChannelProxy::Context for more information.
+ class SyncContext : public Context,
+ public base::WaitableEventWatcher::Delegate {
+ public:
+ SyncContext(Channel::Listener* listener,
+ MessageFilter* filter,
+ MessageLoop* ipc_thread,
+ base::WaitableEvent* shutdown_event);
+
+ ~SyncContext();
+
+ // Adds information about an outgoing sync message to the context so that
+ // we know how to deserialize the reply.
+ void Push(IPC::SyncMessage* sync_msg);
+
+ // Cleanly remove the top deserializer (and throw it away). Returns the
+ // result of the Send call for that message.
+ bool Pop();
+
+ // Returns an event that's set when the send is complete, timed out or the
+ // process shut down.
+ base::WaitableEvent* GetSendDoneEvent();
+
+ // Returns an event that's set when an incoming message that's not the reply
+ // needs to get dispatched (by calling SyncContext::DispatchMessages).
+ base::WaitableEvent* GetDispatchEvent();
+
+ void DispatchMessages();
+
+ // Checks if the given message is blocking the listener thread because of a
+ // synchronous send. If it is, the thread is unblocked and true is
+ // returned. Otherwise the function returns false.
+ bool TryToUnblockListener(const Message* msg);
+
+ // Called on the IPC thread when a sync send that runs a nested message loop
+ // times out.
+ void OnSendTimeout(int message_id);
+
+ base::WaitableEvent* shutdown_event() { return shutdown_event_; }
+
+ private:
+ // IPC::ChannelProxy methods that we override.
+
+ // Called on the listener thread.
+ virtual void Clear();
+
+ // Called on the IPC thread.
+ virtual void OnMessageReceived(const Message& msg);
+ virtual void OnChannelError();
+ virtual void OnChannelOpened();
+ virtual void OnChannelClosed();
+
+ // Cancels all pending Send calls.
+ void CancelPendingSends();
+
+ // WaitableEventWatcher::Delegate implementation.
+ virtual void OnWaitableEventSignaled(base::WaitableEvent* arg);
+
+ // When sending a synchronous message, this structure contains an object
+ // that knows how to deserialize the response.
+ struct PendingSyncMsg {
+ PendingSyncMsg(int id, IPC::MessageReplyDeserializer* d,
+ base::WaitableEvent* e) :
+ id(id), deserializer(d), done_event(e), send_result(false) { }
+ int id;
+ IPC::MessageReplyDeserializer* deserializer;
+ base::WaitableEvent* done_event;
+ bool send_result;
+ };
+
+ typedef std::deque<PendingSyncMsg> PendingSyncMessageQueue;
+ PendingSyncMessageQueue deserializers_;
+ Lock deserializers_lock_;
+
+ scoped_refptr<ReceivedSyncMsgQueue> received_sync_msgs_;
+
+ base::WaitableEvent* shutdown_event_;
+ base::WaitableEventWatcher shutdown_watcher_;
+ };
+
+ private:
+ // WaitableEventWatcher::Delegate implementation.
+ virtual void OnWaitableEventSignaled(base::WaitableEvent* arg);
+
+ SyncContext* sync_context() {
+ return reinterpret_cast<SyncContext*>(context());
+ }
+
+ // Both these functions wait for a reply, timeout or process shutdown. The
+ // latter one also runs a nested message loop in the meantime.
+ void WaitForReply(base::WaitableEvent* pump_messages_event);
+
+ // Runs a nested message loop until a reply arrives, times out, or the process
+ // shuts down.
+ void WaitForReplyWithNestedMessageLoop();
+
+ bool sync_messages_with_no_timeout_allowed_;
+
+ // Used to signal events between the IPC and listener threads.
+ base::WaitableEventWatcher send_done_watcher_;
+ base::WaitableEventWatcher dispatch_watcher_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SyncChannel);
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_SYNC_SENDER_H__
diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc
new file mode 100644
index 0000000..f20f788
--- /dev/null
+++ b/ipc/ipc_sync_channel_unittest.cc
@@ -0,0 +1,1009 @@
+// 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.
+//
+// Unit test for SyncChannel.
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/platform_thread.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/waitable_event.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_sync_channel.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+#define MESSAGES_INTERNAL_FILE "ipc/ipc_sync_message_unittest.h"
+#include "ipc/ipc_message_macros.h"
+
+using namespace IPC;
+using base::WaitableEvent;
+
+namespace {
+
+// Base class for a "process" with listener and IPC threads.
+class Worker : public Channel::Listener, public Message::Sender {
+ public:
+ // Will create a channel without a name.
+ Worker(Channel::Mode mode, const std::string& thread_name)
+ : done_(new WaitableEvent(false, false)),
+ channel_created_(new WaitableEvent(false, false)),
+ mode_(mode),
+ ipc_thread_((thread_name + "_ipc").c_str()),
+ listener_thread_((thread_name + "_listener").c_str()),
+ overrided_thread_(NULL),
+ shutdown_event_(true, false) { }
+
+ // Will create a named channel and use this name for the threads' name.
+ Worker(const std::string& channel_name, Channel::Mode mode)
+ : done_(new WaitableEvent(false, false)),
+ channel_created_(new WaitableEvent(false, false)),
+ channel_name_(channel_name),
+ mode_(mode),
+ ipc_thread_((channel_name + "_ipc").c_str()),
+ listener_thread_((channel_name + "_listener").c_str()),
+ overrided_thread_(NULL),
+ shutdown_event_(true, false) { }
+
+ // The IPC thread needs to outlive SyncChannel, so force the correct order of
+ // destruction.
+ virtual ~Worker() {
+ WaitableEvent listener_done(false, false), ipc_done(false, false);
+ ListenerThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Worker::OnListenerThreadShutdown1, &listener_done,
+ &ipc_done));
+ listener_done.Wait();
+ ipc_done.Wait();
+ ipc_thread_.Stop();
+ listener_thread_.Stop();
+ }
+ void AddRef() { }
+ void Release() { }
+ bool Send(Message* msg) { return channel_->Send(msg); }
+ bool SendWithTimeout(Message* msg, int timeout_ms) {
+ return channel_->SendWithTimeout(msg, timeout_ms);
+ }
+ void WaitForChannelCreation() { channel_created_->Wait(); }
+ void CloseChannel() {
+ DCHECK(MessageLoop::current() == ListenerThread()->message_loop());
+ channel_->Close();
+ }
+ void Start() {
+ StartThread(&listener_thread_, MessageLoop::TYPE_DEFAULT);
+ ListenerThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Worker::OnStart));
+ }
+ void OverrideThread(base::Thread* overrided_thread) {
+ DCHECK(overrided_thread_ == NULL);
+ overrided_thread_ = overrided_thread;
+ }
+ bool SendAnswerToLife(bool pump, int timeout, bool succeed) {
+ int answer = 0;
+ SyncMessage* msg = new SyncChannelTestMsg_AnswerToLife(&answer);
+ if (pump)
+ msg->EnableMessagePumping();
+ bool result = SendWithTimeout(msg, timeout);
+ DCHECK(result == succeed);
+ DCHECK(answer == (succeed ? 42 : 0));
+ return result;
+ }
+ bool SendDouble(bool pump, bool succeed) {
+ int answer = 0;
+ SyncMessage* msg = new SyncChannelTestMsg_Double(5, &answer);
+ if (pump)
+ msg->EnableMessagePumping();
+ bool result = Send(msg);
+ DCHECK(result == succeed);
+ DCHECK(answer == (succeed ? 10 : 0));
+ return result;
+ }
+ Channel::Mode mode() { return mode_; }
+ WaitableEvent* done_event() { return done_.get(); }
+
+ protected:
+ // Derived classes need to call this when they've completed their part of
+ // the test.
+ void Done() { done_->Signal(); }
+ // Functions for dervied classes to implement if they wish.
+ virtual void Run() { }
+ virtual void OnAnswer(int* answer) { NOTREACHED(); }
+ virtual void OnAnswerDelay(Message* reply_msg) {
+ // The message handler map below can only take one entry for
+ // SyncChannelTestMsg_AnswerToLife, so since some classes want
+ // the normal version while other want the delayed reply, we
+ // call the normal version if the derived class didn't override
+ // this function.
+ int answer;
+ OnAnswer(&answer);
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, answer);
+ Send(reply_msg);
+ }
+ virtual void OnDouble(int in, int* out) { NOTREACHED(); }
+ virtual void OnDoubleDelay(int in, Message* reply_msg) {
+ int result;
+ OnDouble(in, &result);
+ SyncChannelTestMsg_Double::WriteReplyParams(reply_msg, result);
+ Send(reply_msg);
+ }
+
+ private:
+ base::Thread* ListenerThread() {
+ return overrided_thread_ ? overrided_thread_ : &listener_thread_;
+ }
+ // Called on the listener thread to create the sync channel.
+ void OnStart() {
+ // Link ipc_thread_, listener_thread_ and channel_ altogether.
+ StartThread(&ipc_thread_, MessageLoop::TYPE_IO);
+ channel_.reset(new SyncChannel(
+ channel_name_, mode_, this, NULL, ipc_thread_.message_loop(), true,
+ &shutdown_event_));
+ channel_created_->Signal();
+ Run();
+ }
+
+ void OnListenerThreadShutdown1(WaitableEvent* listener_event,
+ WaitableEvent* ipc_event) {
+ // SyncChannel needs to be destructed on the thread that it was created on.
+ channel_.reset();
+
+ MessageLoop::current()->RunAllPending();
+
+ ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Worker::OnIPCThreadShutdown, listener_event, ipc_event));
+ }
+
+ void OnIPCThreadShutdown(WaitableEvent* listener_event,
+ WaitableEvent* ipc_event) {
+ MessageLoop::current()->RunAllPending();
+ ipc_event->Signal();
+
+ listener_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Worker::OnListenerThreadShutdown2, listener_event));
+ }
+
+ void OnListenerThreadShutdown2(WaitableEvent* listener_event) {
+ MessageLoop::current()->RunAllPending();
+ listener_event->Signal();
+ }
+
+ void OnMessageReceived(const Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(Worker, message)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_Double, OnDoubleDelay)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_AnswerToLife,
+ OnAnswerDelay)
+ IPC_END_MESSAGE_MAP()
+ }
+
+ void StartThread(base::Thread* thread, MessageLoop::Type type) {
+ base::Thread::Options options;
+ options.message_loop_type = type;
+ thread->StartWithOptions(options);
+ }
+
+ scoped_ptr<WaitableEvent> done_;
+ scoped_ptr<WaitableEvent> channel_created_;
+ std::string channel_name_;
+ Channel::Mode mode_;
+ scoped_ptr<SyncChannel> channel_;
+ base::Thread ipc_thread_;
+ base::Thread listener_thread_;
+ base::Thread* overrided_thread_;
+
+ base::WaitableEvent shutdown_event_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Worker);
+};
+
+
+// Starts the test with the given workers. This function deletes the workers
+// when it's done.
+void RunTest(std::vector<Worker*> workers) {
+ // First we create the workers that are channel servers, or else the other
+ // workers' channel initialization might fail because the pipe isn't created..
+ for (size_t i = 0; i < workers.size(); ++i) {
+ if (workers[i]->mode() == Channel::MODE_SERVER) {
+ workers[i]->Start();
+ workers[i]->WaitForChannelCreation();
+ }
+ }
+
+ // now create the clients
+ for (size_t i = 0; i < workers.size(); ++i) {
+ if (workers[i]->mode() == Channel::MODE_CLIENT)
+ workers[i]->Start();
+ }
+
+ // wait for all the workers to finish
+ for (size_t i = 0; i < workers.size(); ++i)
+ workers[i]->done_event()->Wait();
+
+ STLDeleteContainerPointers(workers.begin(), workers.end());
+}
+
+} // namespace
+
+class IPCSyncChannelTest : public testing::Test {
+ private:
+ MessageLoop message_loop_;
+};
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class SimpleServer : public Worker {
+ public:
+ SimpleServer(bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "simpler_server"),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+class SimpleClient : public Worker {
+ public:
+ SimpleClient() : Worker(Channel::MODE_CLIENT, "simple_client") { }
+
+ void OnAnswer(int* answer) {
+ *answer = 42;
+ Done();
+ }
+};
+
+void Simple(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ workers.push_back(new SimpleServer(pump_during_send));
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests basic synchronous call
+TEST_F(IPCSyncChannelTest, Simple) {
+ Simple(false);
+ Simple(true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class DelayClient : public Worker {
+ public:
+ DelayClient() : Worker(Channel::MODE_CLIENT, "delay_client") { }
+
+ void OnAnswerDelay(Message* reply_msg) {
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ Done();
+ }
+};
+
+void DelayReply(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ workers.push_back(new SimpleServer(pump_during_send));
+ workers.push_back(new DelayClient());
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that asynchronous replies work
+TEST_F(IPCSyncChannelTest, DelayReply) {
+ DelayReply(false);
+ DelayReply(true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class NoHangServer : public Worker {
+ public:
+ explicit NoHangServer(WaitableEvent* got_first_reply, bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "no_hang_server"),
+ got_first_reply_(got_first_reply),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ got_first_reply_->Signal();
+
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, false);
+ Done();
+ }
+
+ WaitableEvent* got_first_reply_;
+ bool pump_during_send_;
+};
+
+class NoHangClient : public Worker {
+ public:
+ explicit NoHangClient(WaitableEvent* got_first_reply)
+ : Worker(Channel::MODE_CLIENT, "no_hang_client"),
+ got_first_reply_(got_first_reply) { }
+
+ virtual void OnAnswerDelay(Message* reply_msg) {
+ // Use the DELAY_REPLY macro so that we can force the reply to be sent
+ // before this function returns (when the channel will be reset).
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ got_first_reply_->Wait();
+ CloseChannel();
+ Done();
+ }
+
+ WaitableEvent* got_first_reply_;
+};
+
+void NoHang(bool pump_during_send) {
+ WaitableEvent got_first_reply(false, false);
+ std::vector<Worker*> workers;
+ workers.push_back(new NoHangServer(&got_first_reply, pump_during_send));
+ workers.push_back(new NoHangClient(&got_first_reply));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that caller doesn't hang if receiver dies
+TEST_F(IPCSyncChannelTest, NoHang) {
+ NoHang(false);
+ NoHang(true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class UnblockServer : public Worker {
+ public:
+ UnblockServer(bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "unblock_server"),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ Done();
+ }
+
+ void OnDouble(int in, int* out) {
+ *out = in * 2;
+ }
+
+ bool pump_during_send_;
+};
+
+class UnblockClient : public Worker {
+ public:
+ UnblockClient(bool pump_during_send)
+ : Worker(Channel::MODE_CLIENT, "unblock_client"),
+ pump_during_send_(pump_during_send) { }
+
+ void OnAnswer(int* answer) {
+ SendDouble(pump_during_send_, true);
+ *answer = 42;
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+void Unblock(bool server_pump, bool client_pump) {
+ std::vector<Worker*> workers;
+ workers.push_back(new UnblockServer(server_pump));
+ workers.push_back(new UnblockClient(client_pump));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that the caller unblocks to answer a sync message from the receiver.
+TEST_F(IPCSyncChannelTest, Unblock) {
+ Unblock(false, false);
+ Unblock(false, true);
+ Unblock(true, false);
+ Unblock(true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class RecursiveServer : public Worker {
+ public:
+ explicit RecursiveServer(
+ bool expected_send_result, bool pump_first, bool pump_second)
+ : Worker(Channel::MODE_SERVER, "recursive_server"),
+ expected_send_result_(expected_send_result),
+ pump_first_(pump_first), pump_second_(pump_second) { }
+ void Run() {
+ SendDouble(pump_first_, expected_send_result_);
+ Done();
+ }
+
+ void OnDouble(int in, int* out) {
+ *out = in * 2;
+ SendAnswerToLife(pump_second_, base::kNoTimeout, expected_send_result_);
+ }
+
+ bool expected_send_result_, pump_first_, pump_second_;
+};
+
+class RecursiveClient : public Worker {
+ public:
+ explicit RecursiveClient(bool pump_during_send, bool close_channel)
+ : Worker(Channel::MODE_CLIENT, "recursive_client"),
+ pump_during_send_(pump_during_send), close_channel_(close_channel) { }
+
+ void OnDoubleDelay(int in, Message* reply_msg) {
+ SendDouble(pump_during_send_, !close_channel_);
+ if (close_channel_) {
+ delete reply_msg;
+ } else {
+ SyncChannelTestMsg_Double::WriteReplyParams(reply_msg, in * 2);
+ Send(reply_msg);
+ }
+ Done();
+ }
+
+ void OnAnswerDelay(Message* reply_msg) {
+ if (close_channel_) {
+ delete reply_msg;
+ CloseChannel();
+ } else {
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ }
+ }
+
+ bool pump_during_send_, close_channel_;
+};
+
+void Recursive(
+ bool server_pump_first, bool server_pump_second, bool client_pump) {
+ std::vector<Worker*> workers;
+ workers.push_back(
+ new RecursiveServer(true, server_pump_first, server_pump_second));
+ workers.push_back(new RecursiveClient(client_pump, false));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests a server calling Send while another Send is pending.
+TEST_F(IPCSyncChannelTest, Recursive) {
+ Recursive(false, false, false);
+ Recursive(false, false, true);
+ Recursive(false, true, false);
+ Recursive(false, true, true);
+ Recursive(true, false, false);
+ Recursive(true, false, true);
+ Recursive(true, true, false);
+ Recursive(true, true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+void RecursiveNoHang(
+ bool server_pump_first, bool server_pump_second, bool client_pump) {
+ std::vector<Worker*> workers;
+ workers.push_back(
+ new RecursiveServer(false, server_pump_first, server_pump_second));
+ workers.push_back(new RecursiveClient(client_pump, true));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that if a caller makes a sync call during an existing sync call and
+// the receiver dies, neither of the Send() calls hang.
+TEST_F(IPCSyncChannelTest, RecursiveNoHang) {
+ RecursiveNoHang(false, false, false);
+ RecursiveNoHang(false, false, true);
+ RecursiveNoHang(false, true, false);
+ RecursiveNoHang(false, true, true);
+ RecursiveNoHang(true, false, false);
+ RecursiveNoHang(true, false, true);
+ RecursiveNoHang(true, true, false);
+ RecursiveNoHang(true, true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class MultipleServer1 : public Worker {
+ public:
+ MultipleServer1(bool pump_during_send)
+ : Worker("test_channel1", Channel::MODE_SERVER),
+ pump_during_send_(pump_during_send) { }
+
+ void Run() {
+ SendDouble(pump_during_send_, true);
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+class MultipleClient1 : public Worker {
+ public:
+ MultipleClient1(WaitableEvent* client1_msg_received,
+ WaitableEvent* client1_can_reply) :
+ Worker("test_channel1", Channel::MODE_CLIENT),
+ client1_msg_received_(client1_msg_received),
+ client1_can_reply_(client1_can_reply) { }
+
+ void OnDouble(int in, int* out) {
+ client1_msg_received_->Signal();
+ *out = in * 2;
+ client1_can_reply_->Wait();
+ Done();
+ }
+
+ private:
+ WaitableEvent *client1_msg_received_, *client1_can_reply_;
+};
+
+class MultipleServer2 : public Worker {
+ public:
+ MultipleServer2() : Worker("test_channel2", Channel::MODE_SERVER) { }
+
+ void OnAnswer(int* result) {
+ *result = 42;
+ Done();
+ }
+};
+
+class MultipleClient2 : public Worker {
+ public:
+ MultipleClient2(
+ WaitableEvent* client1_msg_received, WaitableEvent* client1_can_reply,
+ bool pump_during_send)
+ : Worker("test_channel2", Channel::MODE_CLIENT),
+ client1_msg_received_(client1_msg_received),
+ client1_can_reply_(client1_can_reply),
+ pump_during_send_(pump_during_send) { }
+
+ void Run() {
+ client1_msg_received_->Wait();
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ client1_can_reply_->Signal();
+ Done();
+ }
+
+ private:
+ WaitableEvent *client1_msg_received_, *client1_can_reply_;
+ bool pump_during_send_;
+};
+
+void Multiple(bool server_pump, bool client_pump) {
+ std::vector<Worker*> workers;
+
+ // A shared worker thread so that server1 and server2 run on one thread.
+ base::Thread worker_thread("Multiple");
+ ASSERT_TRUE(worker_thread.Start());
+
+ // Server1 sends a sync msg to client1, which blocks the reply until
+ // server2 (which runs on the same worker thread as server1) responds
+ // to a sync msg from client2.
+ WaitableEvent client1_msg_received(false, false);
+ WaitableEvent client1_can_reply(false, false);
+
+ Worker* worker;
+
+ worker = new MultipleServer2();
+ worker->OverrideThread(&worker_thread);
+ workers.push_back(worker);
+
+ worker = new MultipleClient2(
+ &client1_msg_received, &client1_can_reply, client_pump);
+ workers.push_back(worker);
+
+ worker = new MultipleServer1(server_pump);
+ worker->OverrideThread(&worker_thread);
+ workers.push_back(worker);
+
+ worker = new MultipleClient1(
+ &client1_msg_received, &client1_can_reply);
+ workers.push_back(worker);
+
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that multiple SyncObjects on the same listener thread can unblock each
+// other.
+TEST_F(IPCSyncChannelTest, Multiple) {
+ Multiple(false, false);
+ Multiple(false, true);
+ Multiple(true, false);
+ Multiple(true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class QueuedReplyServer1 : public Worker {
+ public:
+ QueuedReplyServer1(bool pump_during_send)
+ : Worker("test_channel1", Channel::MODE_SERVER),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ SendDouble(pump_during_send_, true);
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+class QueuedReplyClient1 : public Worker {
+ public:
+ QueuedReplyClient1(WaitableEvent* client1_msg_received,
+ WaitableEvent* server2_can_reply) :
+ Worker("test_channel1", Channel::MODE_CLIENT),
+ client1_msg_received_(client1_msg_received),
+ server2_can_reply_(server2_can_reply) { }
+
+ void OnDouble(int in, int* out) {
+ client1_msg_received_->Signal();
+ *out = in * 2;
+ server2_can_reply_->Wait();
+ Done();
+ }
+
+ private:
+ WaitableEvent *client1_msg_received_, *server2_can_reply_;
+};
+
+class QueuedReplyServer2 : public Worker {
+ public:
+ explicit QueuedReplyServer2(WaitableEvent* server2_can_reply) :
+ Worker("test_channel2", Channel::MODE_SERVER),
+ server2_can_reply_(server2_can_reply) { }
+
+ void OnAnswer(int* result) {
+ server2_can_reply_->Signal();
+
+ // give client1's reply time to reach the server listener thread
+ PlatformThread::Sleep(200);
+
+ *result = 42;
+ Done();
+ }
+
+ WaitableEvent *server2_can_reply_;
+};
+
+class QueuedReplyClient2 : public Worker {
+ public:
+ explicit QueuedReplyClient2(
+ WaitableEvent* client1_msg_received, bool pump_during_send)
+ : Worker("test_channel2", Channel::MODE_CLIENT),
+ client1_msg_received_(client1_msg_received),
+ pump_during_send_(pump_during_send){ }
+
+ void Run() {
+ client1_msg_received_->Wait();
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ Done();
+ }
+
+ WaitableEvent *client1_msg_received_;
+ bool pump_during_send_;
+};
+
+void QueuedReply(bool server_pump, bool client_pump) {
+ std::vector<Worker*> workers;
+
+ // A shared worker thread so that server1 and server2 run on one thread.
+ base::Thread worker_thread("QueuedReply");
+ ASSERT_TRUE(worker_thread.Start());
+
+ WaitableEvent client1_msg_received(false, false);
+ WaitableEvent server2_can_reply(false, false);
+
+ Worker* worker;
+
+ worker = new QueuedReplyServer2(&server2_can_reply);
+ worker->OverrideThread(&worker_thread);
+ workers.push_back(worker);
+
+ worker = new QueuedReplyClient2(&client1_msg_received, client_pump);
+ workers.push_back(worker);
+
+ worker = new QueuedReplyServer1(server_pump);
+ worker->OverrideThread(&worker_thread);
+ workers.push_back(worker);
+
+ worker = new QueuedReplyClient1(
+ &client1_msg_received, &server2_can_reply);
+ workers.push_back(worker);
+
+ RunTest(workers);
+}
+
+} // namespace
+
+// While a blocking send is in progress, the listener thread might answer other
+// synchronous messages. This tests that if during the response to another
+// message the reply to the original messages comes, it is queued up correctly
+// and the original Send is unblocked later.
+TEST_F(IPCSyncChannelTest, QueuedReply) {
+ QueuedReply(false, false);
+ QueuedReply(false, true);
+ QueuedReply(true, false);
+ QueuedReply(true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class BadServer : public Worker {
+ public:
+ BadServer(bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "simpler_server"),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ int answer = 0;
+
+ SyncMessage* msg = new SyncMessage(
+ MSG_ROUTING_CONTROL, SyncChannelTestMsg_Double::ID,
+ Message::PRIORITY_NORMAL, NULL);
+ if (pump_during_send_)
+ msg->EnableMessagePumping();
+
+ // Temporarily set the minimum logging very high so that the assertion
+ // in ipc_message_utils doesn't fire.
+ int log_level = logging::GetMinLogLevel();
+ logging::SetMinLogLevel(kint32max);
+ bool result = Send(msg);
+ logging::SetMinLogLevel(log_level);
+ DCHECK(!result);
+
+ // Need to send another message to get the client to call Done().
+ result = Send(new SyncChannelTestMsg_AnswerToLife(&answer));
+ DCHECK(result);
+ DCHECK(answer == 42);
+
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+void BadMessage(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ workers.push_back(new BadServer(pump_during_send));
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that if a message is not serialized correctly, the Send() will fail.
+TEST_F(IPCSyncChannelTest, BadMessage) {
+ BadMessage(false);
+ BadMessage(true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class ChattyClient : public Worker {
+ public:
+ ChattyClient() :
+ Worker(Channel::MODE_CLIENT, "chatty_client") { }
+
+ void OnAnswer(int* answer) {
+ // The PostMessage limit is 10k. Send 20% more than that.
+ const int kMessageLimit = 10000;
+ const int kMessagesToSend = kMessageLimit * 120 / 100;
+ for (int i = 0; i < kMessagesToSend; ++i) {
+ if (!SendDouble(false, true))
+ break;
+ }
+ *answer = 42;
+ Done();
+ }
+};
+
+void ChattyServer(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ workers.push_back(new UnblockServer(pump_during_send));
+ workers.push_back(new ChattyClient());
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests http://b/1093251 - that sending lots of sync messages while
+// the receiver is waiting for a sync reply does not overflow the PostMessage
+// queue.
+TEST_F(IPCSyncChannelTest, ChattyServer) {
+ ChattyServer(false);
+ ChattyServer(true);
+}
+
+//------------------------------------------------------------------------------
+
+namespace {
+
+class TimeoutServer : public Worker {
+ public:
+ TimeoutServer(int timeout_ms,
+ std::vector<bool> timeout_seq,
+ bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "timeout_server"),
+ timeout_ms_(timeout_ms),
+ timeout_seq_(timeout_seq),
+ pump_during_send_(pump_during_send) {
+ }
+
+ void Run() {
+ for (std::vector<bool>::const_iterator iter = timeout_seq_.begin();
+ iter != timeout_seq_.end(); ++iter) {
+ SendAnswerToLife(pump_during_send_, timeout_ms_, !*iter);
+ }
+ Done();
+ }
+
+ private:
+ int timeout_ms_;
+ std::vector<bool> timeout_seq_;
+ bool pump_during_send_;
+};
+
+class UnresponsiveClient : public Worker {
+ public:
+ UnresponsiveClient(std::vector<bool> timeout_seq)
+ : Worker(Channel::MODE_CLIENT, "unresponsive_client"),
+ timeout_seq_(timeout_seq) {
+ }
+
+ void OnAnswerDelay(Message* reply_msg) {
+ DCHECK(!timeout_seq_.empty());
+ if (!timeout_seq_[0]) {
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ } else {
+ // Don't reply.
+ delete reply_msg;
+ }
+ timeout_seq_.erase(timeout_seq_.begin());
+ if (timeout_seq_.empty())
+ Done();
+ }
+
+ private:
+ // Whether we should time-out or respond to the various messages we receive.
+ std::vector<bool> timeout_seq_;
+};
+
+void SendWithTimeoutOK(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(5000, timeout_seq, pump_during_send));
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
+
+void SendWithTimeoutTimeout(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(100, timeout_seq, pump_during_send));
+ workers.push_back(new UnresponsiveClient(timeout_seq));
+ RunTest(workers);
+}
+
+void SendWithTimeoutMixedOKAndTimeout(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(100, timeout_seq, pump_during_send));
+ workers.push_back(new UnresponsiveClient(timeout_seq));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that SendWithTimeout does not time-out if the response comes back fast
+// enough.
+TEST_F(IPCSyncChannelTest, SendWithTimeoutOK) {
+ SendWithTimeoutOK(false);
+ SendWithTimeoutOK(true);
+}
+
+// Tests that SendWithTimeout does time-out.
+TEST_F(IPCSyncChannelTest, SendWithTimeoutTimeout) {
+ SendWithTimeoutTimeout(false);
+ SendWithTimeoutTimeout(true);
+}
+
+// Sends some message that time-out and some that succeed.
+TEST_F(IPCSyncChannelTest, SendWithTimeoutMixedOKAndTimeout) {
+ SendWithTimeoutMixedOKAndTimeout(false);
+ SendWithTimeoutMixedOKAndTimeout(true);
+}
+
+//------------------------------------------------------------------------------
+
+namespace {
+
+class NestedTask : public Task {
+ public:
+ NestedTask(Worker* server) : server_(server) { }
+ void Run() {
+ // Sleep a bit so that we wake up after the reply has been received.
+ PlatformThread::Sleep(250);
+ server_->SendAnswerToLife(true, base::kNoTimeout, true);
+ }
+
+ Worker* server_;
+};
+
+static bool timeout_occured = false;
+
+class TimeoutTask : public Task {
+ public:
+ void Run() {
+ timeout_occured = true;
+ }
+};
+
+class DoneEventRaceServer : public Worker {
+ public:
+ DoneEventRaceServer()
+ : Worker(Channel::MODE_SERVER, "done_event_race_server") { }
+
+ void Run() {
+ MessageLoop::current()->PostTask(FROM_HERE, new NestedTask(this));
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, new TimeoutTask(), 9000);
+ // Even though we have a timeout on the Send, it will succeed since for this
+ // bug, the reply message comes back and is deserialized, however the done
+ // event wasn't set. So we indirectly use the timeout task to notice if a
+ // timeout occurred.
+ SendAnswerToLife(true, 10000, true);
+ DCHECK(!timeout_occured);
+ Done();
+ }
+};
+
+} // namespace
+
+// Tests http://b/1474092 - that if after the done_event is set but before
+// OnObjectSignaled is called another message is sent out, then after its
+// reply comes back OnObjectSignaled will be called for the first message.
+TEST_F(IPCSyncChannelTest, DoneEventRace) {
+ std::vector<Worker*> workers;
+ workers.push_back(new DoneEventRaceServer());
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
diff --git a/ipc/ipc_sync_message.cc b/ipc/ipc_sync_message.cc
new file mode 100644
index 0000000..519adb1
--- /dev/null
+++ b/ipc/ipc_sync_message.cc
@@ -0,0 +1,126 @@
+// 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 "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#include <stack>
+
+#include "base/logging.h"
+#include "base/waitable_event.h"
+#include "ipc/ipc_sync_message.h"
+
+namespace IPC {
+
+uint32 SyncMessage::next_id_ = 0;
+#define kSyncMessageHeaderSize 4
+
+base::WaitableEvent* dummy_event = new base::WaitableEvent(true, true);
+
+SyncMessage::SyncMessage(
+ int32 routing_id,
+ uint16 type,
+ PriorityValue priority,
+ MessageReplyDeserializer* deserializer)
+ : Message(routing_id, type, priority),
+ deserializer_(deserializer),
+ pump_messages_event_(NULL)
+ {
+ set_sync();
+ set_unblock(true);
+
+ // Add synchronous message data before the message payload.
+ SyncHeader header;
+ header.message_id = ++next_id_;
+ WriteSyncHeader(this, header);
+}
+
+MessageReplyDeserializer* SyncMessage::GetReplyDeserializer() {
+ MessageReplyDeserializer* rv = deserializer_;
+ DCHECK(rv);
+ deserializer_ = NULL;
+ return rv;
+}
+
+void SyncMessage::EnableMessagePumping() {
+ DCHECK(!pump_messages_event_);
+ set_pump_messages_event(dummy_event);
+}
+
+bool SyncMessage::IsMessageReplyTo(const Message& msg, int request_id) {
+ if (!msg.is_reply())
+ return false;
+
+ return GetMessageId(msg) == request_id;
+}
+
+void* SyncMessage::GetDataIterator(const Message* msg) {
+ void* iter = const_cast<char*>(msg->payload());
+ UpdateIter(&iter, kSyncMessageHeaderSize);
+ return iter;
+}
+
+int SyncMessage::GetMessageId(const Message& msg) {
+ if (!msg.is_sync() && !msg.is_reply())
+ return 0;
+
+ SyncHeader header;
+ if (!ReadSyncHeader(msg, &header))
+ return 0;
+
+ return header.message_id;
+}
+
+Message* SyncMessage::GenerateReply(const Message* msg) {
+ DCHECK(msg->is_sync());
+
+ Message* reply = new Message(msg->routing_id(), IPC_REPLY_ID,
+ msg->priority());
+ reply->set_reply();
+
+ SyncHeader header;
+
+ // use the same message id, but this time reply bit is set
+ header.message_id = GetMessageId(*msg);
+ WriteSyncHeader(reply, header);
+
+ return reply;
+}
+
+bool SyncMessage::ReadSyncHeader(const Message& msg, SyncHeader* header) {
+ DCHECK(msg.is_sync() || msg.is_reply());
+
+ void* iter = NULL;
+ bool result = msg.ReadInt(&iter, &header->message_id);
+ if (!result) {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool SyncMessage::WriteSyncHeader(Message* msg, const SyncHeader& header) {
+ DCHECK(msg->is_sync() || msg->is_reply());
+ DCHECK(msg->payload_size() == 0);
+ bool result = msg->WriteInt(header.message_id);
+ if (!result) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Note: if you add anything here, you need to update kSyncMessageHeaderSize.
+ DCHECK(kSyncMessageHeaderSize == msg->payload_size());
+
+ return true;
+}
+
+
+bool MessageReplyDeserializer::SerializeOutputParameters(const Message& msg) {
+ return SerializeOutputParameters(msg, SyncMessage::GetDataIterator(&msg));
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_sync_message.h b/ipc/ipc_sync_message.h
new file mode 100644
index 0000000..5d072a7
--- /dev/null
+++ b/ipc/ipc_sync_message.h
@@ -0,0 +1,96 @@
+// 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 IPC_IPC_SYNC_MESSAGE_H__
+#define IPC_IPC_SYNC_MESSAGE_H__
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#include <string>
+#include "base/basictypes.h"
+#include "ipc/ipc_message.h"
+
+namespace base {
+class WaitableEvent;
+}
+
+namespace IPC {
+
+class MessageReplyDeserializer;
+
+class SyncMessage : public Message {
+ public:
+ SyncMessage(int32 routing_id, uint16 type, PriorityValue priority,
+ MessageReplyDeserializer* deserializer);
+
+ // Call this to get a deserializer for the output parameters.
+ // Note that this can only be called once, and the caller is responsible
+ // for deleting the deserializer when they're done.
+ MessageReplyDeserializer* GetReplyDeserializer();
+
+ // If this message can cause the receiver to block while waiting for user
+ // input (i.e. by calling MessageBox), then the caller needs to pump window
+ // messages and dispatch asynchronous messages while waiting for the reply.
+ // If this event is passed in, then window messages will start being pumped
+ // when it's set. Note that this behavior will continue even if the event is
+ // later reset. The event must be valid until after the Send call returns.
+ void set_pump_messages_event(base::WaitableEvent* event) {
+ pump_messages_event_ = event;
+ if (event) {
+ header()->flags |= PUMPING_MSGS_BIT;
+ } else {
+ header()->flags &= ~PUMPING_MSGS_BIT;
+ }
+ }
+
+ // Call this if you always want to pump messages. You can call this method
+ // or set_pump_messages_event but not both.
+ void EnableMessagePumping();
+
+ base::WaitableEvent* pump_messages_event() const {
+ return pump_messages_event_;
+ }
+
+ // Returns true if the message is a reply to the given request id.
+ static bool IsMessageReplyTo(const Message& msg, int request_id);
+
+ // Given a reply message, returns an iterator to the beginning of the data
+ // (i.e. skips over the synchronous specific data).
+ static void* GetDataIterator(const Message* msg);
+
+ // Given a synchronous message (or its reply), returns its id.
+ static int GetMessageId(const Message& msg);
+
+ // Generates a reply message to the given message.
+ static Message* GenerateReply(const Message* msg);
+
+ private:
+ struct SyncHeader {
+ // unique ID (unique per sender)
+ int message_id;
+ };
+
+ static bool ReadSyncHeader(const Message& msg, SyncHeader* header);
+ static bool WriteSyncHeader(Message* msg, const SyncHeader& header);
+
+ MessageReplyDeserializer* deserializer_;
+ base::WaitableEvent* pump_messages_event_;
+
+ static uint32 next_id_; // for generation of unique ids
+};
+
+// Used to deserialize parameters from a reply to a synchronous message
+class MessageReplyDeserializer {
+ public:
+ bool SerializeOutputParameters(const Message& msg);
+ private:
+ // Derived classes need to implement this, using the given iterator (which
+ // is skipped past the header for synchronous messages).
+ virtual bool SerializeOutputParameters(const Message& msg, void* iter) = 0;
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_SYNC_MESSAGE_H__
diff --git a/ipc/ipc_sync_message_unittest.cc b/ipc/ipc_sync_message_unittest.cc
new file mode 100644
index 0000000..1c92574
--- /dev/null
+++ b/ipc/ipc_sync_message_unittest.cc
@@ -0,0 +1,249 @@
+// 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.
+//
+// Unit test to make sure that the serialization of synchronous IPC messages
+// works. This ensures that the macros and templates were defined correctly.
+// Doesn't test the IPC channel mechanism.
+
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_message_utils.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+#define MESSAGES_INTERNAL_FILE "ipc/ipc_sync_message_unittest.h"
+#include "ipc/ipc_message_macros.h"
+
+static IPC::Message* g_reply;
+
+class TestMessageReceiver {
+ public:
+
+ void On_0_1(bool* out1) {
+ *out1 = false;
+ }
+
+ void On_0_2(bool* out1, int* out2) {
+ *out1 = true;
+ *out2 = 2;
+ }
+
+ void On_0_3(bool* out1, int* out2, std::string* out3) {
+ *out1 = false;
+ *out2 = 3;
+ *out3 = "0_3";
+ }
+
+ void On_1_1(int in1, bool* out1) {
+ DCHECK(in1 == 1);
+ *out1 = true;
+ }
+
+ void On_1_2(bool in1, bool* out1, int* out2) {
+ DCHECK(!in1);
+ *out1 = true;
+ *out2 = 12;
+ }
+
+ void On_1_3(int in1, std::string* out1, int* out2, bool* out3) {
+ DCHECK(in1 == 3);
+ *out1 = "1_3";
+ *out2 = 13;
+ *out3 = false;
+ }
+
+ void On_2_1(int in1, bool in2, bool* out1) {
+ DCHECK(in1 == 1 && !in2);
+ *out1 = true;
+ }
+
+ void On_2_2(bool in1, int in2, bool* out1, int* out2) {
+ DCHECK(!in1 && in2 == 2);
+ *out1 = true;
+ *out2 = 22;
+ }
+
+ void On_2_3(int in1, bool in2, std::string* out1, int* out2, bool* out3) {
+ DCHECK(in1 == 3 && in2);
+ *out1 = "2_3";
+ *out2 = 23;
+ *out3 = false;
+ }
+
+ void On_3_1(int in1, bool in2, std::string in3, bool* out1) {
+ DCHECK(in1 == 1 && !in2 && in3 == "3_1");
+ *out1 = true;
+ }
+
+ void On_3_2(std::string in1, bool in2, int in3, bool* out1, int* out2) {
+ DCHECK(in1 == "3_2" && !in2 && in3 == 2);
+ *out1 = true;
+ *out2 = 32;
+ }
+
+ void On_3_3(int in1, std::string in2, bool in3, std::string* out1, int* out2,
+ bool* out3) {
+ DCHECK(in1 == 3 && in2 == "3_3" && in3);
+ *out1 = "3_3";
+ *out2 = 33;
+ *out3 = false;
+ }
+
+ bool Send(IPC::Message* message) {
+ // gets the reply message, stash in global
+ DCHECK(g_reply == NULL);
+ g_reply = message;
+ return true;
+ }
+
+ void OnMessageReceived(const IPC::Message& msg) {
+ IPC_BEGIN_MESSAGE_MAP(TestMessageReceiver, msg)
+ IPC_MESSAGE_HANDLER(Msg_C_0_1, On_0_1)
+ IPC_MESSAGE_HANDLER(Msg_C_0_2, On_0_2)
+ IPC_MESSAGE_HANDLER(Msg_C_0_3, On_0_3)
+ IPC_MESSAGE_HANDLER(Msg_C_1_1, On_1_1)
+ IPC_MESSAGE_HANDLER(Msg_C_1_2, On_1_2)
+ IPC_MESSAGE_HANDLER(Msg_C_1_3, On_1_3)
+ IPC_MESSAGE_HANDLER(Msg_C_2_1, On_2_1)
+ IPC_MESSAGE_HANDLER(Msg_C_2_2, On_2_2)
+ IPC_MESSAGE_HANDLER(Msg_C_2_3, On_2_3)
+ IPC_MESSAGE_HANDLER(Msg_C_3_1, On_3_1)
+ IPC_MESSAGE_HANDLER(Msg_C_3_2, On_3_2)
+ IPC_MESSAGE_HANDLER(Msg_C_3_3, On_3_3)
+ IPC_MESSAGE_HANDLER(Msg_R_0_1, On_0_1)
+ IPC_MESSAGE_HANDLER(Msg_R_0_2, On_0_2)
+ IPC_MESSAGE_HANDLER(Msg_R_0_3, On_0_3)
+ IPC_MESSAGE_HANDLER(Msg_R_1_1, On_1_1)
+ IPC_MESSAGE_HANDLER(Msg_R_1_2, On_1_2)
+ IPC_MESSAGE_HANDLER(Msg_R_1_3, On_1_3)
+ IPC_MESSAGE_HANDLER(Msg_R_2_1, On_2_1)
+ IPC_MESSAGE_HANDLER(Msg_R_2_2, On_2_2)
+ IPC_MESSAGE_HANDLER(Msg_R_2_3, On_2_3)
+ IPC_MESSAGE_HANDLER(Msg_R_3_1, On_3_1)
+ IPC_MESSAGE_HANDLER(Msg_R_3_2, On_3_2)
+ IPC_MESSAGE_HANDLER(Msg_R_3_3, On_3_3)
+ IPC_END_MESSAGE_MAP()
+ }
+
+};
+
+void Send(IPC::SyncMessage* msg) {
+ static TestMessageReceiver receiver;
+
+ IPC::MessageReplyDeserializer* reply_serializer = msg->GetReplyDeserializer();
+ DCHECK(reply_serializer != NULL);
+
+ // "send" the message
+ receiver.OnMessageReceived(*msg);
+ delete msg;
+
+ // get the reply message from the global, and deserialize the output
+ // parameters into the output pointers.
+ DCHECK(g_reply != NULL);
+ bool result = reply_serializer->SerializeOutputParameters(*g_reply);
+ DCHECK(result);
+ delete g_reply;
+ g_reply = NULL;
+ delete reply_serializer;
+}
+
+TEST(IPCSyncMessageTest, Main) {
+ bool bool1 = true;
+ int int1 = 0;
+ std::string string1;
+
+ Send(new Msg_C_0_1(&bool1));
+ DCHECK(!bool1);
+
+ Send(new Msg_C_0_2(&bool1, &int1));
+ DCHECK(bool1 && int1 == 2);
+
+ Send(new Msg_C_0_3(&bool1, &int1, &string1));
+ DCHECK(!bool1 && int1 == 3 && string1 == "0_3");
+
+ bool1 = false;
+ Send(new Msg_C_1_1(1, &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_C_1_2(false, &bool1, &int1));
+ DCHECK(bool1 && int1 == 12);
+
+ bool1 = true;
+ Send(new Msg_C_1_3(3, &string1, &int1, &bool1));
+ DCHECK(string1 == "1_3" && int1 == 13 && !bool1);
+
+ bool1 = false;
+ Send(new Msg_C_2_1(1, false, &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_C_2_2(false, 2, &bool1, &int1));
+ DCHECK(bool1 && int1 == 22);
+
+ bool1 = true;
+ Send(new Msg_C_2_3(3, true, &string1, &int1, &bool1));
+ DCHECK(string1 == "2_3" && int1 == 23 && !bool1);
+
+ bool1 = false;
+ Send(new Msg_C_3_1(1, false, "3_1", &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_C_3_2("3_2", false, 2, &bool1, &int1));
+ DCHECK(bool1 && int1 == 32);
+
+ bool1 = true;
+ Send(new Msg_C_3_3(3, "3_3", true, &string1, &int1, &bool1));
+ DCHECK(string1 == "3_3" && int1 == 33 && !bool1);
+
+ // Routed messages, just a copy of the above but with extra routing paramater
+ Send(new Msg_R_0_1(0, &bool1));
+ DCHECK(!bool1);
+
+ Send(new Msg_R_0_2(0, &bool1, &int1));
+ DCHECK(bool1 && int1 == 2);
+
+ Send(new Msg_R_0_3(0, &bool1, &int1, &string1));
+ DCHECK(!bool1 && int1 == 3 && string1 == "0_3");
+
+ bool1 = false;
+ Send(new Msg_R_1_1(0, 1, &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_R_1_2(0, false, &bool1, &int1));
+ DCHECK(bool1 && int1 == 12);
+
+ bool1 = true;
+ Send(new Msg_R_1_3(0, 3, &string1, &int1, &bool1));
+ DCHECK(string1 == "1_3" && int1 == 13 && !bool1);
+
+ bool1 = false;
+ Send(new Msg_R_2_1(0, 1, false, &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_R_2_2(0, false, 2, &bool1, &int1));
+ DCHECK(bool1 && int1 == 22);
+
+ bool1 = true;
+ Send(new Msg_R_2_3(0, 3, true, &string1, &int1, &bool1));
+ DCHECK(string1 == "2_3" && int1 == 23 && !bool1);
+
+ bool1 = false;
+ Send(new Msg_R_3_1(0, 1, false, "3_1", &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_R_3_2(0, "3_2", false, 2, &bool1, &int1));
+ DCHECK(bool1 && int1 == 32);
+
+ bool1 = true;
+ Send(new Msg_R_3_3(0, 3, "3_3", true, &string1, &int1, &bool1));
+ DCHECK(string1 == "3_3" && int1 == 33 && !bool1);
+}
diff --git a/ipc/ipc_sync_message_unittest.h b/ipc/ipc_sync_message_unittest.h
new file mode 100644
index 0000000..7b252b5
--- /dev/null
+++ b/ipc/ipc_sync_message_unittest.h
@@ -0,0 +1,98 @@
+// 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 "ipc/ipc_message_macros.h"
+
+IPC_BEGIN_MESSAGES(Test)
+ IPC_SYNC_MESSAGE_CONTROL0_0(SyncChannelTestMsg_NoArgs)
+
+ IPC_SYNC_MESSAGE_CONTROL0_1(SyncChannelTestMsg_AnswerToLife,
+ int /* answer */)
+
+ IPC_SYNC_MESSAGE_CONTROL1_1(SyncChannelTestMsg_Double,
+ int /* in */,
+ int /* out */)
+
+ // out1 is false
+ IPC_SYNC_MESSAGE_CONTROL0_1(Msg_C_0_1, bool)
+
+ // out1 is true, out2 is 2
+ IPC_SYNC_MESSAGE_CONTROL0_2(Msg_C_0_2, bool, int)
+
+ // out1 is false, out2 is 3, out3 is "0_3"
+ IPC_SYNC_MESSAGE_CONTROL0_3(Msg_C_0_3, bool, int, std::string)
+
+ // in1 must be 1, out1 is true
+ IPC_SYNC_MESSAGE_CONTROL1_1(Msg_C_1_1, int, bool)
+
+ // in1 must be false, out1 is true, out2 is 12
+ IPC_SYNC_MESSAGE_CONTROL1_2(Msg_C_1_2, bool, bool, int)
+
+ // in1 must be 3, out1 is "1_3", out2 is 13, out3 is false
+ IPC_SYNC_MESSAGE_CONTROL1_3(Msg_C_1_3, int, std::string, int, bool)
+
+ // in1 must be 1, in2 must be false, out1 is true
+ IPC_SYNC_MESSAGE_CONTROL2_1(Msg_C_2_1, int, bool, bool)
+
+ // in1 must be false, in2 must be 2, out1 is true, out2 is 22
+ IPC_SYNC_MESSAGE_CONTROL2_2(Msg_C_2_2, bool, int, bool, int)
+
+ // in1 must be 3, in2 must be true, out1 is "2_3", out2 is 23, out3 is false
+ IPC_SYNC_MESSAGE_CONTROL2_3(Msg_C_2_3, int, bool, std::string, int, bool)
+
+ // in1 must be 1, in2 must be false, in3 must be "3_1", out1 is true
+ IPC_SYNC_MESSAGE_CONTROL3_1(Msg_C_3_1, int, bool, std::string, bool)
+
+ // in1 must be "3_3", in2 must be false, in3 must be 2, out1 is true, out2 is
+ // 32
+ IPC_SYNC_MESSAGE_CONTROL3_2(Msg_C_3_2, std::string, bool, int, bool, int)
+
+ // in1 must be 3, in2 must be "3_3", in3 must be true, out1 is "3_3", out2 is
+ // 33, out3 is false
+ IPC_SYNC_MESSAGE_CONTROL3_3(Msg_C_3_3, int, std::string, bool, std::string,
+ int, bool)
+
+
+ // NOTE: routed messages are just a copy of the above...
+
+ // out1 is false
+ IPC_SYNC_MESSAGE_ROUTED0_1(Msg_R_0_1, bool)
+
+ // out1 is true, out2 is 2
+ IPC_SYNC_MESSAGE_ROUTED0_2(Msg_R_0_2, bool, int)
+
+ // out1 is false, out2 is 3, out3 is "0_3"
+ IPC_SYNC_MESSAGE_ROUTED0_3(Msg_R_0_3, bool, int, std::string)
+
+ // in1 must be 1, out1 is true
+ IPC_SYNC_MESSAGE_ROUTED1_1(Msg_R_1_1, int, bool)
+
+ // in1 must be false, out1 is true, out2 is 12
+ IPC_SYNC_MESSAGE_ROUTED1_2(Msg_R_1_2, bool, bool, int)
+
+ // in1 must be 3, out1 is "1_3", out2 is 13, out3 is false
+ IPC_SYNC_MESSAGE_ROUTED1_3(Msg_R_1_3, int, std::string, int, bool)
+
+ // in1 must be 1, in2 must be false, out1 is true
+ IPC_SYNC_MESSAGE_ROUTED2_1(Msg_R_2_1, int, bool, bool)
+
+ // in1 must be false, in2 must be 2, out1 is true, out2 is 22
+ IPC_SYNC_MESSAGE_ROUTED2_2(Msg_R_2_2, bool, int, bool, int)
+
+ // in1 must be 3, in2 must be true, out1 is "2_3", out2 is 23, out3 is false
+ IPC_SYNC_MESSAGE_ROUTED2_3(Msg_R_2_3, int, bool, std::string, int, bool)
+
+ // in1 must be 1, in2 must be false, in3 must be "3_1", out1 is true
+ IPC_SYNC_MESSAGE_ROUTED3_1(Msg_R_3_1, int, bool, std::string, bool)
+
+ // in1 must be "3_3", in2 must be false, in3 must be 2, out1 is true, out2
+ // is 32
+ IPC_SYNC_MESSAGE_ROUTED3_2(Msg_R_3_2, std::string, bool, int, bool, int)
+
+ // in1 must be 3, in2 must be "3_3", in3 must be true, out1 is "3_3", out2 is
+ // 33, out3 is false
+ IPC_SYNC_MESSAGE_ROUTED3_3(Msg_R_3_3, int, std::string, bool, std::string,
+ int, bool)
+
+IPC_END_MESSAGES(TestMsg)
diff --git a/ipc/ipc_tests.cc b/ipc/ipc_tests.cc
new file mode 100644
index 0000000..172284e
--- /dev/null
+++ b/ipc/ipc_tests.cc
@@ -0,0 +1,476 @@
+// 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 "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <iostream>
+#include <string>
+
+#include "ipc/ipc_tests.h"
+
+#include "base/at_exit.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug_on_start.h"
+#if defined(OS_POSIX)
+#include "base/at_exit.h"
+#include "base/global_descriptors_posix.h"
+#endif
+#include "base/perftimer.h"
+#include "base/perf_test_suite.h"
+#include "base/test_suite.h"
+#include "base/thread.h"
+#include "ipc/ipc_descriptors.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_message_utils.h"
+#include "ipc/ipc_switches.h"
+#include "testing/multiprocess_func_list.h"
+
+// Define to enable IPC performance testing instead of the regular unit tests
+// #define PERFORMANCE_TEST
+
+const char kTestClientChannel[] = "T1";
+const char kReflectorChannel[] = "T2";
+const char kFuzzerChannel[] = "F3";
+
+const size_t kLongMessageStringNumBytes = 50000;
+
+#ifndef PERFORMANCE_TEST
+
+void IPCChannelTest::SetUp() {
+ MultiProcessTest::SetUp();
+
+ // Construct a fresh IO Message loop for the duration of each test.
+ message_loop_ = new MessageLoopForIO();
+}
+
+void IPCChannelTest::TearDown() {
+ delete message_loop_;
+ message_loop_ = NULL;
+
+ MultiProcessTest::TearDown();
+}
+
+#if defined(OS_WIN)
+base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
+ IPC::Channel *channel) {
+ // kDebugChildren support.
+ bool debug_on_start =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
+
+ switch (child_type) {
+ case TEST_CLIENT:
+ return MultiProcessTest::SpawnChild(L"RunTestClient", debug_on_start);
+ break;
+ case TEST_REFLECTOR:
+ return MultiProcessTest::SpawnChild(L"RunReflector", debug_on_start);
+ break;
+ case FUZZER_SERVER:
+ return MultiProcessTest::SpawnChild(L"RunFuzzServer", debug_on_start);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+}
+#elif defined(OS_POSIX)
+base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
+ IPC::Channel *channel) {
+ // kDebugChildren support.
+ bool debug_on_start =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
+
+ base::file_handle_mapping_vector fds_to_map;
+ const int ipcfd = channel->GetClientFileDescriptor();
+ if (ipcfd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
+ }
+
+ base::ProcessHandle ret = NULL;
+ switch (child_type) {
+ case TEST_CLIENT:
+ ret = MultiProcessTest::SpawnChild(L"RunTestClient",
+ fds_to_map,
+ debug_on_start);
+ break;
+ case TEST_DESCRIPTOR_CLIENT:
+ ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClient",
+ fds_to_map,
+ debug_on_start);
+ break;
+ case TEST_DESCRIPTOR_CLIENT_SANDBOXED:
+ ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClientSandboxed",
+ fds_to_map,
+ debug_on_start);
+ break;
+ case TEST_REFLECTOR:
+ ret = MultiProcessTest::SpawnChild(L"RunReflector",
+ fds_to_map,
+ debug_on_start);
+ break;
+ case FUZZER_SERVER:
+ ret = MultiProcessTest::SpawnChild(L"RunFuzzServer",
+ fds_to_map,
+ debug_on_start);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+ return ret;
+}
+#endif // defined(OS_POSIX)
+
+TEST_F(IPCChannelTest, BasicMessageTest) {
+ int v1 = 10;
+ std::string v2("foobar");
+ std::wstring v3(L"hello world");
+
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(v1));
+ EXPECT_TRUE(m.WriteString(v2));
+ EXPECT_TRUE(m.WriteWString(v3));
+
+ void* iter = NULL;
+
+ int vi;
+ std::string vs;
+ std::wstring vw;
+
+ EXPECT_TRUE(m.ReadInt(&iter, &vi));
+ EXPECT_EQ(v1, vi);
+
+ EXPECT_TRUE(m.ReadString(&iter, &vs));
+ EXPECT_EQ(v2, vs);
+
+ EXPECT_TRUE(m.ReadWString(&iter, &vw));
+ EXPECT_EQ(v3, vw);
+
+ // should fail
+ EXPECT_FALSE(m.ReadInt(&iter, &vi));
+ EXPECT_FALSE(m.ReadString(&iter, &vs));
+ EXPECT_FALSE(m.ReadWString(&iter, &vw));
+}
+
+static void Send(IPC::Message::Sender* sender, const char* text) {
+ static int message_index = 0;
+
+ IPC::Message* message = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ message->WriteInt(message_index++);
+ message->WriteString(std::string(text));
+
+ // Make sure we can handle large messages.
+ char junk[kLongMessageStringNumBytes];
+ memset(junk, 'a', sizeof(junk)-1);
+ junk[sizeof(junk)-1] = 0;
+ message->WriteString(std::string(junk));
+
+ // DEBUG: printf("[%u] sending message [%s]\n", GetCurrentProcessId(), text);
+ sender->Send(message);
+}
+
+class MyChannelListener : public IPC::Channel::Listener {
+ public:
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ IPC::MessageIterator iter(message);
+
+ iter.NextInt();
+ const std::string data = iter.NextString();
+ const std::string big_string = iter.NextString();
+ EXPECT_EQ(kLongMessageStringNumBytes - 1, big_string.length());
+
+
+ if (--messages_left_ == 0) {
+ MessageLoop::current()->Quit();
+ } else {
+ Send(sender_, "Foo");
+ }
+ }
+
+ virtual void OnChannelError() {
+ // There is a race when closing the channel so the last message may be lost.
+ EXPECT_LE(messages_left_, 1);
+ MessageLoop::current()->Quit();
+ }
+
+ void Init(IPC::Message::Sender* s) {
+ sender_ = s;
+ messages_left_ = 50;
+ }
+
+ private:
+ IPC::Message::Sender* sender_;
+ int messages_left_;
+};
+
+TEST_F(IPCChannelTest, ChannelTest) {
+ MyChannelListener channel_listener;
+ // Setup IPC channel.
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &channel_listener);
+ chan.Connect();
+
+ channel_listener.Init(&chan);
+
+ base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, &chan);
+ ASSERT_TRUE(process_handle);
+
+ Send(&chan, "hello from parent");
+
+ // Run message loop.
+ MessageLoop::current()->Run();
+
+ // Close Channel so client gets its OnChannelError() callback fired.
+ chan.Close();
+
+ // Cleanup child process.
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
+ base::CloseProcessHandle(process_handle);
+}
+
+TEST_F(IPCChannelTest, ChannelProxyTest) {
+ MyChannelListener channel_listener;
+
+ // The thread needs to out-live the ChannelProxy.
+ base::Thread thread("ChannelProxyTestServer");
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ thread.StartWithOptions(options);
+ {
+ // setup IPC channel proxy
+ IPC::ChannelProxy chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &channel_listener, NULL, thread.message_loop());
+
+ channel_listener.Init(&chan);
+
+#if defined(OS_WIN)
+ base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, NULL);
+#elif defined(OS_POSIX)
+ bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDebugChildren);
+ base::file_handle_mapping_vector fds_to_map;
+ const int ipcfd = chan.GetClientFileDescriptor();
+ if (ipcfd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
+ }
+
+ base::ProcessHandle process_handle = MultiProcessTest::SpawnChild(
+ L"RunTestClient",
+ fds_to_map,
+ debug_on_start);
+#endif // defined(OS_POXIX)
+
+ ASSERT_TRUE(process_handle);
+
+ Send(&chan, "hello from parent");
+
+ // run message loop
+ MessageLoop::current()->Run();
+
+ // cleanup child process
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
+ base::CloseProcessHandle(process_handle);
+ }
+ thread.Stop();
+}
+
+MULTIPROCESS_TEST_MAIN(RunTestClient) {
+ MessageLoopForIO main_message_loop;
+ MyChannelListener channel_listener;
+
+ // setup IPC channel
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT,
+ &channel_listener);
+ chan.Connect();
+ channel_listener.Init(&chan);
+ Send(&chan, "hello from child");
+ // run message loop
+ MessageLoop::current()->Run();
+ // return true;
+ return NULL;
+}
+
+#endif // !PERFORMANCE_TEST
+
+#ifdef PERFORMANCE_TEST
+
+//-----------------------------------------------------------------------------
+// Manually performance test
+//
+// This test times the roundtrip IPC message cycle. It is enabled with a
+// special preprocessor define to enable it instead of the standard IPC
+// unit tests. This works around some funny termination conditions in the
+// regular unit tests.
+//
+// This test is not automated. To test, you will want to vary the message
+// count and message size in TEST to get the numbers you want.
+//
+// FIXME(brettw): Automate this test and have it run by default.
+
+// This channel listener just replies to all messages with the exact same
+// message. It assumes each message has one string parameter. When the string
+// "quit" is sent, it will exit.
+class ChannelReflectorListener : public IPC::Channel::Listener {
+ public:
+ explicit ChannelReflectorListener(IPC::Channel *channel) :
+ channel_(channel),
+ count_messages_(0),
+ latency_messages_(0) {
+ std::cout << "Reflector up" << std::endl;
+ }
+
+ ~ChannelReflectorListener() {
+ std::cout << "Client Messages: " << count_messages_ << std::endl;
+ std::cout << "Client Latency: " << latency_messages_ << std::endl;
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ count_messages_++;
+ IPC::MessageIterator iter(message);
+ int time = iter.NextInt();
+ int msgid = iter.NextInt();
+ std::string payload = iter.NextString();
+ latency_messages_ += GetTickCount() - time;
+
+ // cout << "reflector msg received: " << msgid << endl;
+ if (payload == "quit")
+ MessageLoop::current()->Quit();
+
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(msgid);
+ msg->WriteString(payload);
+ channel_->Send(msg);
+ }
+ private:
+ IPC::Channel *channel_;
+ int count_messages_;
+ int latency_messages_;
+};
+
+class ChannelPerfListener : public IPC::Channel::Listener {
+ public:
+ ChannelPerfListener(IPC::Channel* channel, int msg_count, int msg_size) :
+ count_down_(msg_count),
+ channel_(channel),
+ count_messages_(0),
+ latency_messages_(0) {
+ payload_.resize(msg_size);
+ for (int i = 0; i < static_cast<int>(payload_.size()); i++)
+ payload_[i] = 'a';
+ std::cout << "perflistener up" << std::endl;
+ }
+
+ ~ChannelPerfListener() {
+ std::cout << "Server Messages: " << count_messages_ << std::endl;
+ std::cout << "Server Latency: " << latency_messages_ << std::endl;
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ count_messages_++;
+ // decode the string so this gets counted in the total time
+ IPC::MessageIterator iter(message);
+ int time = iter.NextInt();
+ int msgid = iter.NextInt();
+ std::string cur = iter.NextString();
+ latency_messages_ += GetTickCount() - time;
+
+ // cout << "perflistener got message" << endl;
+
+ count_down_--;
+ if (count_down_ == 0) {
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(count_down_);
+ msg->WriteString("quit");
+ channel_->Send(msg);
+ SetTimer(NULL, 1, 250, (TIMERPROC) PostQuitMessage);
+ return;
+ }
+
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(count_down_);
+ msg->WriteString(payload_);
+ channel_->Send(msg);
+ }
+
+ private:
+ int count_down_;
+ std::string payload_;
+ IPC::Channel *channel_;
+ int count_messages_;
+ int latency_messages_;
+};
+
+TEST_F(IPCChannelTest, Performance) {
+ // setup IPC channel
+ IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_SERVER, NULL);
+ ChannelPerfListener perf_listener(&chan, 10000, 100000);
+ chan.set_listener(&perf_listener);
+ chan.Connect();
+
+ HANDLE process = SpawnChild(TEST_REFLECTOR, &chan);
+ ASSERT_TRUE(process);
+
+ PlatformThread::Sleep(1000);
+
+ PerfTimeLogger logger("IPC_Perf");
+
+ // this initial message will kick-start the ping-pong of messages
+ IPC::Message* message = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ message->WriteInt(GetTickCount());
+ message->WriteInt(-1);
+ message->WriteString("Hello");
+ chan.Send(message);
+
+ // run message loop
+ MessageLoop::current()->Run();
+
+ // cleanup child process
+ WaitForSingleObject(process, 5000);
+ CloseHandle(process);
+}
+
+// This message loop bounces all messages back to the sender
+MULTIPROCESS_TEST_MAIN(RunReflector) {
+ MessageLoopForIO main_message_loop;
+ IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_CLIENT, NULL);
+ ChannelReflectorListener channel_reflector_listener(&chan);
+ chan.set_listener(&channel_reflector_listener);
+ chan.Connect();
+
+ MessageLoop::current()->Run();
+ return true;
+}
+
+#endif // PERFORMANCE_TEST
+
+int main(int argc, char** argv) {
+#ifdef PERFORMANCE_TEST
+ int retval = PerfTestSuite(argc, argv).Run();
+#else
+ int retval = TestSuite(argc, argv).Run();
+#endif
+ return retval;
+}
diff --git a/ipc/ipc_tests.h b/ipc/ipc_tests.h
new file mode 100644
index 0000000..e800883
--- /dev/null
+++ b/ipc/ipc_tests.h
@@ -0,0 +1,47 @@
+// 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 IPC_IPC_TESTS_H__
+#define IPC_IPC_TESTS_H__
+
+#include "base/multiprocess_test.h"
+#include "base/process.h"
+
+// This unit test uses 3 types of child processes, a regular pipe client,
+// a client reflector and a IPC server used for fuzzing tests.
+enum ChildType {
+ TEST_CLIENT,
+ TEST_DESCRIPTOR_CLIENT,
+ TEST_DESCRIPTOR_CLIENT_SANDBOXED,
+ TEST_REFLECTOR,
+ FUZZER_SERVER
+};
+
+// The different channel names for the child processes.
+extern const char kTestClientChannel[];
+extern const char kReflectorChannel[];
+extern const char kFuzzerChannel[];
+
+class MessageLoopForIO;
+namespace IPC {
+class Channel;
+} // namespace IPC
+
+//Base class to facilitate Spawning IPC Client processes.
+class IPCChannelTest : public MultiProcessTest {
+ protected:
+
+ // Create a new MessageLoopForIO For each test.
+ virtual void SetUp();
+ virtual void TearDown();
+
+ // Spawns a child process of the specified type
+ base::ProcessHandle SpawnChild(ChildType child_type,
+ IPC::Channel *channel);
+
+ // Created around each test instantiation.
+ MessageLoopForIO *message_loop_;
+};
+
+#endif // IPC_IPC_TESTS_H__