summaryrefslogtreecommitdiff
path: root/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc')
-rw-r--r--ipc/OWNERS7
-rw-r--r--ipc/file_descriptor_set_posix.cc136
-rw-r--r--ipc/file_descriptor_set_posix.h117
-rw-r--r--ipc/file_descriptor_set_posix_unittest.cc179
-rw-r--r--ipc/ipc.gyp90
-rw-r--r--ipc/ipc.gypi109
-rw-r--r--ipc/ipc_channel.h204
-rw-r--r--ipc/ipc_channel_handle.h52
-rw-r--r--ipc/ipc_channel_posix.cc1217
-rw-r--r--ipc/ipc_channel_posix.h161
-rw-r--r--ipc/ipc_channel_posix_unittest.cc389
-rw-r--r--ipc/ipc_channel_proxy.cc397
-rw-r--r--ipc/ipc_channel_proxy.h272
-rw-r--r--ipc/ipc_channel_win.cc430
-rw-r--r--ipc/ipc_channel_win.h91
-rw-r--r--ipc/ipc_descriptors.h15
-rw-r--r--ipc/ipc_export.h29
-rw-r--r--ipc/ipc_logging.cc251
-rw-r--r--ipc/ipc_logging.h112
-rw-r--r--ipc/ipc_message.cc128
-rw-r--r--ipc/ipc_message.h284
-rw-r--r--ipc/ipc_message_macros.h822
-rw-r--r--ipc/ipc_message_null_macros.h29
-rw-r--r--ipc/ipc_message_utils.cc485
-rwxr-xr-xipc/ipc_message_utils.h1238
-rw-r--r--ipc/ipc_message_utils_impl.h61
-rw-r--r--ipc/ipc_param_traits.h24
-rw-r--r--ipc/ipc_platform_file.cc43
-rw-r--r--ipc/ipc_platform_file.h51
-rw-r--r--ipc/ipc_switches.cc24
-rw-r--r--ipc/ipc_switches.h20
-rw-r--r--ipc/ipc_sync_channel.cc513
-rw-r--r--ipc/ipc_sync_channel.h200
-rw-r--r--ipc/ipc_sync_message.cc130
-rw-r--r--ipc/ipc_sync_message.h109
-rw-r--r--ipc/ipc_sync_message_filter.cc127
-rw-r--r--ipc/ipc_sync_message_filter.h70
-rw-r--r--ipc/param_traits_log_macros.h52
-rw-r--r--ipc/param_traits_macros.h39
-rw-r--r--ipc/param_traits_read_macros.h45
-rw-r--r--ipc/param_traits_write_macros.h38
-rw-r--r--ipc/struct_constructor_macros.h20
-rw-r--r--ipc/struct_destructor_macros.h16
-rw-r--r--ipc/sync_socket_unittest.cc250
44 files changed, 9076 insertions, 0 deletions
diff --git a/ipc/OWNERS b/ipc/OWNERS
new file mode 100644
index 000000000000..fc187f8ba4f1
--- /dev/null
+++ b/ipc/OWNERS
@@ -0,0 +1,7 @@
+agl@chromium.org
+cpu@chromium.org
+darin@chromium.org
+dmaclach@chromium.org
+jam@chromium.org
+jeremy@chromium.org
+tsepez@chromium.org
diff --git a/ipc/file_descriptor_set_posix.cc b/ipc/file_descriptor_set_posix.cc
new file mode 100644
index 000000000000..7f17322de47b
--- /dev/null
+++ b/ipc/file_descriptor_set_posix.cc
@@ -0,0 +1,136 @@
+// 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 <sys/types.h>
+#include <sys/stat.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)
+ if (HANDLE_EINTR(close(descriptors_[i].fd)) < 0)
+ PLOG(ERROR) << "close";
+ }
+}
+
+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;
+ }
+}
+
+bool FileDescriptorSet::ContainsDirectoryDescriptor() const {
+ struct stat st;
+
+ for (std::vector<base::FileDescriptor>::const_iterator
+ i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+ if (fstat(i->fd, &st) == 0 && S_ISDIR(st.st_mode))
+ return true;
+ }
+
+ return false;
+}
+
+void FileDescriptorSet::CommitAll() {
+ for (std::vector<base::FileDescriptor>::iterator
+ i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+ if (i->auto_close)
+ if (HANDLE_EINTR(close(i->fd)) < 0)
+ PLOG(ERROR) << "close";
+ }
+ 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 000000000000..1554c38510d2
--- /dev/null
+++ b/ipc/file_descriptor_set_posix.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_descriptor_posix.h"
+#include "base/memory/ref_counted.h"
+#include "ipc/ipc_export.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 IPC_EXPORT FileDescriptorSet
+ : public base::RefCountedThreadSafe<FileDescriptorSet> {
+ public:
+ 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 = 5,
+ };
+
+ // ---------------------------------------------------------------------------
+ // 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();
+ // Returns true if any contained file descriptors appear to be handles to a
+ // directory.
+ bool ContainsDirectoryDescriptor() const;
+
+ // ---------------------------------------------------------------------------
+
+
+ // ---------------------------------------------------------------------------
+ // 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:
+ friend class base::RefCountedThreadSafe<FileDescriptorSet>;
+
+ ~FileDescriptorSet();
+
+ // A vector of descriptors and close flags. If this message is sent, then
+ // these descriptors are sent as control data. After sending, any descriptors
+ // with a true flag are closed. If this message has been received, then these
+ // are the descriptors which were received and all close flags are true.
+ std::vector<base::FileDescriptor> descriptors_;
+
+ // This contains the index of the next descriptor which should be consumed.
+ // It's used in a couple of ways. Firstly, at destruction we can check that
+ // all the descriptors have been read (with GetNthDescriptor). Secondly, we
+ // can check that they are read in order.
+ mutable unsigned consumed_descriptor_highwater_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorSet);
+};
+
+#endif // IPC_FILE_DESCRIPTOR_SET_POSIX_H_
diff --git a/ipc/file_descriptor_set_posix_unittest.cc b/ipc/file_descriptor_set_posix_unittest.cc
new file mode 100644
index 000000000000..5fc8c50fc9ed
--- /dev/null
+++ b/ipc/file_descriptor_set_posix_unittest.cc
@@ -0,0 +1,179 @@
+// 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 "ipc/file_descriptor_set_posix.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "base/basictypes.h"
+#include "base/eintr_wrapper.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) {
+ EXPECT_NE(HANDLE_EINTR(close(duped)), -1);
+ EXPECT_NE(HANDLE_EINTR(close(fd)), -1);
+ 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 000000000000..ab57cd9b4dd2
--- /dev/null
+++ b/ipc/ipc.gyp
@@ -0,0 +1,90 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'target_defaults': {
+ 'sources/': [
+ ['exclude', '/win/'],
+ ['exclude', '_(posix|win)(_unittest)?\\.(cc|mm?)$'],
+ ['exclude', '/win_[^/]*\\.cc$'],
+ ],
+ 'conditions': [
+ ['os_posix == 1 and OS != "mac"', {'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$'],
+ ]}],
+ ],
+ },
+ 'includes': [
+ 'ipc.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'ipc_tests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'ipc',
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_i18n',
+ '../base/base.gyp:test_support_base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '..'
+ ],
+ 'sources': [
+ 'file_descriptor_set_posix_unittest.cc',
+ 'ipc_channel_posix_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',
+ 'sync_socket_unittest.cc',
+ ],
+ 'conditions': [
+ ['toolkit_uses_gtk == 1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ['os_posix == 1 and OS != "mac"', {
+ 'conditions': [
+ ['linux_use_tcmalloc==1', {
+ 'dependencies': [
+ '../base/allocator/allocator.gyp:allocator',
+ ],
+ }],
+ ],
+ }]
+ ],
+ },
+ {
+ 'target_name': 'test_support_ipc',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'ipc',
+ '../base/base.gyp:base',
+ ],
+ 'sources': [
+ 'ipc_test_sink.cc',
+ 'ipc_test_sink.h',
+ ],
+ },
+ ],
+}
diff --git a/ipc/ipc.gypi b/ipc/ipc.gypi
new file mode 100644
index 000000000000..7791934eabe0
--- /dev/null
+++ b/ipc/ipc.gypi
@@ -0,0 +1,109 @@
+# Copyright (c) 2011 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.
+
+{
+ 'target_defaults': {
+ 'variables': {
+ 'ipc_target': 0,
+ },
+ 'target_conditions': [
+ # This part is shared between the targets defined below.
+ ['ipc_target==1', {
+ '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_export.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_param_traits.h',
+ 'ipc_platform_file.cc',
+ 'ipc_platform_file.h',
+ 'ipc_switches.cc',
+ 'ipc_switches.h',
+ 'ipc_sync_channel.cc',
+ 'ipc_sync_channel.h',
+ 'ipc_sync_message.cc',
+ 'ipc_sync_message.h',
+ 'ipc_sync_message_filter.cc',
+ 'ipc_sync_message_filter.h',
+ 'param_traits_log_macros.h',
+ 'param_traits_macros.h',
+ 'param_traits_read_macros.h',
+ 'param_traits_write_macros.h',
+ 'struct_constructor_macros.h',
+ 'struct_destructor_macros.h',
+ ],
+ 'defines': [
+ 'IPC_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'ipc',
+ 'type': '<(component)',
+ 'variables': {
+ 'ipc_target': 1,
+ },
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+ # TODO(gregoryd): direct_dependent_settings should be shared with the
+ # 64-bit target, but it doesn't work due to a bug in gyp
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ },
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'targets': [
+ {
+ 'target_name': 'ipc_win64',
+ 'type': '<(component)',
+ 'variables': {
+ 'ipc_target': 1,
+ },
+ 'dependencies': [
+ '../base/base.gyp:base_nacl_win64',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64',
+ ],
+ # TODO(gregoryd): direct_dependent_settings should be shared with the
+ # 32-bit target, but it doesn't work due to a bug in gyp
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+ }],
+ ],
+}
diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h
new file mode 100644
index 000000000000..cd57b4b89548
--- /dev/null
+++ b/ipc/ipc_channel.h
@@ -0,0 +1,204 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_message.h"
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+// See
+// http://www.chromium.org/developers/design-documents/inter-process-communication
+// for overview of IPC in Chromium.
+
+// Channels are implemented using named pipes on Windows, and
+// socket pairs (or in some special cases unix domain sockets) on POSIX.
+// On Windows we access pipes in various processes by name.
+// On POSIX we pass file descriptors to child processes and assign names to them
+// in a lookup table.
+// In general on POSIX we do not use unix domain sockets due to security
+// concerns and the fact that they can leave garbage around the file system
+// (MacOS does not support abstract named unix domain sockets).
+// You can use unix domain sockets if you like on POSIX by constructing the
+// the channel with the mode set to one of the NAMED modes. NAMED modes are
+// currently used by automation and service processes.
+
+class IPC_EXPORT 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 IPC_EXPORT Listener {
+ public:
+ virtual ~Listener() {}
+
+ // Called when a message is received. Returns true iff the message was
+ // handled.
+ virtual bool 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() {}
+
+#if defined(OS_POSIX)
+ // Called on the server side when a channel that listens for connections
+ // denies an attempt to connect.
+ virtual void OnChannelDenied() {}
+
+ // Called on the server side when a channel that listens for connections
+ // has an error that causes the listening channel to close.
+ virtual void OnChannelListenError() {}
+#endif // OS_POSIX
+ };
+
+ // Flags to test modes
+ enum ModeFlags {
+ MODE_NO_FLAG = 0x0,
+ MODE_SERVER_FLAG = 0x1,
+ MODE_CLIENT_FLAG = 0x2,
+ MODE_NAMED_FLAG = 0x4,
+#if defined(OS_POSIX)
+ MODE_OPEN_ACCESS_FLAG = 0x8, // Don't restrict access based on client UID.
+#endif
+ };
+
+ // Some Standard Modes
+ enum Mode {
+ MODE_NONE = MODE_NO_FLAG,
+ MODE_SERVER = MODE_SERVER_FLAG,
+ MODE_CLIENT = MODE_CLIENT_FLAG,
+ // Channels on Windows are named by default and accessible from other
+ // processes. On POSIX channels are anonymous by default and not accessible
+ // from other processes. Named channels work via named unix domain sockets.
+ // On Windows MODE_NAMED_SERVER is equivalent to MODE_SERVER and
+ // MODE_NAMED_CLIENT is equivalent to MODE_CLIENT.
+ MODE_NAMED_SERVER = MODE_SERVER_FLAG | MODE_NAMED_FLAG,
+ MODE_NAMED_CLIENT = MODE_CLIENT_FLAG | MODE_NAMED_FLAG,
+#if defined(OS_POSIX)
+ // An "open" named server accepts connections from ANY client.
+ // The caller must then implement their own access-control based on the
+ // client process' user Id.
+ MODE_OPEN_NAMED_SERVER = MODE_OPEN_ACCESS_FLAG | MODE_SERVER_FLAG |
+ MODE_NAMED_FLAG
+#endif
+ };
+
+ enum {
+ // The maximum message size in bytes. Attempting to receive a
+ // message of this size or bigger results in a channel error.
+ kMaximumMessageSize = 128 * 1024 * 1024,
+
+ // Ammount of data to read at once from the pipe.
+ kReadBufferSize = 4 * 1024
+ };
+
+ // Initialize a Channel.
+ //
+ // |channel_handle| identifies the communication Channel. For POSIX, if
+ // the file descriptor in the channel handle is != -1, the channel takes
+ // ownership of the file descriptor and will close it appropriately, otherwise
+ // it will create a new descriptor internally.
+ // |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 IPC::ChannelHandle &channel_handle, Mode mode,
+ Listener* listener);
+
+ virtual ~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() WARN_UNUSED_RESULT;
+
+ // Close this Channel explicitly. May be called multiple times.
+ // On POSIX calling close on an IPC channel that listens for connections will
+ // cause it to close any accepted connections, and it will stop listening for
+ // new connections. If you just want to close the currently accepted
+ // connection and listen for new ones, use ResetToAcceptingConnectionState.
+ 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.
+ virtual bool Send(Message* message);
+
+#if defined(OS_POSIX) && !defined(OS_NACL)
+ // 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.
+ int GetClientFileDescriptor() const;
+
+ // On POSIX an IPC::Channel can either wrap an established socket, or it
+ // can wrap a socket that is listening for connections. Currently an
+ // IPC::Channel that listens for connections can only accept one connection
+ // at a time.
+
+ // Returns true if the channel supports listening for connections.
+ bool AcceptsConnections() const;
+
+ // Returns true if the channel supports listening for connections and is
+ // currently connected.
+ bool HasAcceptedConnection() const;
+
+ // Returns true if the peer process' effective user id can be determined, in
+ // which case the supplied client_euid is updated with it.
+ bool GetClientEuid(uid_t* client_euid) const;
+
+ // Closes any currently connected socket, and returns to a listening state
+ // for more connections.
+ void ResetToAcceptingConnectionState();
+#endif // defined(OS_POSIX) && !defined(OS_NACL)
+
+ // Returns true if a named server channel is initialized on the given channel
+ // ID. Even if true, the server may have already accepted a connection.
+ static bool IsNamedServerInitialized(const std::string& channel_id);
+
+ protected:
+ // Used in Chrome by the TestSink to provide a dummy channel implementation
+ // for testing. TestSink overrides the "interesting" functions in Channel so
+ // no actual implementation is needed. This will cause un-overridden calls to
+ // segfault. Do not use outside of test code!
+ Channel() : channel_impl_(0) { }
+
+ 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 000000000000..ba034cca23b8
--- /dev/null
+++ b/ipc/ipc_channel_handle.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include <string>
+
+#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.
+ ChannelHandle() {}
+ // The name that is passed in should be an absolute path for Posix.
+ // Otherwise there may be a problem in IPC communication between
+ // processes with different working directories.
+ ChannelHandle(const std::string& n) : name(n) {}
+ ChannelHandle(const char* n) : name(n) {}
+#if defined(OS_POSIX)
+ ChannelHandle(const std::string& n, const base::FileDescriptor& s)
+ : name(n), socket(s) {}
+#endif // defined(OS_POSIX)
+
+ std::string name;
+#if defined(OS_POSIX)
+ base::FileDescriptor socket;
+#endif // defined(OS_POSIX)
+
+};
+
+} // 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 000000000000..c358d35c6519
--- /dev/null
+++ b/ipc/ipc_channel_posix.cc
@@ -0,0 +1,1217 @@
+// Copyright (c) 2011 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/file_path.h"
+#include "base/file_util.h"
+#include "base/global_descriptors_posix.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/process_util.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/synchronization/lock.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 sockets as
+// pipes These don't quite line up.
+//
+// When creating a child subprocess we use a socket pair and 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().
+//
+// A POSIX IPC channel can also be set up as a server for a bound UNIX domain
+// socket, and will handle multiple connect and disconnect sequences. Currently
+// it is limited to one connection at a time.
+
+//------------------------------------------------------------------------------
+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:
+ static PipeMap* GetInstance() {
+ return Singleton<PipeMap>::get();
+ }
+
+ ~PipeMap() {
+ // Shouldn't have left over pipes.
+ DCHECK(map_.empty());
+ }
+
+ // Lookup a given channel id. Return -1 if not found.
+ int Lookup(const std::string& channel_id) {
+ base::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) {
+ base::AutoLock locked(lock_);
+
+ ChannelToFDMap::iterator i = map_.find(channel_id);
+ if (i != map_.end()) {
+ if (HANDLE_EINTR(close(i->second)) < 0)
+ PLOG(ERROR) << "close " << channel_id;
+ 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) {
+ base::AutoLock locked(lock_);
+ DCHECK_NE(-1, fd);
+
+ 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:
+ base::Lock lock_;
+ typedef std::map<std::string, int> ChannelToFDMap;
+ ChannelToFDMap map_;
+
+ friend struct DefaultSingletonTraits<PipeMap>;
+};
+
+//------------------------------------------------------------------------------
+// Verify that kMaxPipeNameLength is a decent size.
+COMPILE_ASSERT(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxPipeNameLength,
+ BAD_SUN_PATH_LENGTH);
+
+// Creates a unix domain socket bound to the specified name that is listening
+// for connections.
+bool CreateServerUnixDomainSocket(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) {
+ PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name;
+ if (HANDLE_EINTR(close(fd)) < 0)
+ PLOG(ERROR) << "close " << pipe_name;
+ return false;
+ }
+
+ // Delete any old FS instances.
+ unlink(pipe_name.c_str());
+
+ // Make sure the path we need exists.
+ FilePath path(pipe_name);
+ FilePath dir_path = path.DirName();
+ if (!file_util::CreateDirectory(dir_path)) {
+ return false;
+ }
+
+ // Create unix_addr structure.
+ struct sockaddr_un unix_addr;
+ memset(&unix_addr, 0, sizeof(unix_addr));
+ unix_addr.sun_family = AF_UNIX;
+ int path_len = snprintf(unix_addr.sun_path, IPC::kMaxPipeNameLength,
+ "%s", pipe_name.c_str());
+ DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len);
+ size_t unix_addr_len = offsetof(struct sockaddr_un,
+ sun_path) + path_len + 1;
+
+ // Bind the socket.
+ if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr),
+ unix_addr_len) != 0) {
+ PLOG(ERROR) << "bind " << pipe_name;
+ if (HANDLE_EINTR(close(fd)) < 0)
+ PLOG(ERROR) << "close " << pipe_name;
+ return false;
+ }
+
+ // Start listening on the socket.
+ const int listen_queue_length = 1;
+ if (listen(fd, listen_queue_length) != 0) {
+ PLOG(ERROR) << "listen " << pipe_name;
+ if (HANDLE_EINTR(close(fd)) < 0)
+ PLOG(ERROR) << "close " << pipe_name;
+ return false;
+ }
+
+ *server_listen_fd = fd;
+ return true;
+}
+
+// Accept a connection on a socket we are listening to.
+bool ServerAcceptConnection(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) {
+ PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd;
+ if (HANDLE_EINTR(close(accept_fd)) < 0)
+ PLOG(ERROR) << "close " << accept_fd;
+ return false;
+ }
+
+ *server_socket = accept_fd;
+ return true;
+}
+
+bool CreateClientUnixDomainSocket(const std::string& pipe_name,
+ int* client_socket) {
+ DCHECK(client_socket);
+ 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) {
+ PLOG(ERROR) << "socket " << pipe_name;
+ return false;
+ }
+
+ // Make socket non-blocking
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name;
+ if (HANDLE_EINTR(close(fd)) < 0)
+ PLOG(ERROR) << "close " << pipe_name;
+ 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;
+ int path_len = snprintf(server_unix_addr.sun_path, IPC::kMaxPipeNameLength,
+ "%s", pipe_name.c_str());
+ DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len);
+ size_t server_unix_addr_len = offsetof(struct sockaddr_un,
+ sun_path) + path_len + 1;
+
+ if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&server_unix_addr),
+ server_unix_addr_len)) != 0) {
+ PLOG(ERROR) << "connect " << pipe_name;
+ if (HANDLE_EINTR(close(fd)) < 0)
+ PLOG(ERROR) << "close " << pipe_name;
+ return false;
+ }
+
+ *client_socket = fd;
+ return true;
+}
+
+bool SocketWriteErrorIsRecoverable() {
+#if defined(OS_MACOSX)
+ // On OS X if sendmsg() is trying to send fds between processes and there
+ // isn't enough room in the output buffer to send the fd structure over
+ // atomically then EMSGSIZE is returned.
+ //
+ // EMSGSIZE presents a problem since the system APIs can only call us when
+ // there's room in the socket buffer and not when there is "enough" room.
+ //
+ // The current behavior is to return to the event loop when EMSGSIZE is
+ // received and hopefull service another FD. This is however still
+ // technically a busy wait since the event loop will call us right back until
+ // the receiver has read enough data to allow passing the FD over atomically.
+ return errno == EAGAIN || errno == EMSGSIZE;
+#else
+ return errno == EAGAIN;
+#endif // OS_MACOSX
+}
+
+} // namespace
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle,
+ Mode mode, Listener* listener)
+ : mode_(mode),
+ is_blocked_on_write_(false),
+ waiting_connect_(true),
+ message_send_bytes_written_(0),
+ server_listen_pipe_(-1),
+ pipe_(-1),
+ client_pipe_(-1),
+#if defined(IPC_USES_READWRITE)
+ fd_pipe_(-1),
+ remote_fd_pipe_(-1),
+#endif // IPC_USES_READWRITE
+ pipe_name_(channel_handle.name),
+ listener_(listener),
+ must_unlink_(false) {
+ memset(input_buf_, 0, sizeof(input_buf_));
+ memset(input_cmsg_buf_, 0, sizeof(input_cmsg_buf_));
+ if (!CreatePipe(channel_handle)) {
+ // The pipe may have been closed already.
+ const char *modestr = (mode_ & MODE_SERVER_FLAG) ? "server" : "client";
+ // The pipe may have been closed already.
+ LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name
+ << "\" in " << modestr << " mode";
+ }
+}
+
+Channel::ChannelImpl::~ChannelImpl() {
+ Close();
+}
+
+bool SocketPair(int* fd1, int* fd2) {
+ int pipe_fds[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) {
+ PLOG(ERROR) << "socketpair()";
+ 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) {
+ PLOG(ERROR) << "fcntl(O_NONBLOCK)";
+ if (HANDLE_EINTR(close(pipe_fds[0])) < 0)
+ PLOG(ERROR) << "close";
+ if (HANDLE_EINTR(close(pipe_fds[1])) < 0)
+ PLOG(ERROR) << "close";
+ return false;
+ }
+
+ *fd1 = pipe_fds[0];
+ *fd2 = pipe_fds[1];
+
+ return true;
+}
+
+bool Channel::ChannelImpl::CreatePipe(
+ const IPC::ChannelHandle& channel_handle) {
+ DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
+
+ // Four possible cases:
+ // 1) It's a channel wrapping a pipe that is given to us.
+ // 2) It's for a named channel, so we create it.
+ // 3) It's for a client that we implement ourself. This is used
+ // in unittesting.
+ // 4) It's the initial IPC channel:
+ // 4a) Client side: Pull the pipe out of the GlobalDescriptors set.
+ // 4b) Server side: create the pipe.
+
+ int local_pipe = -1;
+ if (channel_handle.socket.fd != -1) {
+ // Case 1 from comment above.
+ local_pipe = channel_handle.socket.fd;
+#if defined(IPC_USES_READWRITE)
+ // Test the socket passed into us to make sure it is nonblocking.
+ // We don't want to call read/write on a blocking socket.
+ int value = fcntl(local_pipe, F_GETFL);
+ if (value == -1) {
+ PLOG(ERROR) << "fcntl(F_GETFL) " << pipe_name_;
+ return false;
+ }
+ if (!(value & O_NONBLOCK)) {
+ LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK";
+ return false;
+ }
+#endif // IPC_USES_READWRITE
+ } else if (mode_ & MODE_NAMED_FLAG) {
+ // Case 2 from comment above.
+ if (mode_ & MODE_SERVER_FLAG) {
+ if (!CreateServerUnixDomainSocket(pipe_name_, &local_pipe)) {
+ return false;
+ }
+ must_unlink_ = true;
+ } else if (mode_ & MODE_CLIENT_FLAG) {
+ if (!CreateClientUnixDomainSocket(pipe_name_, &local_pipe)) {
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Bad mode: " << mode_;
+ return false;
+ }
+ } else {
+ local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_);
+ if (mode_ & MODE_CLIENT_FLAG) {
+ if (local_pipe != -1) {
+ // Case 3 from comment above.
+ // We only allow one connection.
+ local_pipe = HANDLE_EINTR(dup(local_pipe));
+ PipeMap::GetInstance()->RemoveAndClose(pipe_name_);
+ } else {
+ // Case 4a from comment above.
+ // Guard against inappropriate reuse of the initial IPC channel. If
+ // an IPC channel closes and someone attempts to reuse it by name, the
+ // initial channel must not be recycled here. http://crbug.com/26754.
+ static bool used_initial_channel = false;
+ if (used_initial_channel) {
+ LOG(FATAL) << "Denying attempt to reuse initial IPC channel for "
+ << pipe_name_;
+ return false;
+ }
+ used_initial_channel = true;
+
+ local_pipe =
+ base::GlobalDescriptors::GetInstance()->Get(kPrimaryIPCChannel);
+ }
+ } else if (mode_ & MODE_SERVER_FLAG) {
+ // Case 4b from comment above.
+ if (local_pipe != -1) {
+ LOG(ERROR) << "Server already exists for " << pipe_name_;
+ return false;
+ }
+ if (!SocketPair(&local_pipe, &client_pipe_))
+ return false;
+ PipeMap::GetInstance()->Insert(pipe_name_, client_pipe_);
+ } else {
+ LOG(ERROR) << "Bad mode: " << mode_;
+ return false;
+ }
+ }
+
+#if defined(IPC_USES_READWRITE)
+ // Create a dedicated socketpair() for exchanging file descriptors.
+ // See comments for IPC_USES_READWRITE for details.
+ if (mode_ & MODE_CLIENT_FLAG) {
+ if (!SocketPair(&fd_pipe_, &remote_fd_pipe_)) {
+ return false;
+ }
+ }
+#endif // IPC_USES_READWRITE
+
+ if ((mode_ & MODE_SERVER_FLAG) && (mode_ & MODE_NAMED_FLAG)) {
+ server_listen_pipe_ = local_pipe;
+ local_pipe = -1;
+ }
+
+ pipe_ = local_pipe;
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+ if (server_listen_pipe_ == -1 && pipe_ == -1) {
+ DLOG(INFO) << "Channel creation failed: " << pipe_name_;
+ return false;
+ }
+
+ bool did_connect = true;
+ if (server_listen_pipe_ != -1) {
+ // Watch the pipe for connections, and turn any connections into
+ // active sockets.
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ server_listen_pipe_,
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &server_listen_connection_watcher_,
+ this);
+ } else {
+ did_connect = AcceptConnection();
+ }
+ return did_connect;
+}
+
+bool Channel::ChannelImpl::ProcessIncomingMessages() {
+ ssize_t bytes_read = 0;
+
+ struct msghdr msg = {0};
+ struct iovec iov = {input_buf_, Channel::kReadBufferSize};
+
+ msg.msg_iovlen = 1;
+ msg.msg_control = input_cmsg_buf_;
+
+ for (;;) {
+ msg.msg_iov = &iov;
+
+ 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.
+#if defined(IPC_USES_READWRITE)
+ if (fd_pipe_ >= 0) {
+ bytes_read = HANDLE_EINTR(read(pipe_, input_buf_,
+ Channel::kReadBufferSize));
+ msg.msg_controllen = 0;
+ } else
+#endif // IPC_USES_READWRITE
+ {
+ msg.msg_controllen = sizeof(input_cmsg_buf_);
+ 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 // OS_MACOSX
+ } else if (errno == ECONNRESET || errno == EPIPE) {
+ return false;
+ } else {
+ PLOG(ERROR) << "pipe error (" << pipe_ << ")";
+ return false;
+ }
+ } else if (bytes_read == 0) {
+ // The pipe has closed...
+ return false;
+ }
+ }
+ DCHECK(bytes_read);
+
+ if (client_pipe_ != -1) {
+ PipeMap::GetInstance()->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_EQ(0U, payload_len % sizeof(int));
+ 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)
+ if (HANDLE_EINTR(close(wire_fds[i])) < 0)
+ PLOG(ERROR) << "close " << 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 = NULL;
+ unsigned num_fds = 0;
+ 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 {
+ if (num_wire_fds > 0) {
+ const size_t prev_size = input_overflow_fds_.size();
+ input_overflow_fds_.resize(prev_size + num_wire_fds);
+ memcpy(&input_overflow_fds_[prev_size], wire_fds,
+ num_wire_fds * sizeof(int));
+ }
+ fds = &input_overflow_fds_[0];
+ num_fds = input_overflow_fds_.size();
+ }
+
+ 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);
+ const uint16 header_fds = m.header()->num_fds;
+ if (header_fds) {
+ // the message has file descriptors
+ const char* error = NULL;
+ if (header_fds > num_fds - fds_i) {
+ // the message has been completely received, but we didn't get
+ // enough file descriptors.
+#if defined(IPC_USES_READWRITE)
+ char dummy;
+ struct iovec fd_pipe_iov = { &dummy, 1 };
+ msg.msg_iov = &fd_pipe_iov;
+ msg.msg_controllen = sizeof(input_cmsg_buf_);
+ ssize_t n = HANDLE_EINTR(recvmsg(fd_pipe_, &msg, MSG_DONTWAIT));
+ if (n == 1 && msg.msg_controllen > 0) {
+ 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_EQ(0U, payload_len % sizeof(int));
+ 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)
+ if (HANDLE_EINTR(close(wire_fds[i])) < 0)
+ PLOG(ERROR) << "close " << i;
+ return false;
+ }
+ break;
+ }
+ }
+ if (input_overflow_fds_.empty()) {
+ fds = wire_fds;
+ num_fds = num_wire_fds;
+ } else {
+ if (num_wire_fds > 0) {
+ const size_t prev_size = input_overflow_fds_.size();
+ input_overflow_fds_.resize(prev_size + num_wire_fds);
+ memcpy(&input_overflow_fds_[prev_size], wire_fds,
+ num_wire_fds * sizeof(int));
+ }
+ fds = &input_overflow_fds_[0];
+ num_fds = input_overflow_fds_.size();
+ }
+ }
+ if (header_fds > num_fds - fds_i)
+#endif // IPC_USES_READWRITE
+ error = "Message needs unreceived descriptors";
+ }
+
+ if (header_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:" << header_fds
+ << " num_fds:" << num_fds
+ << " fds_i:" << fds_i;
+#if defined(CHROMIUM_SELINUX)
+ LOG(WARNING) << "In the case of SELinux this can be caused when "
+ "using a --user-data-dir to which the default "
+ "policy doesn't give the renderer access to. ";
+#endif // CHROMIUM_SELINUX
+ // close the existing file descriptors so that we don't leak them
+ for (unsigned i = fds_i; i < num_fds; ++i)
+ if (HANDLE_EINTR(close(fds[i])) < 0)
+ PLOG(ERROR) << "close " << i;
+ input_overflow_fds_.clear();
+ // abort the connection
+ return false;
+ }
+
+ m.file_descriptor_set()->SetDescriptors(
+ &fds[fds_i], header_fds);
+ fds_i += header_fds;
+ }
+ DVLOG(2) << "received message on channel @" << this
+ << " with type " << m.type() << " on fd " << pipe_;
+ if (IsHelloMessage(&m)) {
+ // The Hello message contains only the process id.
+ void *iter = NULL;
+ int pid;
+ if (!m.ReadInt(&iter, &pid)) {
+ NOTREACHED();
+ }
+#if defined(IPC_USES_READWRITE)
+ if (mode_ & MODE_SERVER_FLAG) {
+ // With IPC_USES_READWRITE, the Hello message from the client to the
+ // server also contains the fd_pipe_, which will be used for all
+ // subsequent file descriptor passing.
+ DCHECK_EQ(m.file_descriptor_set()->size(), 1U);
+ base::FileDescriptor descriptor;
+ if (!m.ReadFileDescriptor(&iter, &descriptor)) {
+ NOTREACHED();
+ }
+ fd_pipe_ = descriptor.fd;
+ CHECK(descriptor.auto_close);
+ }
+#endif // IPC_USES_READWRITE
+ listener_->OnChannelConnected(pid);
+ } else {
+ listener_->OnMessageReceived(m);
+ }
+ p = message_tail;
+ } else {
+ // Last message is partial.
+ break;
+ }
+ input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
+ fds_i = 0;
+ fds = vector_as_array(&input_overflow_fds_);
+ num_fds = input_overflow_fds_.size();
+ }
+ 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.
+ }
+}
+
+bool Channel::ChannelImpl::ProcessOutgoingMessages() {
+ DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
+ // no connection?
+ 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_NE(0U, amt_to_write);
+ 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]))];
+
+ ssize_t bytes_written = 1;
+ int fd_written = -1;
+/*
+ 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);
+ if (msg->file_descriptor_set()->ContainsDirectoryDescriptor()) {
+ LOG(FATAL) << "Panic: attempting to transport directory descriptor over"
+ " IPC. Aborting to maintain sandbox isolation.";
+ // If you have hit this then something tried to send a file descriptor
+ // to a directory over an IPC channel. Since IPC channels span
+ // sandboxes this is very bad: the receiving process can use openat
+ // with ".." elements in the path in order to reach the real
+ // filesystem.
+ }
+
+ 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;
+
+ // DCHECK_LE above already checks that
+ // num_fds < MAX_DESCRIPTORS_PER_MESSAGE so no danger of overflow.
+ msg->header()->num_fds = static_cast<uint16>(num_fds);
+
+#if defined(IPC_USES_READWRITE)
+ if (!IsHelloMessage(msg)) {
+ // Only the Hello message sends the file descriptor with the message.
+ // Subsequently, we can send file descriptors on the dedicated
+ // fd_pipe_ which makes Seccomp sandbox operation more efficient.
+ struct iovec fd_pipe_iov = { const_cast<char *>(""), 1 };
+ msgh.msg_iov = &fd_pipe_iov;
+ fd_written = fd_pipe_;
+ bytes_written = HANDLE_EINTR(sendmsg(fd_pipe_, &msgh, MSG_DONTWAIT));
+ msgh.msg_iov = &iov;
+ msgh.msg_controllen = 0;
+ if (bytes_written > 0) {
+ msg->file_descriptor_set()->CommitAll();
+ }
+ }
+#endif // IPC_USES_READWRITE
+ }
+
+*/
+ if (bytes_written == 1) {
+ fd_written = pipe_;
+#if defined(IPC_USES_READWRITE)
+ if ((mode_ & MODE_CLIENT_FLAG) && IsHelloMessage(msg)) {
+ DCHECK_EQ(msg->file_descriptor_set()->size(), 1U);
+ }
+ if (!msgh.msg_controllen) {
+ bytes_written = HANDLE_EINTR(write(pipe_, out_bytes, amt_to_write));
+ } else
+#endif // IPC_USES_READWRITE
+ {
+ bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT));
+ }
+ }
+ if (bytes_written > 0)
+ msg->file_descriptor_set()->CommitAll();
+
+ if (bytes_written < 0 && !SocketWriteErrorIsRecoverable()) {
+#if defined(OS_MACOSX)
+ // On OSX writing to a pipe with no listener returns EPERM.
+ if (errno == EPERM) {
+ Close();
+ return false;
+ }
+#endif // OS_MACOSX
+ if (errno == EPIPE) {
+ Close();
+ return false;
+ }
+ PLOG(ERROR) << "pipe error on "
+ << fd_written
+ << " Currently writing message of size: "
+ << msg->size();
+ 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!
+ DVLOG(2) << "sent message @" << msg << " on channel @" << this
+ << " with type " << msg->type() << " on fd " << pipe_;
+ delete output_queue_.front();
+ output_queue_.pop();
+ }
+ }
+ return true;
+}
+
+bool Channel::ChannelImpl::Send(Message* message) {
+ DVLOG(2) << "sending message @" << message << " on channel @" << this
+ << " with type " << message->type()
+ << " (" << output_queue_.size() << " in queue)";
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::GetInstance()->OnSendMessage(message, "");
+#endif // IPC_MESSAGE_LOG_ENABLED
+
+ output_queue_.push(message);
+ if (!is_blocked_on_write_ && !waiting_connect_) {
+ return ProcessOutgoingMessages();
+ }
+
+ return true;
+}
+
+int Channel::ChannelImpl::GetClientFileDescriptor() const {
+ return client_pipe_;
+}
+
+bool Channel::ChannelImpl::AcceptsConnections() const {
+ return server_listen_pipe_ != -1;
+}
+
+bool Channel::ChannelImpl::HasAcceptedConnection() const {
+ return AcceptsConnections() && pipe_ != -1;
+}
+
+bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const {
+ DCHECK(HasAcceptedConnection());
+#if defined(OS_MACOSX)
+ uid_t peer_euid;
+ gid_t peer_gid;
+ if (getpeereid(pipe_, &peer_euid, &peer_gid) != 0) {
+ PLOG(ERROR) << "getpeereid " << pipe_;
+ return false;
+ }
+ *client_euid = peer_euid;
+ return true;
+#elif defined(OS_SOLARIS)
+ return false;
+#else
+ struct ucred cred;
+ socklen_t cred_len = sizeof(cred);
+ if (getsockopt(pipe_, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) != 0) {
+ PLOG(ERROR) << "getsockopt " << pipe_;
+ return false;
+ }
+ if (cred_len < sizeof(cred)) {
+ NOTREACHED() << "Truncated ucred from SO_PEERCRED?";
+ return false;
+ }
+ *client_euid = cred.uid;
+ return true;
+#endif
+}
+
+void Channel::ChannelImpl::ResetToAcceptingConnectionState() {
+ // Unregister libevent for the unix domain socket and close it.
+ read_watcher_.StopWatchingFileDescriptor();
+ write_watcher_.StopWatchingFileDescriptor();
+ if (pipe_ != -1) {
+ if (HANDLE_EINTR(close(pipe_)) < 0)
+ PLOG(ERROR) << "close pipe_ " << pipe_name_;
+ pipe_ = -1;
+ }
+#if defined(IPC_USES_READWRITE)
+ if (fd_pipe_ != -1) {
+ if (HANDLE_EINTR(close(fd_pipe_)) < 0)
+ PLOG(ERROR) << "close fd_pipe_ " << pipe_name_;
+ fd_pipe_ = -1;
+ }
+ if (remote_fd_pipe_ != -1) {
+ if (HANDLE_EINTR(close(remote_fd_pipe_)) < 0)
+ PLOG(ERROR) << "close remote_fd_pipe_ " << pipe_name_;
+ remote_fd_pipe_ = -1;
+ }
+#endif // IPC_USES_READWRITE
+
+ 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) {
+ if (HANDLE_EINTR(close(*i)) < 0)
+ PLOG(ERROR) << "close";
+ }
+ input_overflow_fds_.clear();
+}
+
+// static
+bool Channel::ChannelImpl::IsNamedServerInitialized(
+ const std::string& channel_id) {
+ return file_util::PathExists(FilePath(channel_id));
+}
+
+// Called by libevent when we can read from the pipe without blocking.
+void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
+ bool send_server_hello_msg = false;
+ if (fd == server_listen_pipe_) {
+ int new_pipe = 0;
+ if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe)) {
+ Close();
+ listener_->OnChannelListenError();
+ }
+
+ if (pipe_ != -1) {
+ // We already have a connection. We only handle one at a time.
+ // close our new descriptor.
+ if (HANDLE_EINTR(shutdown(new_pipe, SHUT_RDWR)) < 0)
+ PLOG(ERROR) << "shutdown " << pipe_name_;
+ if (HANDLE_EINTR(close(new_pipe)) < 0)
+ PLOG(ERROR) << "close " << pipe_name_;
+ listener_->OnChannelDenied();
+ return;
+ }
+ pipe_ = new_pipe;
+
+ if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) {
+ // Verify that the IPC channel peer is running as the same user.
+ uid_t client_euid;
+ if (!GetClientEuid(&client_euid)) {
+ LOG(ERROR) << "Unable to query client euid";
+ ResetToAcceptingConnectionState();
+ return;
+ }
+ if (client_euid != geteuid()) {
+ LOG(WARNING) << "Client euid is not authorised";
+ ResetToAcceptingConnectionState();
+ return;
+ }
+ }
+
+ if (!AcceptConnection()) {
+ NOTREACHED() << "AcceptConnection should not fail on server";
+ }
+ send_server_hello_msg = true;
+ waiting_connect_ = false;
+ } else if (fd == pipe_) {
+ if (waiting_connect_ && (mode_ & MODE_SERVER_FLAG)) {
+ send_server_hello_msg = true;
+ waiting_connect_ = false;
+ }
+ if (!ProcessIncomingMessages()) {
+ // ClosePipeOnError may delete this object, so we mustn't call
+ // ProcessOutgoingMessages.
+ send_server_hello_msg = false;
+ ClosePipeOnError();
+ }
+ } else {
+ NOTREACHED() << "Unknown pipe " << fd;
+ }
+
+ // 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) {
+ ProcessOutgoingMessages();
+ }
+}
+
+// Called by libevent when we can write to the pipe without blocking.
+void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) {
+ DCHECK_EQ(pipe_, fd);
+ is_blocked_on_write_ = false;
+ if (!ProcessOutgoingMessages()) {
+ ClosePipeOnError();
+ }
+}
+
+bool Channel::ChannelImpl::AcceptConnection() {
+ MessageLoopForIO::current()->WatchFileDescriptor(pipe_,
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &read_watcher_,
+ this);
+ QueueHelloMessage();
+
+ if (mode_ & MODE_CLIENT_FLAG) {
+ // If we are a client we want to send a hello message out immediately.
+ // In server mode we will send a hello message when we receive one from a
+ // client.
+ waiting_connect_ = false;
+ return ProcessOutgoingMessages();
+ } else if (mode_ & MODE_SERVER_FLAG) {
+ waiting_connect_ = true;
+ return true;
+ } else {
+ NOTREACHED();
+ return false;
+ }
+}
+
+void Channel::ChannelImpl::ClosePipeOnError() {
+ if (HasAcceptedConnection()) {
+ ResetToAcceptingConnectionState();
+ listener_->OnChannelError();
+ } else {
+ Close();
+ if (AcceptsConnections()) {
+ listener_->OnChannelListenError();
+ } else {
+ listener_->OnChannelError();
+ }
+ }
+}
+
+void Channel::ChannelImpl::QueueHelloMessage() {
+ // Create the Hello message
+ scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
+ HELLO_MESSAGE_TYPE,
+ IPC::Message::PRIORITY_NORMAL));
+
+ if (!msg->WriteInt(base::GetCurrentProcId())) {
+ NOTREACHED() << "Unable to pickle hello message proc id";
+ }
+#if defined(IPC_USES_READWRITE)
+ scoped_ptr<Message> hello;
+ if (remote_fd_pipe_ != -1) {
+ if (!msg->WriteFileDescriptor(base::FileDescriptor(remote_fd_pipe_,
+ false))) {
+ NOTREACHED() << "Unable to pickle hello message file descriptors";
+ }
+ DCHECK_EQ(msg->file_descriptor_set()->size(), 1U);
+ }
+#endif // IPC_USES_READWRITE
+ output_queue_.push(msg.release());
+}
+
+bool Channel::ChannelImpl::IsHelloMessage(const Message* m) const {
+ return m->routing_id() == MSG_ROUTING_NONE && m->type() == HELLO_MESSAGE_TYPE;
+}
+
+void Channel::ChannelImpl::Close() {
+ // Close can be called multiple time, so we need to make sure we're
+ // idempotent.
+
+ ResetToAcceptingConnectionState();
+
+ if (must_unlink_) {
+ unlink(pipe_name_.c_str());
+ must_unlink_ = false;
+ }
+ if (server_listen_pipe_ != -1) {
+ if (HANDLE_EINTR(close(server_listen_pipe_)) < 0)
+ PLOG(ERROR) << "close " << server_listen_pipe_;
+ server_listen_pipe_ = -1;
+ // Unregister libevent for the listening socket and close it.
+ server_listen_connection_watcher_.StopWatchingFileDescriptor();
+ }
+
+ if (client_pipe_ != -1) {
+ PipeMap::GetInstance()->RemoveAndClose(pipe_name_);
+ client_pipe_ = -1;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const IPC::ChannelHandle& channel_handle, Mode mode,
+ Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_handle, 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();
+}
+
+bool Channel::AcceptsConnections() const {
+ return channel_impl_->AcceptsConnections();
+}
+
+bool Channel::HasAcceptedConnection() const {
+ return channel_impl_->HasAcceptedConnection();
+}
+
+bool Channel::GetClientEuid(uid_t* client_euid) const {
+ return channel_impl_->GetClientEuid(client_euid);
+}
+
+void Channel::ResetToAcceptingConnectionState() {
+ channel_impl_->ResetToAcceptingConnectionState();
+}
+
+// static
+bool Channel::IsNamedServerInitialized(const std::string& channel_id) {
+ return ChannelImpl::IsNamedServerInitialized(channel_id);
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_channel_posix.h b/ipc/ipc_channel_posix.h
new file mode 100644
index 000000000000..b66b1fcaf9b4
--- /dev/null
+++ b/ipc/ipc_channel_posix.h
@@ -0,0 +1,161 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#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"
+
+#if !defined(OS_MACOSX)
+// On Linux, the seccomp sandbox makes it very expensive to call
+// recvmsg() and sendmsg(). The restriction on calling read() and write(), which
+// are cheap, is that we can't pass file descriptors over them.
+//
+// As we cannot anticipate when the sender will provide us with file
+// descriptors, we have to make the decision about whether we call read() or
+// recvmsg() before we actually make the call. The easiest option is to
+// create a dedicated socketpair() for exchanging file descriptors. Any file
+// descriptors are split out of a message, with the non-file-descriptor payload
+// going over the normal connection, and the file descriptors being sent
+// separately over the other channel. When read()ing from a channel, we'll
+// notice if the message was supposed to have come with file descriptors and
+// use recvmsg on the other socketpair to retrieve them and combine them
+// back with the rest of the message.
+//
+// Mac can also run in IPC_USES_READWRITE mode if necessary, but at this time
+// doesn't take a performance hit from recvmsg and sendmsg, so it doesn't
+// make sense to waste resources on having the separate dedicated socketpair.
+// It is however useful for debugging between Linux and Mac to be able to turn
+// this switch 'on' on the Mac as well.
+//
+// The HELLO message from the client to the server is always sent using
+// sendmsg because it will contain the file descriptor that the server
+// needs to send file descriptors in later messages.
+#define IPC_USES_READWRITE 1
+#endif
+
+namespace IPC {
+
+class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
+ public:
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const IPC::ChannelHandle& channel_handle, Mode mode,
+ Listener* listener);
+ virtual ~ChannelImpl();
+ bool Connect();
+ void Close();
+ void set_listener(Listener* listener) { listener_ = listener; }
+ bool Send(Message* message);
+ int GetClientFileDescriptor() const;
+ bool AcceptsConnections() const;
+ bool HasAcceptedConnection() const;
+ bool GetClientEuid(uid_t* client_euid) const;
+ void ResetToAcceptingConnectionState();
+ static bool IsNamedServerInitialized(const std::string& channel_id);
+
+ private:
+ bool CreatePipe(const IPC::ChannelHandle& channel_handle);
+
+ bool ProcessIncomingMessages();
+ bool ProcessOutgoingMessages();
+
+ bool AcceptConnection();
+ void ClosePipeOnError();
+ void QueueHelloMessage();
+ bool IsHelloMessage(const Message* m) const;
+
+ // 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_;
+ bool waiting_connect_;
+
+ // If sending a message blocks then we use this variable
+ // to keep track of where we are.
+ size_t message_send_bytes_written_;
+
+ // File descriptor we're listening on for new connections if we listen
+ // for connections.
+ 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_;
+
+#if defined(IPC_USES_READWRITE)
+ // Linux/BSD use a dedicated socketpair() for passing file descriptors.
+ int fd_pipe_;
+ int remote_fd_pipe_;
+#endif
+
+ // 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_;
+
+ // True if we are responsible for unlinking the unix domain socket file.
+ bool must_unlink_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelImpl);
+};
+
+// The maximum length of the name of a pipe for MODE_NAMED_SERVER or
+// MODE_NAMED_CLIENT if you want to pass in your own socket.
+// The standard size on linux is 108, mac is 104. To maintain consistency
+// across platforms we standardize on the smaller value.
+static const size_t kMaxPipeNameLength = 104;
+
+} // namespace IPC
+
+#endif // IPC_IPC_CHANNEL_POSIX_H_
diff --git a/ipc/ipc_channel_posix_unittest.cc b/ipc/ipc_channel_posix_unittest.cc
new file mode 100644
index 000000000000..14ecc6083e54
--- /dev/null
+++ b/ipc/ipc_channel_posix_unittest.cc
@@ -0,0 +1,389 @@
+// Copyright (c) 2011 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.
+
+// These tests are POSIX only.
+
+#include "ipc/ipc_channel_posix.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace {
+
+enum {
+ QUIT_MESSAGE = 47
+};
+
+class IPCChannelPosixTestListener : public IPC::Channel::Listener {
+ public:
+ enum STATUS {
+ DISCONNECTED,
+ MESSAGE_RECEIVED,
+ CHANNEL_ERROR,
+ CONNECTED,
+ DENIED,
+ LISTEN_ERROR
+ };
+
+ IPCChannelPosixTestListener(bool quit_only_on_message)
+ : status_(DISCONNECTED), quit_only_on_message_(quit_only_on_message) {}
+
+ virtual ~IPCChannelPosixTestListener() {}
+
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ EXPECT_EQ(message.type(), QUIT_MESSAGE);
+ status_ = MESSAGE_RECEIVED;
+ QuitRunLoop();
+ return true;
+ }
+
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
+ status_ = CONNECTED;
+ if (!quit_only_on_message_) {
+ QuitRunLoop();
+ }
+ }
+
+ virtual void OnChannelError() OVERRIDE {
+ status_ = CHANNEL_ERROR;
+ if (!quit_only_on_message_) {
+ QuitRunLoop();
+ }
+ }
+
+ virtual void OnChannelDenied() OVERRIDE {
+ status_ = DENIED;
+ if (!quit_only_on_message_) {
+ QuitRunLoop();
+ }
+ }
+
+ virtual void OnChannelListenError() OVERRIDE {
+ status_ = LISTEN_ERROR;
+ if (!quit_only_on_message_) {
+ QuitRunLoop();
+ }
+ }
+
+ STATUS status() { return status_; }
+
+ void QuitRunLoop() {
+ MessageLoopForIO::current()->QuitNow();
+ }
+
+ private:
+ // The current status of the listener.
+ STATUS status_;
+ // If |quit_only_on_message_| then the listener will only break out of
+ // the run loop when the QUIT_MESSAGE is received.
+ bool quit_only_on_message_;
+};
+
+} // namespace
+
+class IPCChannelPosixTest : public base::MultiProcessTest {
+ public:
+ static const char kConnectionSocketTestName[];
+ static void SetUpSocket(IPC::ChannelHandle *handle,
+ IPC::Channel::Mode mode);
+ static void SpinRunLoop(int milliseconds);
+
+ protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+private:
+ scoped_ptr<MessageLoopForIO> message_loop_;
+};
+
+const char IPCChannelPosixTest::kConnectionSocketTestName[] =
+ "/var/tmp/chrome_IPCChannelPosixTest__ConnectionSocket";
+
+void IPCChannelPosixTest::SetUp() {
+ MultiProcessTest::SetUp();
+ // Construct a fresh IO Message loop for the duration of each test.
+ message_loop_.reset(new MessageLoopForIO());
+}
+
+void IPCChannelPosixTest::TearDown() {
+ message_loop_.reset(NULL);
+ MultiProcessTest::TearDown();
+}
+
+// Create up a socket and bind and listen to it, or connect it
+// depending on the |mode|.
+void IPCChannelPosixTest::SetUpSocket(IPC::ChannelHandle *handle,
+ IPC::Channel::Mode mode) {
+ const std::string& name = handle->name;
+
+ int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ ASSERT_GE(socket_fd, 0) << name;
+ ASSERT_GE(fcntl(socket_fd, F_SETFL, O_NONBLOCK), 0);
+ struct sockaddr_un server_address = { 0 };
+ memset(&server_address, 0, sizeof(server_address));
+ server_address.sun_family = AF_UNIX;
+ int path_len = snprintf(server_address.sun_path, IPC::kMaxPipeNameLength,
+ "%s", name.c_str());
+ DCHECK_EQ(static_cast<int>(name.length()), path_len);
+ size_t server_address_len = offsetof(struct sockaddr_un,
+ sun_path) + path_len + 1;
+
+ if (mode == IPC::Channel::MODE_NAMED_SERVER) {
+ // Only one server at a time. Cleanup garbage if it exists.
+ unlink(name.c_str());
+ // Make sure the path we need exists.
+ FilePath path(name);
+ FilePath dir_path = path.DirName();
+ ASSERT_TRUE(file_util::CreateDirectory(dir_path));
+ ASSERT_GE(bind(socket_fd,
+ reinterpret_cast<struct sockaddr *>(&server_address),
+ server_address_len), 0) << server_address.sun_path
+ << ": " << strerror(errno)
+ << "(" << errno << ")";
+ ASSERT_GE(listen(socket_fd, SOMAXCONN), 0) << server_address.sun_path
+ << ": " << strerror(errno)
+ << "(" << errno << ")";
+ } else if (mode == IPC::Channel::MODE_NAMED_CLIENT) {
+ ASSERT_GE(connect(socket_fd,
+ reinterpret_cast<struct sockaddr *>(&server_address),
+ server_address_len), 0) << server_address.sun_path
+ << ": " << strerror(errno)
+ << "(" << errno << ")";
+ } else {
+ FAIL() << "Unknown mode " << mode;
+ }
+ handle->socket.fd = socket_fd;
+}
+
+void IPCChannelPosixTest::SpinRunLoop(int milliseconds) {
+ MessageLoopForIO *loop = MessageLoopForIO::current();
+ // Post a quit task so that this loop eventually ends and we don't hang
+ // in the case of a bad test. Usually, the run loop will quit sooner than
+ // that because all tests use a IPCChannelPosixTestListener which quits the
+ // current run loop on any channel activity.
+ loop->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), milliseconds);
+ loop->Run();
+}
+
+TEST_F(IPCChannelPosixTest, BasicListen) {
+ // Test creating a socket that is listening.
+ IPC::ChannelHandle handle("/var/tmp/IPCChannelPosixTest_BasicListen");
+ SetUpSocket(&handle, IPC::Channel::MODE_NAMED_SERVER);
+ unlink(handle.name.c_str());
+ IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL);
+ ASSERT_TRUE(channel.Connect());
+ ASSERT_TRUE(channel.AcceptsConnections());
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+ channel.ResetToAcceptingConnectionState();
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+}
+
+TEST_F(IPCChannelPosixTest, BasicConnected) {
+ // Test creating a socket that is connected.
+ int pipe_fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds));
+ std::string socket_name("/var/tmp/IPCChannelPosixTest_BasicConnected");
+ ASSERT_GE(fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK), 0);
+
+ base::FileDescriptor fd(pipe_fds[0], false);
+ IPC::ChannelHandle handle(socket_name, fd);
+ IPC::Channel channel(handle, IPC::Channel::MODE_SERVER, NULL);
+ ASSERT_TRUE(channel.Connect());
+ ASSERT_FALSE(channel.AcceptsConnections());
+ channel.Close();
+ ASSERT_TRUE(HANDLE_EINTR(close(pipe_fds[1])) == 0);
+
+ // Make sure that we can use the socket that is created for us by
+ // a standard channel.
+ IPC::Channel channel2(socket_name, IPC::Channel::MODE_SERVER, NULL);
+ ASSERT_TRUE(channel2.Connect());
+ ASSERT_FALSE(channel2.AcceptsConnections());
+}
+
+TEST_F(IPCChannelPosixTest, AdvancedConnected) {
+ // Test creating a connection to an external process.
+ IPCChannelPosixTestListener listener(false);
+ IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
+ SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
+ IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
+ ASSERT_TRUE(channel.Connect());
+ ASSERT_TRUE(channel.AcceptsConnections());
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+
+ base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
+ false);
+ ASSERT_TRUE(handle);
+ SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
+ ASSERT_TRUE(channel.HasAcceptedConnection());
+ IPC::Message* message = new IPC::Message(0, // routing_id
+ QUIT_MESSAGE, // message type
+ IPC::Message::PRIORITY_NORMAL);
+ channel.Send(message);
+ SpinRunLoop(TestTimeouts::action_timeout_ms());
+ int exit_code = 0;
+ EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
+ EXPECT_EQ(0, exit_code);
+ ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+}
+
+TEST_F(IPCChannelPosixTest, ResetState) {
+ // Test creating a connection to an external process. Close the connection,
+ // but continue to listen and make sure another external process can connect
+ // to us.
+ IPCChannelPosixTestListener listener(false);
+ IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
+ SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
+ IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
+ ASSERT_TRUE(channel.Connect());
+ ASSERT_TRUE(channel.AcceptsConnections());
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+
+ base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
+ false);
+ ASSERT_TRUE(handle);
+ SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
+ ASSERT_TRUE(channel.HasAcceptedConnection());
+ channel.ResetToAcceptingConnectionState();
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+
+ base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixTestConnectionProc",
+ false);
+ ASSERT_TRUE(handle2);
+ SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
+ ASSERT_TRUE(channel.HasAcceptedConnection());
+ IPC::Message* message = new IPC::Message(0, // routing_id
+ QUIT_MESSAGE, // message type
+ IPC::Message::PRIORITY_NORMAL);
+ channel.Send(message);
+ SpinRunLoop(TestTimeouts::action_timeout_ms());
+ EXPECT_TRUE(base::KillProcess(handle, 0, false));
+ int exit_code = 0;
+ EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code));
+ EXPECT_EQ(0, exit_code);
+ ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+}
+
+TEST_F(IPCChannelPosixTest, MultiConnection) {
+ // Test setting up a connection to an external process, and then have
+ // another external process attempt to connect to us.
+ IPCChannelPosixTestListener listener(false);
+ IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
+ SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
+ IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
+ ASSERT_TRUE(channel.Connect());
+ ASSERT_TRUE(channel.AcceptsConnections());
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+
+ base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc",
+ false);
+ ASSERT_TRUE(handle);
+ SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
+ ASSERT_TRUE(channel.HasAcceptedConnection());
+ base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc",
+ false);
+ ASSERT_TRUE(handle2);
+ SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ int exit_code = 0;
+ EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code));
+ EXPECT_EQ(exit_code, 0);
+ ASSERT_EQ(IPCChannelPosixTestListener::DENIED, listener.status());
+ ASSERT_TRUE(channel.HasAcceptedConnection());
+ IPC::Message* message = new IPC::Message(0, // routing_id
+ QUIT_MESSAGE, // message type
+ IPC::Message::PRIORITY_NORMAL);
+ channel.Send(message);
+ SpinRunLoop(TestTimeouts::action_timeout_ms());
+ EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
+ EXPECT_EQ(exit_code, 0);
+ ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
+ ASSERT_FALSE(channel.HasAcceptedConnection());
+}
+
+TEST_F(IPCChannelPosixTest, DoubleServer) {
+ // Test setting up two servers with the same name.
+ IPCChannelPosixTestListener listener(false);
+ IPCChannelPosixTestListener listener2(false);
+ IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
+ IPC::Channel channel(chan_handle, IPC::Channel::MODE_SERVER, &listener);
+ IPC::Channel channel2(chan_handle, IPC::Channel::MODE_SERVER, &listener2);
+ ASSERT_TRUE(channel.Connect());
+ ASSERT_FALSE(channel2.Connect());
+}
+
+TEST_F(IPCChannelPosixTest, BadMode) {
+ // Test setting up two servers with a bad mode.
+ IPCChannelPosixTestListener listener(false);
+ IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
+ IPC::Channel channel(chan_handle, IPC::Channel::MODE_NONE, &listener);
+ ASSERT_FALSE(channel.Connect());
+}
+
+TEST_F(IPCChannelPosixTest, IsNamedServerInitialized) {
+ IPCChannelPosixTestListener listener(false);
+ IPC::ChannelHandle chan_handle(kConnectionSocketTestName);
+ ASSERT_TRUE(file_util::Delete(FilePath(kConnectionSocketTestName), false));
+ ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized(
+ kConnectionSocketTestName));
+ IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
+ ASSERT_TRUE(IPC::Channel::IsNamedServerInitialized(
+ kConnectionSocketTestName));
+ channel.Close();
+ ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized(
+ kConnectionSocketTestName));
+}
+
+// A long running process that connects to us
+MULTIPROCESS_TEST_MAIN(IPCChannelPosixTestConnectionProc) {
+ MessageLoopForIO message_loop;
+ IPCChannelPosixTestListener listener(true);
+ IPC::ChannelHandle handle(IPCChannelPosixTest::kConnectionSocketTestName);
+ IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT);
+ IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener);
+ EXPECT_TRUE(channel.Connect());
+ IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ EXPECT_EQ(IPCChannelPosixTestListener::MESSAGE_RECEIVED, listener.status());
+ return 0;
+}
+
+// Simple external process that shouldn't be able to connect to us.
+MULTIPROCESS_TEST_MAIN(IPCChannelPosixFailConnectionProc) {
+ MessageLoopForIO message_loop;
+ IPCChannelPosixTestListener listener(false);
+ IPC::ChannelHandle handle(IPCChannelPosixTest::kConnectionSocketTestName);
+ IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT);
+ IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener);
+
+ // In this case connect may succeed or fail depending on if the packet
+ // actually gets sent at sendmsg. Since we never delay on send, we may not
+ // see the error. However even if connect succeeds, eventually we will get an
+ // error back since the channel will be closed when we attempt to read from
+ // it.
+ bool connected = channel.Connect();
+ if (connected) {
+ IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout_ms());
+ EXPECT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
+ } else {
+ EXPECT_EQ(IPCChannelPosixTestListener::DISCONNECTED, listener.status());
+ }
+ return 0;
+}
+
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc
new file mode 100644
index 000000000000..dc990c26184a
--- /dev/null
+++ b/ipc/ipc_channel_proxy.cc
@@ -0,0 +1,397 @@
+// Copyright (c) 2011 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/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_logging.h"
+#include "ipc/ipc_message_utils.h"
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+// This task ensures the message is deleted if the task is deleted without
+// having been run.
+class SendTask : public Task {
+ public:
+ SendTask(ChannelProxy::Context* context, Message* message)
+ : context_(context),
+ message_(message) {
+ }
+
+ virtual void Run() {
+ context_->OnSendMessage(message_.release());
+ }
+
+ private:
+ scoped_refptr<ChannelProxy::Context> context_;
+ scoped_ptr<Message> message_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendTask);
+};
+
+//------------------------------------------------------------------------------
+
+ChannelProxy::MessageFilter::MessageFilter() {}
+
+ChannelProxy::MessageFilter::~MessageFilter() {}
+
+void ChannelProxy::MessageFilter::OnFilterAdded(Channel* channel) {}
+
+void ChannelProxy::MessageFilter::OnFilterRemoved() {}
+
+void ChannelProxy::MessageFilter::OnChannelConnected(int32 peer_pid) {}
+
+void ChannelProxy::MessageFilter::OnChannelError() {}
+
+void ChannelProxy::MessageFilter::OnChannelClosing() {}
+
+bool ChannelProxy::MessageFilter::OnMessageReceived(const Message& message) {
+ return false;
+}
+
+void ChannelProxy::MessageFilter::OnDestruct() const {
+ delete this;
+}
+
+//------------------------------------------------------------------------------
+
+ChannelProxy::Context::Context(Channel::Listener* listener,
+ base::MessageLoopProxy* ipc_message_loop)
+ : listener_message_loop_(base::MessageLoopProxy::current()),
+ listener_(listener),
+ ipc_message_loop_(ipc_message_loop),
+ peer_pid_(0),
+ channel_connected_called_(false) {
+}
+
+ChannelProxy::Context::~Context() {
+}
+
+void ChannelProxy::Context::CreateChannel(const IPC::ChannelHandle& handle,
+ const Channel::Mode& mode) {
+ DCHECK(channel_.get() == NULL);
+ channel_id_ = handle.name;
+ channel_.reset(new Channel(handle, mode, this));
+}
+
+bool ChannelProxy::Context::TryFilters(const Message& message) {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging* logger = Logging::GetInstance();
+ 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
+bool ChannelProxy::Context::OnMessageReceived(const Message& message) {
+ // First give a chance to the filters to process this message.
+ if (!TryFilters(message))
+ OnMessageReceivedNoFilter(message);
+ return true;
+}
+
+// Called on the IPC::Channel thread
+bool 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));
+ return true;
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) {
+ // Add any pending filters. This avoids a race condition where someone
+ // creates a ChannelProxy, calls AddFilter, and then right after starts the
+ // peer process. The IO thread could receive a message before the task to add
+ // the filter is run on the IO thread.
+ OnAddFilter();
+
+ 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_.get());
+}
+
+// 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_.get())
+ 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();
+
+ channel_.reset();
+
+ // 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_.get()) {
+ delete message;
+ OnChannelClosed();
+ return;
+ }
+ if (!channel_->Send(message))
+ OnChannelError();
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnAddFilter() {
+ std::vector<scoped_refptr<MessageFilter> > new_filters;
+ {
+ base::AutoLock auto_lock(pending_filters_lock_);
+ new_filters.swap(pending_filters_);
+ }
+
+ for (size_t i = 0; i < new_filters.size(); ++i) {
+ filters_.push_back(new_filters[i]);
+
+ // 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_.get())
+ new_filters[i]->OnFilterAdded(channel_.get());
+ // Ditto for if the channel has been connected.
+ if (peer_pid_)
+ new_filters[i]->OnChannelConnected(peer_pid_);
+ }
+}
+
+// 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::AddFilter(MessageFilter* filter) {
+ base::AutoLock auto_lock(pending_filters_lock_);
+ pending_filters_.push_back(make_scoped_refptr(filter));
+ ipc_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &Context::OnAddFilter));
+}
+
+// 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::GetInstance();
+ 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 IPC::ChannelHandle& channel_handle,
+ Channel::Mode mode,
+ Channel::Listener* listener,
+ base::MessageLoopProxy* ipc_thread)
+ : context_(new Context(listener, ipc_thread)),
+ outgoing_message_filter_(NULL) {
+ Init(channel_handle, mode, ipc_thread, true);
+}
+
+ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle,
+ Channel::Mode mode,
+ base::MessageLoopProxy* ipc_thread,
+ Context* context,
+ bool create_pipe_now)
+ : context_(context),
+ outgoing_message_filter_(NULL) {
+ Init(channel_handle, mode, ipc_thread, create_pipe_now);
+}
+
+ChannelProxy::~ChannelProxy() {
+ Close();
+}
+
+void ChannelProxy::Init(const IPC::ChannelHandle& channel_handle,
+ Channel::Mode mode,
+ base::MessageLoopProxy* ipc_thread_loop,
+ bool create_pipe_now) {
+#if defined(OS_POSIX)
+ // When we are creating a server on POSIX, we need its file descriptor
+ // to be created immediately so that it can be accessed and passed
+ // to other processes. Forcing it to be created immediately avoids
+ // race conditions that may otherwise arise.
+ if (mode & Channel::MODE_SERVER_FLAG) {
+ create_pipe_now = true;
+ }
+#endif // defined(OS_POSIX)
+
+ 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_handle, mode);
+ } else {
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::CreateChannel, channel_handle, 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) {
+ if (outgoing_message_filter())
+ message = outgoing_message_filter()->Rewrite(message);
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::GetInstance()->OnSendMessage(message, context_->channel_id());
+#endif
+
+ context_->ipc_message_loop()->PostTask(FROM_HERE,
+ new SendTask(context_.get(), message));
+ return true;
+}
+
+void ChannelProxy::AddFilter(MessageFilter* filter) {
+ context_->AddFilter(filter);
+}
+
+void ChannelProxy::RemoveFilter(MessageFilter* filter) {
+ context_->ipc_message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(
+ context_.get(),
+ &Context::OnRemoveFilter,
+ make_scoped_refptr(filter)));
+}
+
+void ChannelProxy::ClearIPCMessageLoop() {
+ context()->ClearIPCMessageLoop();
+}
+
+#if defined(OS_POSIX) && !defined(OS_NACL)
+// 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_.get();
+ // Channel must have been created first.
+ DCHECK(channel) << context_.get()->channel_id_;
+ return channel->GetClientFileDescriptor();
+}
+
+bool ChannelProxy::GetClientEuid(uid_t* client_euid) const {
+ Channel *channel = context_.get()->channel_.get();
+ // Channel must have been created first.
+ DCHECK(channel) << context_.get()->channel_id_;
+ return channel->GetClientEuid(client_euid);
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
+} // namespace IPC
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
new file mode 100644
index 000000000000..792e3d6a506e
--- /dev/null
+++ b/ipc/ipc_channel_proxy.h
@@ -0,0 +1,272 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_handle.h"
+
+namespace IPC {
+
+class SendTask;
+
+//-----------------------------------------------------------------------------
+// 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 IPC_EXPORT ChannelProxy : public Message::Sender {
+ public:
+
+ struct MessageFilterTraits;
+
+ // 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 IPC_EXPORT MessageFilter
+ : public base::RefCountedThreadSafe<MessageFilter, MessageFilterTraits> {
+ public:
+ MessageFilter();
+ 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);
+
+ // Called when the message filter is about to be deleted. This gives
+ // derived classes the option of controlling which thread they're deleted
+ // on etc.
+ virtual void OnDestruct() const;
+ };
+
+ struct MessageFilterTraits {
+ static void Destruct(const MessageFilter* filter) {
+ filter->OnDestruct();
+ }
+ };
+
+ // Interface for a filter to be imposed on outgoing messages which can
+ // re-write the message. Used mainly for testing.
+ class OutgoingMessageFilter {
+ public:
+ // Returns a re-written message, freeing the original, or simply the
+ // original unchanged if no rewrite indicated.
+ virtual Message *Rewrite(Message *message) = 0;
+ };
+
+ // Initializes a channel proxy. The channel_handle 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 IPC::ChannelHandle& channel_handle,
+ Channel::Mode mode,
+ Channel::Listener* listener,
+ base::MessageLoopProxy* ipc_thread_loop);
+
+ virtual ~ChannelProxy();
+
+ // 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.
+ // If you call this before the target process is launched, then you're
+ // guaranteed to not miss any messages. But if you call this anytime after,
+ // then some messages might be missed since the filter is added internally on
+ // the IO thread.
+ void AddFilter(MessageFilter* filter);
+ void RemoveFilter(MessageFilter* filter);
+
+ void set_outgoing_message_filter(OutgoingMessageFilter* filter) {
+ outgoing_message_filter_ = filter;
+ }
+
+ // Called to clear the pointer to the IPC message loop when it's going away.
+ void ClearIPCMessageLoop();
+
+#if defined(OS_POSIX)
+ // Calls through to the underlying channel's methods.
+ int GetClientFileDescriptor() const;
+ bool GetClientEuid(uid_t* client_euid) 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 IPC::ChannelHandle& channel_handle,
+ Channel::Mode mode,
+ base::MessageLoopProxy* 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, base::MessageLoopProxy* ipc_thread);
+ void ClearIPCMessageLoop() { ipc_message_loop_ = NULL; }
+ base::MessageLoopProxy* ipc_message_loop() const {
+ return ipc_message_loop_.get();
+ }
+ const std::string& channel_id() const { return channel_id_; }
+
+ // Dispatches a message on the listener thread.
+ void OnDispatchMessage(const Message& message);
+
+ protected:
+ friend class base::RefCountedThreadSafe<Context>;
+ virtual ~Context();
+
+ // IPC::Channel::Listener methods:
+ virtual bool OnMessageReceived(const Message& message) OVERRIDE;
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
+ virtual void OnChannelError() OVERRIDE;
+
+ // Like OnMessageReceived but doesn't try the filters.
+ bool 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;
+ friend class SendTask;
+
+ // Create the Channel
+ void CreateChannel(const IPC::ChannelHandle& channel_handle,
+ const Channel::Mode& mode);
+
+ // Methods called on the IO thread.
+ void OnSendMessage(Message* message_ptr);
+ void OnAddFilter();
+ void OnRemoveFilter(MessageFilter* filter);
+
+ // Methods called on the listener thread.
+ void AddFilter(MessageFilter* filter);
+ void OnDispatchConnected();
+ void OnDispatchError();
+
+ scoped_refptr<base::MessageLoopProxy> listener_message_loop_;
+ Channel::Listener* listener_;
+
+ // List of filters. This is only accessed on the IPC thread.
+ std::vector<scoped_refptr<MessageFilter> > filters_;
+ scoped_refptr<base::MessageLoopProxy> ipc_message_loop_;
+ scoped_ptr<Channel> channel_;
+ std::string channel_id_;
+ int peer_pid_;
+ bool channel_connected_called_;
+
+ // Holds filters between the AddFilter call on the listerner thread and the
+ // IPC thread when they're added to filters_.
+ std::vector<scoped_refptr<MessageFilter> > pending_filters_;
+ // Lock for pending_filters_.
+ base::Lock pending_filters_lock_;
+ };
+
+ Context* context() { return context_; }
+
+ OutgoingMessageFilter* outgoing_message_filter() {
+ return outgoing_message_filter_;
+ }
+
+ private:
+ friend class SendTask;
+
+ void Init(const IPC::ChannelHandle& channel_handle, Channel::Mode mode,
+ base::MessageLoopProxy* 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_;
+
+ OutgoingMessageFilter* outgoing_message_filter_;
+};
+
+} // 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 000000000000..3a85a1651fb4
--- /dev/null
+++ b/ipc/ipc_channel_win.cc
@@ -0,0 +1,430 @@
+// Copyright (c) 2011 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 "base/auto_reset.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_handle.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 IPC::ChannelHandle &channel_handle,
+ 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_FLAG),
+ processing_incoming_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+ CreatePipe(channel_handle, mode);
+}
+
+Channel::ChannelImpl::~ChannelImpl() {
+ Close();
+}
+
+void Channel::ChannelImpl::Close() {
+ if (thread_check_.get()) {
+ DCHECK(thread_check_->CalledOnValidThread());
+ }
+
+ if (input_state_.is_pending || output_state_.is_pending)
+ CancelIo(pipe_);
+
+ // 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);
+ }
+
+ while (!output_queue_.empty()) {
+ Message* m = output_queue_.front();
+ output_queue_.pop();
+ delete m;
+ }
+}
+
+bool Channel::ChannelImpl::Send(Message* message) {
+ DCHECK(thread_check_->CalledOnValidThread());
+ DVLOG(2) << "sending message @" << message << " on channel @" << this
+ << " with type " << message->type()
+ << " (" << output_queue_.size() << " in queue)";
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::GetInstance()->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;
+}
+
+// static
+bool Channel::ChannelImpl::IsNamedServerInitialized(
+ const std::string& channel_id) {
+ if (WaitNamedPipe(PipeName(channel_id).c_str(), 1))
+ return true;
+ // If ERROR_SEM_TIMEOUT occurred, the pipe exists but is handling another
+ // connection.
+ return GetLastError() == ERROR_SEM_TIMEOUT;
+}
+
+// static
+const std::wstring Channel::ChannelImpl::PipeName(
+ const std::string& channel_id) {
+ std::string name("\\\\.\\pipe\\chrome.");
+ return ASCIIToWide(name.append(channel_id));
+}
+
+bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle &channel_handle,
+ Mode mode) {
+ DCHECK_EQ(INVALID_HANDLE_VALUE, pipe_);
+ const std::wstring pipe_name = PipeName(channel_handle.name);
+ if (mode & MODE_SERVER_FLAG) {
+ pipe_ = CreateNamedPipeW(pipe_name.c_str(),
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+ FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1,
+ Channel::kReadBufferSize,
+ Channel::kReadBufferSize,
+ 5000,
+ NULL);
+ } else if (mode & MODE_CLIENT_FLAG) {
+ pipe_ = CreateFileW(pipe_name.c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
+ FILE_FLAG_OVERLAPPED,
+ NULL);
+ } else {
+ NOTREACHED();
+ }
+ if (pipe_ == INVALID_HANDLE_VALUE) {
+ // If this process is being closed, the pipe may be gone already.
+ LOG(WARNING) << "Unable to create pipe \"" << pipe_name <<
+ "\" in " << (mode == 0 ? "server" : "client")
+ << " mode. Error :" << 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_IF(WARNING, thread_check_.get()) << "Connect called more than once";
+
+ if (!thread_check_.get())
+ thread_check_.reset(new base::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);
+ DVLOG(2) << "received message on channel @" << this
+ << " with type " << m.type();
+ 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();
+ DCHECK(m->size() <= INT_MAX);
+ BOOL ok = WriteFile(pipe_,
+ m->data(),
+ static_cast<int>(m->size()),
+ &bytes_written,
+ &output_state_.context.overlapped);
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ output_state_.is_pending = true;
+
+ DVLOG(2) << "sent pending message @" << m << " on channel @" << this
+ << " with type " << m->type();
+
+ return true;
+ }
+ LOG(ERROR) << "pipe error: " << err;
+ return false;
+ }
+
+ DVLOG(2) << "sent message @" << m << " on channel @" << this
+ << " with type " << m->type();
+
+ 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_);
+ AutoReset<bool> auto_reset_processing_incoming(&processing_incoming_, true);
+ ok = ProcessIncomingMessages(context, bytes_transfered);
+ } 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 IPC::ChannelHandle &channel_handle, Mode mode,
+ Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_handle, 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);
+}
+
+// static
+bool Channel::IsNamedServerInitialized(const std::string& channel_id) {
+ return ChannelImpl::IsNamedServerInitialized(channel_id);
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_channel_win.h b/ipc/ipc_channel_win.h
new file mode 100644
index 000000000000..ada88ac3c0e4
--- /dev/null
+++ b/ipc/ipc_channel_win.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include "ipc/ipc_channel.h"
+
+#include <queue>
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+
+namespace base {
+class NonThreadSafe;
+}
+
+namespace IPC {
+
+class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
+ public:
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const IPC::ChannelHandle &channel_handle, Mode mode,
+ Listener* listener);
+ ~ChannelImpl();
+ bool Connect();
+ void Close();
+ void set_listener(Listener* listener) { listener_ = listener; }
+ bool Send(Message* message);
+ static bool IsNamedServerInitialized(const std::string& channel_id);
+ private:
+ static const std::wstring PipeName(const std::string& channel_id);
+ bool CreatePipe(const IPC::ChannelHandle &channel_handle, 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<base::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 000000000000..4750fa8e7633
--- /dev/null
+++ b/ipc/ipc_descriptors.h
@@ -0,0 +1,15 @@
+// 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_
+#pragma once
+
+// 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_export.h b/ipc/ipc_export.h
new file mode 100644
index 000000000000..dc38fffa204b
--- /dev/null
+++ b/ipc/ipc_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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_OSP_EXPORT_
+#define IPC_IPC_OSP_EXPORT_
+#pragma once
+
+// Defines IPC_EXPORT so that functionality implemented by the IPC module can be
+// exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(IPC_IMPLEMENTATION)
+#define IPC_EXPORT __declspec(dllexport)
+#else
+#define IPC_EXPORT __declspec(dllimport)
+#endif // defined(IPC_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#define IPC_EXPORT __attribute__((visibility("default")))
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define IPC_EXPORT
+#endif
+
+#endif // IPC_IPC_OSP_EXPORT_
diff --git a/ipc/ipc_logging.cc b/ipc/ipc_logging.cc
new file mode 100644
index 000000000000..138e6acce735
--- /dev/null
+++ b/ipc/ipc_logging.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipc/ipc_logging.h"
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+#define IPC_MESSAGE_MACROS_LOG_ENABLED
+#endif
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/threading/thread.h"
+#include "base/time.h"
+#include "ipc/ipc_switches.h"
+#include "ipc/ipc_sync_message.h"
+#include "ipc/ipc_message_utils.h"
+
+#if defined(OS_POSIX)
+#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.
+DISABLE_RUNNABLE_METHOD_REFCOUNT(IPC::Logging);
+
+namespace IPC {
+
+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.
+LogFunctionMap* Logging::log_function_map_;
+
+Logging::Logging()
+ : enabled_(false),
+ enabled_on_stderr_(false),
+ queue_invoke_later_pending_(false),
+ sender_(NULL),
+ main_thread_(MessageLoop::current()),
+ consumer_(NULL) {
+#if defined(OS_WIN)
+ // getenv triggers an unsafe warning. Simply check how big of a buffer
+ // would be needed to fetch the value to see if the enviornment variable is
+ // set.
+ size_t requiredSize = 0;
+ getenv_s(&requiredSize, NULL, 0, "CHROME_IPC_LOGGING");
+ bool logging_env_var_set = (requiredSize != 0);
+#else // !defined(OS_WIN)
+ bool logging_env_var_set = (getenv("CHROME_IPC_LOGGING") != NULL);
+#endif //defined(OS_WIN)
+ if (logging_env_var_set) {
+ enabled_ = true;
+ enabled_on_stderr_ = true;
+ }
+}
+
+Logging::~Logging() {
+}
+
+Logging* Logging::GetInstance() {
+ return Singleton<Logging>::get();
+}
+
+void Logging::SetConsumer(Consumer* consumer) {
+ consumer_ = consumer;
+}
+
+void Logging::Enable() {
+ enabled_ = true;
+}
+
+void Logging::Disable() {
+ enabled_ = false;
+}
+
+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() ||
+ !message.sent_time() ||
+ !message.received_time() ||
+ 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(uint32 type, std::string* name,
+ const Message* message,
+ std::string* params) {
+ if (!log_function_map_)
+ return;
+
+ LogFunctionMap::iterator it = log_function_map_->find(type);
+ if (it == log_function_map_->end()) {
+ if (name) {
+ *name = "[UNKNOWN MSG ";
+ *name += base::IntToString(type);
+ *name += " ]";
+ }
+ return;
+ }
+
+ (*it->second)(name, message, params);
+}
+
+void Logging::Log(const LogData& data) {
+ 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);
+ }
+ }
+ }
+ if (enabled_on_stderr_) {
+ std::string message_name;
+ if (data.message_name.empty()) {
+ message_name = StringPrintf("[unknown type %d]", data.type);
+ } else {
+ message_name = data.message_name;
+ }
+ fprintf(stderr, "ipc %s %d %s %s %s\n",
+ data.channel.c_str(),
+ data.routing_id,
+ data.flags.c_str(),
+ message_name.c_str(),
+ data.params.c_str());
+ }
+}
+
+void GenerateLogData(const std::string& channel, const Message& message,
+ LogData* data) {
+ if (message.is_reply()) {
+ // "data" should already be filled in.
+ std::string params;
+ Logging::GetMessageText(data->type, NULL, &message, &params);
+
+ if (!data->params.empty() && !params.empty())
+ data->params += ", ";
+
+ data->flags += " DR";
+
+ data->params += params;
+ } else {
+ std::string flags;
+ if (message.is_sync())
+ flags = "S";
+
+ if (message.is_reply())
+ flags += "R";
+
+ if (message.is_reply_error())
+ flags += "E";
+
+ std::string 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 000000000000..42237c7ec61e
--- /dev/null
+++ b/ipc/ipc_logging.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include "ipc/ipc_message.h" // For IPC_MESSAGE_LOG_ENABLED.
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+
+#include <vector>
+
+#include "base/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop.h"
+#include "ipc/ipc_export.h"
+
+// Logging function. |name| is a string in ASCII and |params| is a string in
+// UTF-8.
+typedef void (*LogFunction)(std::string* name,
+ const IPC::Message* msg,
+ std::string* params);
+
+typedef base::hash_map<uint32, LogFunction > LogFunctionMap;
+
+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 IPC_EXPORT Logging {
+ public:
+ // Implemented by consumers of log messages.
+ class Consumer {
+ public:
+ virtual void Log(const LogData& data) = 0;
+
+ protected:
+ virtual ~Consumer() {}
+ };
+
+ void SetConsumer(Consumer* consumer);
+
+ ~Logging();
+ static Logging* GetInstance();
+
+ // Enable and Disable are NOT cross-process; they only affect the
+ // current thread/process. If you want to modify the value for all
+ // processes, perhaps your intent is to call
+ // g_browser_process->SetIPCLoggingEnabled().
+ 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);
+
+ // 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(uint32 type, std::string* name,
+ const Message* message, std::string* params);
+
+ static void set_log_function_map(LogFunctionMap* functions) {
+ log_function_map_ = functions;
+ }
+
+ static LogFunctionMap* log_function_map() {
+ return log_function_map_;
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<Logging>;
+ Logging();
+
+ void OnSendLogs();
+ void Log(const LogData& data);
+
+ bool enabled_;
+ bool enabled_on_stderr_; // only used on POSIX for now
+
+ std::vector<LogData> queued_logs_;
+ bool queue_invoke_later_pending_;
+
+ Message::Sender* sender_;
+ MessageLoop* main_thread_;
+
+ Consumer* consumer_;
+
+ static LogFunctionMap* log_function_map_;
+};
+
+} // 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 000000000000..235a7d592eca
--- /dev/null
+++ b/ipc/ipc_message.cc
@@ -0,0 +1,128 @@
+// 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 "log.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;
+ header()->pad = 0;
+#endif
+ InitLoggingVariables();
+}
+
+Message::Message(int32 routing_id, uint32 type, PriorityValue priority)
+ : Pickle(sizeof(Header)) {
+ header()->routing = routing_id;
+ header()->type = type;
+ header()->flags = priority;
+#if defined(OS_POSIX)
+ header()->num_fds = 0;
+ header()->pad = 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 000000000000..11ed17c4672c
--- /dev/null
+++ b/ipc/ipc_message.h
@@ -0,0 +1,284 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/pickle.h"
+#include "ipc/ipc_export.h"
+
+#ifndef NDEBUG
+//#define IPC_MESSAGE_LOG_ENABLED
+#endif
+
+#if defined(OS_POSIX)
+#include "base/memory/ref_counted.h"
+#endif
+
+namespace base {
+struct FileDescriptor;
+}
+
+class FileDescriptorSet;
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+class Channel;
+class Message;
+struct LogData;
+
+class IPC_EXPORT Message : public Pickle {
+ public:
+ // Implemented by objects that can send IPC messages across a channel.
+ class IPC_EXPORT 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, uint32 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;
+ }
+
+ uint32 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, class S>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ void (T::*func)()) {
+ (obj->*func)();
+ return true;
+ }
+
+ template<class T, class S>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ void (T::*func)() const) {
+ (obj->*func)();
+ return true;
+ }
+
+ template<class T, class S>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ void (T::*func)(const Message&)) {
+ (obj->*func)(*msg);
+ return true;
+ }
+
+ template<class T, class S>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ void (T::*func)(const Message&) const) {
+ (obj->*func)(*msg);
+ return true;
+ }
+
+ // Used for async messages with no parameters.
+ static void Log(std::string* name, const Message* msg, std::string* 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::string& op) const { output_params_ = op; }
+ const std::string& 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, 4)
+ struct Header : Pickle::Header {
+ int32 routing; // ID of the view that this message is destined for
+ uint32 type; // specifies the user-defined message type
+ uint32 flags; // specifies control flags for the message
+#if defined(OS_POSIX)
+ uint16 num_fds; // the number of descriptors included with this message
+ uint16 pad; // explicitly initialize this to appease valgrind
+#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::string 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 0xFFFFFFF0 // Special message id for replies
+#define IPC_LOGGING_ID 0xFFFFFFF1 // 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 000000000000..302d12544e4e
--- /dev/null
+++ b/ipc/ipc_message_macros.h
@@ -0,0 +1,822 @@
+// Copyright (c) 2011 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.
+
+// Defining IPC Messages
+//
+// Your IPC messages will be defined by macros inside of an XXX_messages.h
+// header file. Most of the time, the system can automatically generate all
+// of messaging mechanism from these definitions, but sometimes some manual
+// coding is required. In these cases, you will also have an XXX_messages.cc
+// implemation file as well.
+//
+// The senders of your messages will include your XXX_messages.h file to
+// get the full set of definitions they need to send your messages.
+//
+// Each XXX_messages.h file must be registered with the IPC system. This
+// requires adding two things:
+// - An XXXMsgStart value to the IPCMessageStart enum in ipc_message_utils.h
+// - An inclusion of XXX_messages.h file in a message generator .h file
+//
+// The XXXMsgStart value is an enumeration that ensures uniqueness for
+// each different message file. Later, you will use this inside your
+// XXX_messages.h file before invoking message declatation macros:
+// #define IPC_MESSAGE_START XXXMsgStart
+// ( ... your macro invocations go here ... )
+//
+// Message Generator Files
+//
+// A message generator .h header file pulls in all other message-declaring
+// headers for a given component. It is included by a message generator
+// .cc file, which is where all the generated code will wind up. Typically,
+// you will use an existing generator (e.g. common_message_generator.cc
+// in /chrome/common), but there are circumstances where you may add a
+// new one.
+//
+// In the rare cicrucmstances where you can't re-use an existing file,
+// your YYY_message_generator.cc file for a component YYY would contain
+// the following code:
+// // Get basic type definitions.
+// #define IPC_MESSAGE_IMPL
+// #include "path/to/YYY_message_generator.h"
+// // Generate constructors.
+// #include "ipc/struct_constructor_macros.h"
+// #include "path/to/YYY_message_generator.h"
+// // Generate destructors.
+// #include "ipc/struct_destructor_macros.h"
+// #include "path/to/YYY_message_generator.h"
+// // Generate param traits write methods.
+// #include "ipc/param_traits_write_macros.h"
+// namespace IPC {
+// #include "path/to/YYY_message_generator.h"
+// } // namespace IPC
+// // Generate param traits read methods.
+// #include "ipc/param_traits_read_macros.h"
+// namespace IPC {
+// #include "path/to/YYY_message_generator.h"
+// } // namespace IPC
+// // Generate param traits log methods.
+// #include "ipc/param_traits_log_macros.h"
+// namespace IPC {
+// #include "path/to/YYY_message_generator.h"
+// } // namespace IPC
+//
+// In cases where manual generation is required, in your XXX_messages.cc
+// file, put the following after all the includes for param types:
+// #define IPC_MESSAGE_IMPL
+// #include "XXX_messages.h"
+// (... implementation of traits not auto-generated ...)
+//
+// Multiple Inclusion
+//
+// The XXX_messages.h file will be multiply-included by the
+// YYY_message_generator.cc file, so your XXX_messages file can't be
+// guarded in the usual manner. Ideally, there will be no need for any
+// inclusion guard, since the XXX_messages.h file should consist soley
+// of inclusions of other headers (which are self-guarding) and IPC
+// macros (which are multiply evaluating).
+//
+// Note that there is no #pragma once either; doing so would mark the whole
+// file as being singly-included. Since your XXX_messages.h file is only
+// partially-guarded, care must be taken to ensure that it is only included
+// by other .cc files (and the YYY_message_generator.h file). Including an
+// XXX_messages.h file in some other .h file may result in duplicate
+// declarations and a compilation failure.
+//
+// Type Declarations
+//
+// It is generally a bad idea to have type definitions in a XXX_messages.h
+// file; most likely the typedef will then be used in the message, as opposed
+// to the struct iself. Later, an IPC message dispatcher wil need to call
+// a function taking that type, and that function is declared in some other
+// header. Thus, in order to get the type definition, the other header
+// would have to include the XXX_messages.h file, violating the rule above
+// about not including XXX_messages.h file in other .h files.
+//
+// One approach here is to move these type definitions to another (guarded)
+// .h file and include this second .h in your XXX_messages.h file. This
+// is still less than ideal, because the dispatched function would have to
+// redeclare the typedef or include this second header. This may be
+// reasonable in a few cases.
+//
+// Failing all of the above, then you will want to bracket the smallest
+// possible section of your XXX_messages.h file containing these types
+// with an include guard macro. Be aware that providing an incomplete
+// class type declaration to avoid pulling in a long chain of headers is
+// acceptable when your XXX_messages.h header is being included by the
+// message sending caller's code, but not when the YYY_message_generator.c
+// is building the messages. In addtion, due to the multiple inclusion
+// restriction, these type ought to be guarded. Follow a convention like:
+// #ifndef SOME_GUARD_MACRO
+// #define SOME_GUARD_MACRO
+// class some_class; // One incomplete class declaration
+// class_some_other_class; // Another incomplete class declaration
+// #endif // SOME_GUARD_MACRO
+// #ifdef IPC_MESSAGE_IMPL
+// #inlcude "path/to/some_class.h" // Full class declaration
+// #inlcude "path/to/some_other_class.h" // Full class declaration
+// #endif // IPC_MESSAGE_IMPL
+// (.. IPC macros using some_class and some_other_class ...)
+//
+// Macro Invocations
+//
+// You will use IPC message macro invocations for three things:
+// - New struct definitions for IPC
+// - Registering existing struct and enum definitions with IPC
+// - Defining the messages themselves
+//
+// New structs are defined with IPC_STRUCT_BEGIN(), IPC_STRUCT_MEMBER(),
+// IPC_STRUCT_END() family of macros. These cause the XXX_messages.h
+// to proclaim equivalent struct declarations for use by callers, as well
+// as later registering the type with the message generation. Note that
+// IPC_STRUCT_MEMBER() is only permitted inside matching calls to
+// IPC_STRUCT_BEGIN() / IPC_STRUCT_END().
+//
+// Externally-defined structs are registered with IPC_STRUCT_TRAITS_BEGIN(),
+// IPC_STRUCT_TRAITS_MEMBER(), and IPC_STRUCT_TRAITS_END() macros. These
+// cause registration of the types with message generation only.
+// There's also IPC_STRUCT_TRAITS_PARENT, which is used to register a parent
+// class (whose own traits are already defined). Note that
+// IPC_STRUCT_TRAITS_MEMBER() and IPC_STRUCT_TRAITS_PARENT are only permitted
+// inside matching calls to IPC_STRUCT_TRAITS_BEGIN() /
+// IPC_STRUCT_TRAITS_END().
+//
+// Enum types are registered with a single IPC_ENUM_TRAITS() macro. There
+// is no need to enumerate each value to the IPC mechanism.
+//
+// Do not place semicolons following these IPC_ macro invocations. There
+// is no reason to expect that their expansion corresponds one-to-one with
+// C++ statements.
+//
+// Once the types have been declared / registered, message definitions follow.
+// "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 side. 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);
+
+#ifndef IPC_IPC_MESSAGE_MACROS_H_
+#define IPC_IPC_MESSAGE_MACROS_H_
+
+#include "ipc/ipc_message_utils.h"
+#include "ipc/param_traits_macros.h"
+
+#if defined(IPC_MESSAGE_IMPL)
+#include "ipc/ipc_message_utils_impl.h"
+#endif
+
+// Macros for defining structs. May be subsequently redefined.
+#define IPC_STRUCT_BEGIN(struct_name) \
+ struct struct_name; \
+ IPC_STRUCT_TRAITS_BEGIN(struct_name) \
+ IPC_STRUCT_TRAITS_END() \
+ struct struct_name : IPC::NoParams { \
+ struct_name(); \
+ ~struct_name();
+#define IPC_STRUCT_MEMBER(type, name) type name;
+#define IPC_STRUCT_END() };
+
+// Message macros collect specific numbers of arguments and funnel them into
+// the common message generation macro. These should never be redefined.
+#define IPC_MESSAGE_CONTROL0(msg_class) \
+ IPC_MESSAGE_DECL(EMPTY, CONTROL, msg_class, 0, 0, (), ())
+
+#define IPC_MESSAGE_CONTROL1(msg_class, type1) \
+ IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 1, 0, (type1), ())
+
+#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \
+ IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 2, 0, (type1, type2), ())
+
+#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \
+ IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 3, 0, (type1, type2, type3), ())
+
+#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \
+ IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 4, 0, (type1, type2, type3, type4), ())
+
+#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \
+ IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 5, 0, (type1, type2, type3, type4, type5), ())
+
+#define IPC_MESSAGE_CONTROL6(msg_class, type1, type2, type3, type4, type5, type6) \
+ IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 6, 0, (type1, type2, type3, type4, type5, type6), ())
+
+
+#define IPC_MESSAGE_ROUTED0(msg_class) \
+ IPC_MESSAGE_DECL(EMPTY, ROUTED, msg_class, 0, 0, (), ())
+
+#define IPC_MESSAGE_ROUTED1(msg_class, type1) \
+ IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 1, 0, (type1), ())
+
+#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \
+ IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 2, 0, (type1, type2), ())
+
+#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \
+ IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 3, 0, (type1, type2, type3), ())
+
+#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \
+ IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 4, 0, (type1, type2, type3, type4), ())
+
+#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \
+ IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 5, 0, (type1, type2, type3, type4, type5), ())
+
+#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, type6) \
+ IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 6, 0, (type1, type2, type3, type4, type5, type6), ())
+
+#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 0, (), ())
+
+#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 1, (), (type1_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 2, (), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 3, (), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL0_4(msg_class, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 4, (), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 0, (type1_in), ())
+
+#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 1, (type1_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 2, (type1_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 3, (type1_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 4, (type1_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 0, (type1_in, type2_in), ())
+
+#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 1, (type1_in, type2_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 2, (type1_in, type2_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 3, (type1_in, type2_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL2_4(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 4, (type1_in, type2_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL3_0(msg_class, type1_in, type2_in, type3_in) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 0, (type1_in, type2_in, type3_in), ())
+
+#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 1, (type1_in, type2_in, type3_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 2, (type1_in, type2_in, type3_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 3, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL3_4(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 4, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 0, (type1_in, type2_in, type3_in, type4_in), ())
+
+#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 2, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 3, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL4_4(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 4, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 0, (type1_in, type2_in, type3_in, type4_in, type5_in), ())
+
+#define IPC_SYNC_MESSAGE_CONTROL5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 2, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 3, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL5_4(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 4, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_CONTROL6_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type6_in) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 6, 0, (type1_in, type2_in, type3_in, type4_in, type5_in, type6_in), ())
+
+#define IPC_SYNC_MESSAGE_CONTROL6_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type6_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 6, 1, (type1_in, type2_in, type3_in, type4_in, type5_in, type6_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 0, (), ())
+
+#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 1, (), (type1_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 2, (), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 3, (), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED0_4(msg_class, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 4, (), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 0, (type1_in), ())
+
+#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 1, (type1_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 2, (type1_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 3, (type1_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 4, (type1_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 0, (type1_in, type2_in), ())
+
+#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 1, (type1_in, type2_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 2, (type1_in, type2_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 3, (type1_in, type2_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED2_4(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 4, (type1_in, type2_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 0, (type1_in, type2_in, type3_in), ())
+
+#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 1, (type1_in, type2_in, type3_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 2, (type1_in, type2_in, type3_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 3, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED3_4(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 4, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), ())
+
+#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 2, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 3, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED4_4(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 4, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out, type4_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), ())
+
+#define IPC_SYNC_MESSAGE_ROUTED5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 2, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 3, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out))
+
+#define IPC_SYNC_MESSAGE_ROUTED5_4(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 4, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out, type4_out))
+
+// Common message macro which dispatches into one of the 6 (sync x kind)
+// routines. There is a way that these 6 cases can be lumped together,
+// but the macros get very complicated in that case.
+// Note: intended be redefined to generate other information.
+#define IPC_MESSAGE_DECL(sync, kind, msg_class, \
+ in_cnt, out_cnt, in_list, out_list) \
+ IPC_##sync##_##kind##_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ IPC_MESSAGE_EXTRA(sync, kind, msg_class, in_cnt, out_cnt, in_list, out_list)
+
+#define IPC_EMPTY_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ class __attribute__((visibility("default"))) msg_class : public IPC::Message\
+ { \
+ public: \
+ enum { ID = IPC_MESSAGE_ID() }; \
+ msg_class() : IPC::Message(MSG_ROUTING_CONTROL, ID, PRIORITY_NORMAL) {} \
+ };
+
+#define IPC_EMPTY_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ class __attribute__((visibility("default"))) msg_class : public IPC::Message\
+ { \
+ public: \
+ enum { ID = IPC_MESSAGE_ID() }; \
+ msg_class(int32 routing_id) \
+ : IPC::Message(routing_id, ID, PRIORITY_NORMAL) {} \
+ };
+
+#define IPC_ASYNC_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ class __attribute__((visibility("default"))) msg_class : \
+ public IPC::MessageWithTuple<IPC_TUPLE_IN_##in_cnt in_list> { \
+ public: \
+ enum { ID = IPC_MESSAGE_ID() }; \
+ msg_class(IPC_TYPE_IN_##in_cnt in_list); \
+ virtual ~msg_class(); \
+ static void Log(std::string* name, const Message* msg, std::string* l); \
+ };
+
+#define IPC_ASYNC_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ class __attribute__((visibility("default"))) msg_class : \
+ public IPC::MessageWithTuple<IPC_TUPLE_IN_##in_cnt in_list> { \
+ public: \
+ enum { ID = IPC_MESSAGE_ID() }; \
+ msg_class(int32 routing_id IPC_COMMA_##in_cnt \
+ IPC_TYPE_IN_##in_cnt in_list); \
+ virtual ~msg_class(); \
+ static void Log(std::string* name, const Message* msg, std::string* l); \
+ };
+
+#define IPC_SYNC_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ class __attribute__((visibility("default"))) msg_class : \
+ public IPC::MessageWithReply<IPC_TUPLE_IN_##in_cnt in_list, \
+ IPC_TUPLE_OUT_##out_cnt out_list> { \
+ public: \
+ enum { ID = IPC_MESSAGE_ID() }; \
+ msg_class(IPC_TYPE_IN_##in_cnt in_list \
+ IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \
+ IPC_TYPE_OUT_##out_cnt out_list); \
+ virtual ~msg_class(); \
+ static void Log(std::string* name, const Message* msg, std::string* l); \
+ };
+
+#define IPC_SYNC_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ class __attribute__((visibility("default"))) msg_class : \
+ public IPC::MessageWithReply<IPC_TUPLE_IN_##in_cnt in_list, \
+ IPC_TUPLE_OUT_##out_cnt out_list> { \
+ public: \
+ enum { ID = IPC_MESSAGE_ID() }; \
+ msg_class(int32 routing_id \
+ IPC_COMMA_OR_##in_cnt(IPC_COMMA_##out_cnt) \
+ IPC_TYPE_IN_##in_cnt in_list \
+ IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \
+ IPC_TYPE_OUT_##out_cnt out_list); \
+ virtual ~msg_class(); \
+ static void Log(std::string* name, const Message* msg, std::string* l); \
+ };
+
+#if defined(IPC_MESSAGE_IMPL)
+
+// "Implementation" inclusion produces constructors, destructors, and
+// logging functions, except for the no-arg special cases, where the
+// implementation occurs in the declaration, and there is no special
+// logging function.
+#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \
+ in_cnt, out_cnt, in_list, out_list) \
+ IPC_##sync##_##kind##_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ IPC_##sync##_MESSAGE_LOG(msg_class)
+
+#define IPC_EMPTY_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list)
+#define IPC_EMPTY_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list)
+
+#define IPC_ASYNC_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ msg_class::msg_class(IPC_TYPE_IN_##in_cnt in_list) : \
+ IPC::MessageWithTuple<IPC_TUPLE_IN_##in_cnt in_list> \
+ (MSG_ROUTING_CONTROL, ID, IPC_NAME_IN_##in_cnt in_list) \
+ {} \
+ msg_class::~msg_class() {}
+
+#define IPC_ASYNC_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ msg_class::msg_class(int32 routing_id IPC_COMMA_##in_cnt \
+ IPC_TYPE_IN_##in_cnt in_list) : \
+ IPC::MessageWithTuple<IPC_TUPLE_IN_##in_cnt in_list> \
+ (routing_id, ID, IPC_NAME_IN_##in_cnt in_list) \
+ {} \
+ msg_class::~msg_class() {}
+
+#define IPC_SYNC_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ msg_class::msg_class(IPC_TYPE_IN_##in_cnt in_list \
+ IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \
+ IPC_TYPE_OUT_##out_cnt out_list) : \
+ IPC::MessageWithReply<IPC_TUPLE_IN_##in_cnt in_list, \
+ IPC_TUPLE_OUT_##out_cnt out_list> \
+ (MSG_ROUTING_CONTROL, ID, \
+ IPC_NAME_IN_##in_cnt in_list, \
+ IPC_NAME_OUT_##out_cnt out_list) \
+ { \
+ } \
+ msg_class::~msg_class() {}
+
+#define IPC_SYNC_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \
+ msg_class::msg_class(int32 routing_id \
+ IPC_COMMA_OR_##in_cnt(IPC_COMMA_##out_cnt) \
+ IPC_TYPE_IN_##in_cnt in_list \
+ IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \
+ IPC_TYPE_OUT_##out_cnt out_list) : \
+ IPC::MessageWithReply<IPC_TUPLE_IN_##in_cnt in_list, \
+ IPC_TUPLE_OUT_##out_cnt out_list> \
+ (routing_id, ID, \
+ IPC_NAME_IN_##in_cnt in_list, \
+ IPC_NAME_OUT_##out_cnt out_list) \
+ {} \
+ msg_class::~msg_class() {}
+
+#define IPC_EMPTY_MESSAGE_LOG(msg_class)
+
+#define IPC_ASYNC_MESSAGE_LOG(msg_class) \
+ void msg_class::Log(std::string* name, \
+ const Message* msg, \
+ std::string* l) { \
+ if (name) \
+ { \
+ *name = #msg_class; \
+ } \
+ if (!msg || !l) \
+ { \
+ return; \
+ } \
+ Param p; \
+ if (Read(msg, &p)) \
+ { \
+ IPC::LogParam(p, l); \
+ } \
+ }
+
+#define IPC_SYNC_MESSAGE_LOG(msg_class) \
+ void msg_class::Log(std::string* name, \
+ const Message* msg, \
+ std::string* l) { \
+ if (name) \
+ { \
+ *name = #msg_class; \
+ } \
+ if (!msg || !l) \
+ { \
+ return; \
+ } \
+ if (msg->is_sync()) { \
+ TupleTypes<SendParam>::ValueTuple p; \
+ if (ReadSendParam(msg, &p)) \
+ { \
+ IPC::LogParam(p, l); \
+ } \
+ AddOutputParamsToLog(msg, l); \
+ } else { \
+ TupleTypes<ReplyParam>::ValueTuple p; \
+ if (ReadReplyParam(msg, &p)) \
+ { \
+ IPC::LogParam(p, l); \
+ } \
+ } \
+ }
+
+#elif defined(IPC_MESSAGE_MACROS_LOG_ENABLED)
+
+#ifndef IPC_LOG_TABLE_CREATED
+#define IPC_LOG_TABLE_CREATED
+
+#include "base/hash_tables.h"
+
+typedef void (*LogFunction)(std::string* name,
+ const IPC::Message* msg,
+ std::string* params);
+
+typedef base::hash_map<uint32, LogFunction > LogFunctionMap;
+LogFunctionMap g_log_function_mapping;
+
+#endif // IPC_LOG_TABLE_CREATED
+
+// "Log table" inclusion produces extra logging registration code.
+#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \
+ in_cnt, out_cnt, in_list, out_list) \
+ class __attribute__((visibility("default"))) LoggerRegisterHelper##msg_class { \
+ public: \
+ LoggerRegisterHelper##msg_class() { \
+ g_log_function_mapping[msg_class::ID] = msg_class::Log; \
+ } \
+ }; \
+ LoggerRegisterHelper##msg_class g_LoggerRegisterHelper##msg_class;
+
+#else
+
+// Normal inclusion produces nothing extra.
+#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \
+ in_cnt, out_cnt, in_list, out_list)
+
+#endif // defined(IPC_MESSAGE_IMPL)
+
+// Handle variable sized argument lists. These are usually invoked by token
+// pasting against the argument counts.
+#define IPC_TYPE_IN_0()
+#define IPC_TYPE_IN_1(t1) const t1& arg1
+#define IPC_TYPE_IN_2(t1, t2) const t1& arg1, const t2& arg2
+#define IPC_TYPE_IN_3(t1, t2, t3) const t1& arg1, const t2& arg2, const t3& arg3
+#define IPC_TYPE_IN_4(t1, t2, t3, t4) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4
+#define IPC_TYPE_IN_5(t1, t2, t3, t4, t5) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4, const t5& arg5
+#define IPC_TYPE_IN_6(t1, t2, t3, t4, t5, t6) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4, const t5& arg5, const t6& arg6
+
+#define IPC_TYPE_OUT_0()
+#define IPC_TYPE_OUT_1(t1) t1* arg7
+#define IPC_TYPE_OUT_2(t1, t2) t1* arg7, t2* arg8
+#define IPC_TYPE_OUT_3(t1, t2, t3) t1* arg7, t2* arg8, t3* arg9
+#define IPC_TYPE_OUT_4(t1, t2, t3, t4) t1* arg7, t2* arg8, t3* arg9, t4* arg10
+
+#define IPC_TUPLE_IN_0() Tuple0
+#define IPC_TUPLE_IN_1(t1) Tuple1<t1>
+#define IPC_TUPLE_IN_2(t1, t2) Tuple2<t1, t2>
+#define IPC_TUPLE_IN_3(t1, t2, t3) Tuple3<t1, t2, t3>
+#define IPC_TUPLE_IN_4(t1, t2, t3, t4) Tuple4<t1, t2, t3, t4>
+#define IPC_TUPLE_IN_5(t1, t2, t3, t4, t5) Tuple5<t1, t2, t3, t4, t5>
+#define IPC_TUPLE_IN_6(t1, t2, t3, t4, t5, t6) Tuple6<t1, t2, t3, t4, t5, t6>
+
+#define IPC_TUPLE_OUT_0() Tuple0
+#define IPC_TUPLE_OUT_1(t1) Tuple1<t1&>
+#define IPC_TUPLE_OUT_2(t1, t2) Tuple2<t1&, t2&>
+#define IPC_TUPLE_OUT_3(t1, t2, t3) Tuple3<t1&, t2&, t3&>
+#define IPC_TUPLE_OUT_4(t1, t2, t3, t4) Tuple4<t1&, t2&, t3&, t4&>
+
+#define IPC_NAME_IN_0() MakeTuple()
+#define IPC_NAME_IN_1(t1) MakeRefTuple(arg1)
+#define IPC_NAME_IN_2(t1, t2) MakeRefTuple(arg1, arg2)
+#define IPC_NAME_IN_3(t1, t2, t3) MakeRefTuple(arg1, arg2, arg3)
+#define IPC_NAME_IN_4(t1, t2, t3, t4) MakeRefTuple(arg1, arg2, arg3, arg4)
+#define IPC_NAME_IN_5(t1, t2, t3, t4, t5) MakeRefTuple(arg1, arg2, arg3, arg4, arg5)
+#define IPC_NAME_IN_6(t1, t2, t3, t4, t5, t6) MakeRefTuple(arg1, arg2, arg3, arg4, arg5, arg6)
+
+#define IPC_NAME_OUT_0() MakeTuple()
+#define IPC_NAME_OUT_1(t1) MakeRefTuple(*arg7)
+#define IPC_NAME_OUT_2(t1, t2) MakeRefTuple(*arg7, *arg8)
+#define IPC_NAME_OUT_3(t1, t2, t3) MakeRefTuple(*arg7, *arg8, *arg9)
+#define IPC_NAME_OUT_4(t1, t2, t3, t4) MakeRefTuple(*arg7, *arg8, *arg9, *arg10)
+
+// There are places where the syntax requires a comma if there are input args,
+// if there are input args and output args, or if there are input args or
+// output args. These macros allow generation of the comma as needed; invoke
+// by token pasting against the argument counts.
+#define IPC_COMMA_0
+#define IPC_COMMA_1 ,
+#define IPC_COMMA_2 ,
+#define IPC_COMMA_3 ,
+#define IPC_COMMA_4 ,
+#define IPC_COMMA_5 ,
+#define IPC_COMMA_6 ,
+
+#define IPC_COMMA_AND_0(x)
+#define IPC_COMMA_AND_1(x) x
+#define IPC_COMMA_AND_2(x) x
+#define IPC_COMMA_AND_3(x) x
+#define IPC_COMMA_AND_4(x) x
+#define IPC_COMMA_AND_5(x) x
+#define IPC_COMMA_AND_6(x) x
+
+#define IPC_COMMA_OR_0(x) x
+#define IPC_COMMA_OR_1(x) ,
+#define IPC_COMMA_OR_2(x) ,
+#define IPC_COMMA_OR_3(x) ,
+#define IPC_COMMA_OR_4(x) ,
+#define IPC_COMMA_OR_5(x) ,
+#define IPC_COMMA_OR_6(x) ,
+
+// Message IDs
+// Note: we currently use __LINE__ to give unique IDs to messages within
+// a file. They're globally unique since each file defines its own
+// IPC_MESSAGE_START. Ideally, we wouldn't use line numbers (a possibility
+// is to instead use the __COUNTER__ macro, but it needs gcc 4.3 and xcode
+// doesn't use it yet).
+#define IPC_MESSAGE_ID() ((IPC_MESSAGE_START << 16) + __LINE__)
+#define IPC_MESSAGE_ID_CLASS(id) ((id) >> 16)
+#define IPC_MESSAGE_ID_LINE(id) ((id) & 0xffff)
+
+// 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:
+//
+// bool MyClass::OnMessageReceived(const IPC::Message& msg) {
+// bool handled = true;
+// 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_MESSAGE_UNHANDLED(handled = false)
+// IPC_END_MESSAGE_MAP_EX()
+// if (!msg_is_good) {
+// // Signal error here or terminate offending process.
+// }
+// return handled;
+// }
+
+
+#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, this, &member_func); \
+ break;
+
+#define IPC_MESSAGE_FORWARD_EX(msg_class, obj, sender, member_func) \
+ case msg_class::ID: \
+ msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, sender, &member_func); \
+ break;
+
+#define IPC_MESSAGE_HANDLER(msg_class, member_func, sender) \
+ IPC_MESSAGE_FORWARD_EX(msg_class, this, sender, _IpcMessageHandlerClass::member_func)
+
+#define IPC_MESSAGE_HANDLER_EX(msg_class, sender, member_func) \
+ IPC_MESSAGE_FORWARD_EX(msg_class, this, sender, _IpcMessageHandlerClass::member_func)
+
+#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \
+ case msg_class::ID: \
+ msg_is_ok__ = 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() \
+ } \
+}
+
+#define IPC_END_MESSAGE_MAP_EX() \
+ } \
+}
+
+// This corresponds to an enum value from IPCMessageStart.
+#define IPC_MESSAGE_CLASS(message) \
+ IPC_MESSAGE_ID_CLASS(message.type())
+
+#endif // IPC_IPC_MESSAGE_MACROS_H_
+
+// Clean up IPC_MESSAGE_START in this unguarded section so that the
+// XXX_messages.h files need not do so themselves. This makes the
+// XXX_messages.h files easier to write.
+#undef IPC_MESSAGE_START
diff --git a/ipc/ipc_message_null_macros.h b/ipc/ipc_message_null_macros.h
new file mode 100644
index 000000000000..7cd02f55569d
--- /dev/null
+++ b/ipc/ipc_message_null_macros.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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.
+
+// No include guard, may be included multiple times.
+
+// NULL out all the macros that need NULLing, so that multiple includes of
+// the XXXX_messages_internal.h files will not generate noise.
+#undef IPC_STRUCT_BEGIN
+#undef IPC_STRUCT_MEMBER
+#undef IPC_STRUCT_END
+#undef IPC_STRUCT_TRAITS_BEGIN
+#undef IPC_STRUCT_TRAITS_MEMBER
+#undef IPC_STRUCT_TRAITS_PARENT
+#undef IPC_STRUCT_TRAITS_END
+#undef IPC_ENUM_TRAITS
+#undef IPC_MESSAGE_DECL
+
+#define IPC_STRUCT_BEGIN(struct_name)
+#define IPC_STRUCT_MEMBER(type, name)
+#define IPC_STRUCT_END()
+#define IPC_STRUCT_TRAITS_BEGIN(struct_name)
+#define IPC_STRUCT_TRAITS_MEMBER(name)
+#define IPC_STRUCT_TRAITS_PARENT(type)
+#define IPC_STRUCT_TRAITS_END()
+#define IPC_ENUM_TRAITS(enum_name)
+#define IPC_MESSAGE_DECL(sync, kind, msg_class, \
+ in_cnt, out_cnt, in_list, out_list)
+
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc
new file mode 100644
index 000000000000..2e3f04253fb8
--- /dev/null
+++ b/ipc/ipc_message_utils.cc
@@ -0,0 +1,485 @@
+// Copyright (c) 2011 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/file_path.h"
+//#include "base/json/json_writer.h"
+//#include "base/memory/scoped_ptr.h"
+//#include "base/nullable_string16.h"
+#include "base/string_number_conversions.h"
+#include "base/time.h"
+//#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#if defined(OS_POSIX)
+//#include "ipc/file_descriptor_set_posix.h"
+#endif
+//#include "ipc/ipc_channel_handle.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_DOUBLE: {
+ double val;
+ value->GetAsDouble(&val);
+ WriteParam(m, val);
+ break;
+ }
+ case Value::TYPE_STRING: {
+ std::string val;
+ value->GetAsString(&val);
+ WriteParam(m, val);
+ break;
+ }
+ case Value::TYPE_BINARY: {
+ const base::BinaryValue* binary =
+ static_cast<const base::BinaryValue*>(value);
+ m->WriteData(binary->GetBuffer(), static_cast<int>(binary->GetSize()));
+ break;
+ }
+ case Value::TYPE_DICTIONARY: {
+ const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
+
+ WriteParam(m, static_cast<int>(dict->size()));
+
+ for (DictionaryValue::key_iterator it = dict->begin_keys();
+ it != dict->end_keys(); ++it) {
+ Value* subval;
+ if (dict->GetWithoutPathExpansion(*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::string 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_DOUBLE: {
+ double val;
+ if (!ReadParam(m, iter, &val))
+ return false;
+ *value = Value::CreateDoubleValue(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: {
+ const char* data;
+ int length;
+ if (!m->ReadData(iter, &data, &length))
+ return false;
+ *value = base::BinaryValue::CreateWithCopiedBuffer(data, length);
+ 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<int>::Log(const param_type& p, std::string* l) {
+ //l->append(base::IntToString(p));
+}
+
+void ParamTraits<unsigned int>::Log(const param_type& p, std::string* l) {
+ //l->append(base::UintToString(p));
+}
+
+void ParamTraits<long>::Log(const param_type& p, std::string* l) {
+ //l->append(base::Int64ToString(static_cast<int64>(p)));
+}
+
+void ParamTraits<unsigned long>::Log(const param_type& p, std::string* l) {
+ //l->append(base::Uint64ToString(static_cast<uint64>(p)));
+}
+
+void ParamTraits<long long>::Log(const param_type& p, std::string* l) {
+ //l->append(base::Int64ToString(static_cast<int64>(p)));
+}
+
+void ParamTraits<unsigned long long>::Log(const param_type& p, std::string* l) {
+ //l->append(base::Uint64ToString(p));
+}
+
+
+void ParamTraits<unsigned short>::Write(Message* m, const param_type& p) {
+ m->WriteBytes(&p, sizeof(param_type));
+}
+
+bool ParamTraits<unsigned short>::Read(const Message* m, void** iter,
+ param_type* r) {
+ const char* data;
+ if (!m->ReadBytes(iter, &data, sizeof(param_type)))
+ return false;
+ memcpy(r, data, sizeof(param_type));
+ return true;
+}
+
+/*
+void ParamTraits<unsigned short>::Log(const param_type& p, std::string* l) {
+ l->append(base::UintToString(p));
+}
+
+
+void ParamTraits<base::Time>::Write(Message* m, const param_type& p) {
+ ParamTraits<int64>::Write(m, p.ToInternalValue());
+}
+
+bool ParamTraits<base::Time>::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;
+}
+
+void ParamTraits<base::Time>::Log(const param_type& p, std::string* l) {
+ ParamTraits<int64>::Log(p.ToInternalValue(), l);
+}
+
+void ParamTraits<base::TimeDelta> ::Write(Message* m, const param_type& p) {
+ ParamTraits<int64> ::Write(m, p.InMicroseconds());
+}
+
+bool ParamTraits<base::TimeDelta> ::Read(const Message* m,
+ void** iter,
+ param_type* r) {
+ int64 value;
+ bool ret = ParamTraits<int64> ::Read(m, iter, &value);
+ if (ret)
+ *r = base::TimeDelta::FromMicroseconds(value);
+
+ return ret;
+}
+
+void ParamTraits<base::TimeDelta> ::Log(const param_type& p, std::string* l) {
+ ParamTraits<int64> ::Log(p.InMicroseconds(), l);
+}
+
+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::string* l) {
+ std::string json;
+ base::JSONWriter::Write(&p, false, &json);
+ l->append(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::string* l) {
+ std::string json;
+ base::JSONWriter::Write(&p, false, &json);
+ l->append(json);
+}
+
+void ParamTraits<std::wstring>::Log(const param_type& p, std::string* l) {
+ l->append(WideToUTF8(p));
+}
+
+void ParamTraits<NullableString16>::Write(Message* m, const param_type& p) {
+ WriteParam(m, p.string());
+ WriteParam(m, p.is_null());
+}
+
+bool ParamTraits<NullableString16>::Read(const Message* m, void** iter,
+ param_type* r) {
+ string16 string;
+ if (!ReadParam(m, iter, &string))
+ return false;
+ bool is_null;
+ if (!ReadParam(m, iter, &is_null))
+ return false;
+ *r = NullableString16(string, is_null);
+ return true;
+}
+
+void ParamTraits<NullableString16>::Log(const param_type& p, std::string* l) {
+ l->append("(");
+ LogParam(p.string(), l);
+ l->append(", ");
+ LogParam(p.is_null(), l);
+ l->append(")");
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+void ParamTraits<string16>::Log(const param_type& p, std::string* l) {
+ l->append(UTF16ToUTF8(p));
+}
+#endif
+
+*/
+
+/*
+void ParamTraits<FilePath>::Write(Message* m, const param_type& p) {
+ ParamTraits<FilePath::StringType>::Write(m, p.value());
+}
+
+bool ParamTraits<FilePath>::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;
+}
+
+void ParamTraits<FilePath>::Log(const param_type& p, std::string* l) {
+ ParamTraits<FilePath::StringType>::Log(p.value(), l);
+}
+
+#if defined(OS_POSIX)
+void ParamTraits<base::FileDescriptor>::Write(Message* m, const param_type& p) {
+ const bool valid = p.fd >= 0;
+ WriteParam(m, valid);
+
+ if (valid) {
+ if (!m->WriteFileDescriptor(p))
+ NOTREACHED();
+ }
+}
+
+bool ParamTraits<base::FileDescriptor>::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);
+}
+
+void ParamTraits<base::FileDescriptor>::Log(const param_type& p,
+ std::string* l) {
+ if (p.auto_close) {
+ l->append(StringPrintf("FD(%d auto-close)", p.fd));
+ } else {
+ l->append(StringPrintf("FD(%d)", p.fd));
+ }
+}
+#endif // defined(OS_POSIX)
+
+void ParamTraits<IPC::ChannelHandle>::Write(Message* m, const param_type& p) {
+ WriteParam(m, p.name);
+#if defined(OS_POSIX)
+ WriteParam(m, p.socket);
+#endif
+}
+
+bool ParamTraits<IPC::ChannelHandle>::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
+ ;
+}
+
+void ParamTraits<IPC::ChannelHandle>::Log(const param_type& p,
+ std::string* l) {
+ l->append(StringPrintf("ChannelHandle(%s", p.name.c_str()));
+#if defined(OS_POSIX)
+ ParamTraits<base::FileDescriptor>::Log(p.socket, l);
+#endif
+ l->append(")");
+}
+
+LogData::LogData()
+ : routing_id(0),
+ type(0),
+ sent(0),
+ receive(0),
+ dispatch(0) {
+}
+
+LogData::~LogData() {
+}
+
+void ParamTraits<LogData>::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);
+}
+
+bool ParamTraits<LogData>::Read(const Message* m, void** iter, param_type* r) {
+ int type = -1;
+ 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;
+}
+*/
+} // namespace IPC
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
new file mode 100755
index 000000000000..8c2014f35056
--- /dev/null
+++ b/ipc/ipc_message_utils.h
@@ -0,0 +1,1238 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include <algorithm>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+//#include "base/format_macros.h"
+//#include "base/string16.h"
+//#include "base/stringprintf.h"
+//#include "base/string_util.h"
+#include "base/tuple.h"
+#include "ipc/ipc_param_traits.h"
+#include "ipc/ipc_sync_message.h"
+
+#if defined(COMPILER_GCC)
+// GCC "helpfully" tries to inline template methods in release mode. Except we
+// want the majority of the template junk being expanded once in the
+// implementation file (and only provide the definitions in
+// ipc_message_utils_impl.h in those files) and exported, instead of expanded
+// at every call site. Special note: GCC happily accepts the attribute before
+// the method declaration, but only acts on it if it is after.
+#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40500
+// Starting in gcc 4.5, the noinline no longer implies the concept covered by
+// the introduced noclone attribute, which will create specialized versions of
+// functions/methods when certain types are constant.
+// www.gnu.org/software/gcc/gcc-4.5/changes.html
+#define IPC_MSG_NOINLINE __attribute__((noinline, noclone));
+#else
+#define IPC_MSG_NOINLINE __attribute__((noinline));
+#endif
+#elif defined(COMPILER_MSVC)
+// MSVC++ doesn't do this.
+#define IPC_MSG_NOINLINE
+#else
+#error "Please add the noinline property for your new compiler here."
+#endif
+
+// 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 {
+ AutomationMsgStart = 0,
+ PushServiceMsgStart,
+ CmcStrRegistryManagerStart,
+ LastIPCMsgStart // Must come last.
+};
+
+class FilePath;
+class NullableString16;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class Time;
+class TimeDelta;
+struct FileDescriptor;
+}
+
+namespace IPC {
+
+struct ChannelHandle;
+
+//-----------------------------------------------------------------------------
+// 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 = -1;
+ if (!msg_.ReadInt(&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;
+ }
+*/
+ void NextData(const char** data, int* length) const {
+ if (!msg_.ReadData(&iter_, data, length)) {
+ NOTREACHED();
+ }
+ }
+ private:
+ const Message& msg_;
+ mutable void* iter_;
+};
+
+//-----------------------------------------------------------------------------
+// A dummy struct to place first just to allow leading commas for all
+// members in the macro-generated constructor initializer lists.
+struct NoParams {
+};
+
+//-----------------------------------------------------------------------------
+// ParamTraits specializations, etc.
+
+template <class P>
+static inline void WriteParam(Message* m, const P& p) {
+ typedef typename SimilarTypeTraits<P>::Type Type;
+ ParamTraits<Type>::Write(m, static_cast<const Type& >(p));
+}
+
+template <class P>
+static inline bool WARN_UNUSED_RESULT ReadParam(const Message* m, void** iter,
+ P* p) {
+ typedef typename SimilarTypeTraits<P>::Type Type;
+ return ParamTraits<Type>::Read(m, iter, reinterpret_cast<Type* >(p));
+}
+
+
+template <class P>
+static inline void LogParam(const P& p, std::string* l) {
+ typedef typename SimilarTypeTraits<P>::Type Type;
+ ParamTraits<Type>::Log(static_cast<const Type& >(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::string* l) {
+ l->append(p ? "true" : "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);
+ }
+ IPC_EXPORT static void Log(const param_type& p, std::string* l);
+};
+
+template <>
+struct ParamTraits<unsigned int> {
+ typedef unsigned 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, reinterpret_cast<int*>(r));
+ }
+ IPC_EXPORT static void Log(const param_type& p, std::string* l);
+};
+
+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);
+ }
+ IPC_EXPORT static void Log(const param_type& p, std::string* l);
+};
+
+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) {
+ return m->ReadLong(iter, reinterpret_cast<long*>(r));
+ }
+ IPC_EXPORT static void Log(const param_type& p, std::string* l);
+};
+
+template <>
+struct ParamTraits<long long> {
+ typedef long long 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));
+ }
+ IPC_EXPORT static void Log(const param_type& p, std::string* l);
+};
+
+template <>
+struct ParamTraits<unsigned long long> {
+ typedef unsigned long long 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, reinterpret_cast<int64*>(r));
+ }
+ IPC_EXPORT static void Log(const param_type& p, std::string* l);
+};
+
+template <>
+struct IPC_EXPORT ParamTraits<unsigned short> {
+ typedef unsigned short 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::string* l);
+};
+
+// Note that the IPC layer doesn't sanitize NaNs and +/- INF values. Clients
+// should be sure to check the sanity of these values after receiving them over
+// IPC.
+template <>
+struct ParamTraits<float> {
+ typedef float 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;
+ if (!m->ReadData(iter, &data, &data_size) ||
+ data_size != sizeof(param_type)) {
+ NOTREACHED();
+ return false;
+ }
+ memcpy(r, data, sizeof(param_type));
+ return true;
+ }
+
+ static void Log(const param_type& p, std::string* l) {
+ //l->append(StringPrintf("%e", 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;
+ if (!m->ReadData(iter, &data, &data_size) ||
+ data_size != sizeof(param_type)) {
+ NOTREACHED();
+ return false;
+ }
+ memcpy(r, data, sizeof(param_type));
+ return true;
+ }
+
+ static void Log(const param_type& p, std::string* l) {
+ //l->append(StringPrintf("%e", p));
+ }
+
+};
+
+/*
+template <>
+struct IPC_EXPORT ParamTraits<base::Time> {
+ typedef base::Time 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::string* l);
+};
+
+template <>
+struct IPC_EXPORT ParamTraits<base::TimeDelta> {
+ typedef base::TimeDelta 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::string* 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::string* l) {
+// l->append(StringPrintf("<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;
+ }
+ static void Log(const param_type& p, std::string* l) {
+ l->append("<MSG>");
+ }
+};
+#endif // defined(OS_WIN)
+
+template <>
+struct IPC_EXPORT ParamTraits<base::DictionaryValue> {
+ typedef base::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::string* l);
+};
+
+template <>
+struct IPC_EXPORT ParamTraits<base::ListValue> {
+ typedef base::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::string* 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::string* l) {
+ l->append(p);
+ }
+};
+
+/*
+template<typename CharType>
+static void LogBytes(const std::vector<CharType>& data, std::string* out) {
+#if defined(OS_WIN)
+ // Windows has a GUI for logging, which can handle arbitrary binary data.
+ for (size_t i = 0; i < data.size(); ++i)
+ out->push_back(data[i]);
+#else
+ // On POSIX, we log to stdout, which we assume can display ASCII.
+ static const size_t kMaxBytesToLog = 100;
+ for (size_t i = 0; i < std::min(data.size(), kMaxBytesToLog); ++i) {
+ if (isprint(data[i]))
+ out->push_back(data[i]);
+ else
+ {
+ // out->append(StringPrintf("[%02X]", static_cast<unsigned char>(data[i])));
+ }
+ }
+ if (data.size() > kMaxBytesToLog) {
+// out->append(
+// StringPrintf(" and %u more bytes",
+// static_cast<unsigned>(data.size() - kMaxBytesToLog)));
+ }
+#endif
+}
+*/
+
+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.empty()) {
+ 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::string* l) {
+ // LogBytes(p, l);
+ }
+};
+
+template <>
+struct ParamTraits<std::vector<char> > {
+ typedef std::vector<char> param_type;
+ static void Write(Message* m, const param_type& p) {
+ if (p.empty()) {
+ 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::string* l) {
+ //LogBytes(p, l);
+ }
+};
+
+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;
+ // ReadLength() checks for < 0 itself.
+ if (!m->ReadLength(iter, &size))
+ return false;
+ // Resizing beforehand is not safe, see BUG 1006367 for details.
+ if (INT_MAX / sizeof(P) <= static_cast<size_t>(size))
+ return false;
+ r->resize(size);
+ for (int i = 0; i < size; i++) {
+ if (!ReadParam(m, iter, &(*r)[i]))
+ return false;
+ }
+ return true;
+ }
+ static void Log(const param_type& p, std::string* l) {
+// for (size_t i = 0; i < p.size(); ++i) {
+// if (i != 0)
+// l->append(" ");
+// LogParam((p[i]), l);
+// }
+ }
+};
+
+/*
+template <class P>
+struct ParamTraits<std::set<P> > {
+ typedef std::set<P> 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);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int size;
+ if (!m->ReadLength(iter, &size))
+ return false;
+ for (int i = 0; i < size; ++i) {
+ P item;
+ if (!ReadParam(m, iter, &item))
+ return false;
+ r->insert(item);
+ }
+ return true;
+ }
+ static void Log(const param_type& p, std::string* l) {
+ l->append("<std::set>");
+ }
+};
+
+
+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::string* l) {
+ l->append("<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);
+ }
+ IPC_EXPORT static void Log(const param_type& p, std::string* l);
+};
+
+template <class A, class B>
+struct ParamTraits<std::pair<A, B> > {
+ typedef std::pair<A, B> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.first);
+ WriteParam(m, p.second);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return ReadParam(m, iter, &r->first) && ReadParam(m, iter, &r->second);
+ }
+ static void Log(const param_type& p, std::string* l) {
+ l->append("(");
+ LogParam(p.first, l);
+ l->append(", ");
+ LogParam(p.second, l);
+ l->append(")");
+ }
+};
+
+template <>
+struct IPC_EXPORT ParamTraits<NullableString16> {
+ typedef NullableString16 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::string* l);
+};
+
+// 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);
+ }
+ IPC_EXPORT static void Log(const param_type& p, std::string* l);
+};
+#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) {
+ // Note that HWNDs/HANDLE/HCURSOR/HACCEL etc are always 32 bits, even on 64
+ // bit systems.
+ m->WriteUInt32(reinterpret_cast<uint32>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(uint32));
+ return m->ReadUInt32(iter, reinterpret_cast<uint32*>(r));
+ }
+ static void Log(const param_type& p, std::string* l) {
+// l->append(StringPrintf("0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraits<HCURSOR> {
+ typedef HCURSOR param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteUInt32(reinterpret_cast<uint32>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(uint32));
+ return m->ReadUInt32(iter, reinterpret_cast<uint32*>(r));
+ }
+ static void Log(const param_type& p, std::string* l) {
+// l->append(StringPrintf("0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraits<HACCEL> {
+ typedef HACCEL param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteUInt32(reinterpret_cast<uint32>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(uint32));
+ return m->ReadUInt32(iter, reinterpret_cast<uint32*>(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::string* l) {
+// l->append(StringPrintf("(%d, %d)", p.x, p.y));
+ }
+};
+#endif // defined(OS_WIN)
+
+template <>
+struct IPC_EXPORT ParamTraits<FilePath> {
+ typedef FilePath 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::string* 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 IPC_EXPORT ParamTraits<base::FileDescriptor> {
+ typedef base::FileDescriptor 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::string* l);
+};
+#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 IPC_EXPORT ParamTraits<IPC::ChannelHandle> {
+ typedef ChannelHandle 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::string* 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::string* l) {
+ l->append("<XFORM>");
+ }
+};
+#endif // defined(OS_WIN)
+
+struct IPC_EXPORT LogData {
+ LogData();
+ ~LogData();
+
+ std::string channel;
+ int32 routing_id;
+ uint32 type; // "User-defined" message type, from ipc_message.h.
+ std::string 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::string message_name;
+ std::string params;
+};
+
+template <>
+struct IPC_EXPORT ParamTraits<LogData> {
+ typedef LogData 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::string* l) {
+ // Doesn't make sense to implement this!
+ }
+};
+
+*/
+template <>
+struct ParamTraits<Message> {
+ static void Write(Message* m, const Message& p) {
+ DCHECK(p.size() <= INT_MAX);
+ int message_size = static_cast<int>(p.size());
+ m->WriteInt(message_size);
+ m->WriteData(reinterpret_cast<const char*>(p.data()), message_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::string* l) {
+ l->append("<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::string* 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::string* 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::string* l) {
+ LogParam(p.a, l);
+ l->append(", ");
+ 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::string* l) {
+ LogParam(p.a, l);
+ l->append(", ");
+ LogParam(p.b, l);
+ l->append(", ");
+ 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::string* l) {
+ LogParam(p.a, l);
+ l->append(", ");
+ LogParam(p.b, l);
+ l->append(", ");
+ LogParam(p.c, l);
+ l->append(", ");
+ 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::string* l) {
+ LogParam(p.a, l);
+ l->append(", ");
+ LogParam(p.b, l);
+ l->append(", ");
+ LogParam(p.c, l);
+ l->append(", ");
+ LogParam(p.d, l);
+ l->append(", ");
+ LogParam(p.e, l);
+ }
+};
+
+template <class A, class B, class C, class D, class E, class F>
+struct ParamTraits< Tuple6<A, B, C, D, E, F> > {
+typedef Tuple6<A, B, C, D, E, F> 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);
+ WriteParam(m, p.f);
+ }
+ 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) &&
+ ReadParam(m, iter, &r->f));
+ }
+ static void Log(const param_type& p, std::string* l) {
+ LogParam(p.a, l);
+ l->append(", ");
+ LogParam(p.b, l);
+ l->append(", ");
+ LogParam(p.c, l);
+ l->append(", ");
+ LogParam(p.d, l);
+ l->append(", ");
+ LogParam(p.e, l);
+ l->append(", ");
+ LogParam(p.f, l);
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Generic message subclasses
+
+// Used for asynchronous messages.
+template <class ParamType>
+class __attribute__((visibility("default"))) MessageWithTuple : public Message {
+ public:
+ typedef ParamType Param;
+ typedef typename TupleTypes<ParamType>::ParamTuple RefParam;
+
+ // The constructor and the Read() method's templated implementations are in
+ // ipc_message_utils_impl.h. The subclass constructor and Log() methods call
+ // the templated versions of these and make sure there are instantiations in
+ // those translation units.
+ MessageWithTuple(int32 routing_id, uint32 type, const RefParam& p);
+
+ static bool Read(const Message* msg, Param* p) IPC_MSG_NOINLINE;
+
+ // Generic dispatcher. Should cover most cases.
+ template<class T, class S, class Method>
+ static bool Dispatch(const Message* msg, T* obj, S* sender, 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, class S, typename TA>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ void (T::*func)(const Message&, TA)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, class S, typename TA, typename TB>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ 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, class S, typename TA, typename TB, typename TC>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ 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, class S, typename TA, typename TB, typename TC, typename TD>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ 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, class S, typename TA, typename TB, typename TC, typename TD,
+ typename TE>
+ static bool Dispatch(const Message* msg, T* obj, S* sender,
+ 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;
+ }
+
+ // 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;
+ }
+};
+
+// defined in ipc_logging.cc
+IPC_EXPORT void GenerateLogData(const std::string& channel,
+ const Message& message,
+ LogData* data);
+
+
+#if defined(IPC_MESSAGE_LOG_ENABLED)
+inline void AddOutputParamsToLog(const Message* msg, std::string* l) {
+ const std::string& output_params = msg->output_params();
+ if (!l->empty() && !output_params.empty())
+ l->append(", ");
+
+ l->append(output_params);
+}
+
+template <class ReplyParamType>
+inline void LogReplyParamsToMessage(const ReplyParamType& reply_params,
+ const Message* msg) {
+ if (msg->received_time() != 0) {
+ std::string output_params;
+ LogParam(reply_params, &output_params);
+ msg->set_output_params(output_params);
+ }
+}
+
+inline void ConnectMessageAndReply(const Message* msg, Message* reply) {
+ 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);
+ }
+}
+#else
+inline void AddOutputParamsToLog(const Message* msg, std::string* l) {}
+
+template <class ReplyParamType>
+inline void LogReplyParamsToMessage(const ReplyParamType& reply_params,
+ const Message* msg) {}
+
+inline void ConnectMessageAndReply(const Message* msg, Message* reply) {}
+#endif
+
+// This class assumes that its template argument is a RefTuple (a Tuple with
+// reference elements). This would go into ipc_message_utils_impl.h, but it is
+// also used by chrome_frame.
+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_;
+};
+
+// Used for synchronous messages.
+template <class SendParamType, class ReplyParamType>
+class __attribute__((visibility("default"))) MessageWithReply : public SyncMessage {
+ public:
+ typedef SendParamType SendParam;
+ typedef typename TupleTypes<SendParam>::ParamTuple RefSendParam;
+ typedef ReplyParamType ReplyParam;
+
+ MessageWithReply(int32 routing_id, uint32 type,
+ const RefSendParam& send, const ReplyParam& reply);
+ static bool ReadSendParam(const Message* msg, SendParam* p) IPC_MSG_NOINLINE;
+ static bool ReadReplyParam(
+ const Message* msg,
+ typename TupleTypes<ReplyParam>::ValueTuple* p) IPC_MSG_NOINLINE;
+
+ template<class T, class S, class Method>
+ static bool Dispatch(const Message* msg, T* obj, S* sender, Method func) {
+ SendParam send_params;
+ Message* reply = GenerateReply(msg);
+ bool error;
+ if (ReadSendParam(msg, &send_params)) {
+ typename TupleTypes<ReplyParam>::ValueTuple reply_params;
+ DispatchToMethod(obj, func, send_params, &reply_params);
+ WriteParam(reply, reply_params);
+ error = false;
+ LogReplyParamsToMessage(reply_params, msg);
+ } else {
+ NOTREACHED() << "Error deserializing message " << msg->type();
+ reply->set_reply_error();
+ error = true;
+ }
+
+ sender->Send(reply);
+ return !error;
+ }
+
+ template<class T, class Method>
+ static bool DispatchDelayReply(const Message* msg, T* obj, Method func) {
+ SendParam send_params;
+ Message* reply = GenerateReply(msg);
+ bool error;
+ if (ReadSendParam(msg, &send_params)) {
+ Tuple1<Message&> t = MakeRefTuple(*reply);
+ ConnectMessageAndReply(msg, reply);
+ 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_message_utils_impl.h b/ipc/ipc_message_utils_impl.h
new file mode 100644
index 000000000000..7e512a62c28b
--- /dev/null
+++ b/ipc/ipc_message_utils_impl.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains templates forward declared (but not defined) in
+// ipc_message_utils.h so that they are only instantiated in certain files,
+// notably a few IPC unit tests.
+
+#ifndef IPC_IPC_MESSAGE_UTILS_IMPL_H_
+#define IPC_IPC_MESSAGE_UTILS_IMPL_H_
+
+namespace IPC {
+
+template <class ParamType>
+MessageWithTuple<ParamType>::MessageWithTuple(
+ int32 routing_id, uint32 type, const RefParam& p)
+ : Message(routing_id, type, PRIORITY_NORMAL) {
+ WriteParam(this, p);
+}
+
+template <class ParamType>
+bool MessageWithTuple<ParamType>::Read(const Message* msg, Param* p) {
+ void* iter = NULL;
+ if (ReadParam(msg, &iter, p))
+ return true;
+ NOTREACHED() << "Error deserializing message " << msg->type();
+ return false;
+}
+
+// We can't migrate the template for Log() to MessageWithTuple, because each
+// subclass needs to have Log() to call Read(), which instantiates the above
+// template.
+
+template <class SendParamType, class ReplyParamType>
+MessageWithReply<SendParamType, ReplyParamType>::MessageWithReply(
+ int32 routing_id, uint32 type,
+ const RefSendParam& send,
+ const ReplyParam& reply)
+ : SyncMessage(routing_id, type, PRIORITY_NORMAL,
+ new ParamDeserializer<ReplyParam>(reply))
+{
+ WriteParam(this, send);
+}
+
+template <class SendParamType, class ReplyParamType>
+bool MessageWithReply<SendParamType, ReplyParamType>::ReadSendParam(
+ const Message* msg, SendParam* p) {
+ void* iter = SyncMessage::GetDataIterator(msg);
+ return ReadParam(msg, &iter, p);
+}
+
+template <class SendParamType, class ReplyParamType>
+bool MessageWithReply<SendParamType, ReplyParamType>::ReadReplyParam(
+ const Message* msg, typename TupleTypes<ReplyParam>::ValueTuple* p) {
+ void* iter = SyncMessage::GetDataIterator(msg);
+ return ReadParam(msg, &iter, p);
+}
+
+} // namespace IPC
+
+#endif // IPC_IPC_MESSAGE_UTILS_IMPL_H_
diff --git a/ipc/ipc_param_traits.h b/ipc/ipc_param_traits.h
new file mode 100644
index 000000000000..e90be18a1b21
--- /dev/null
+++ b/ipc/ipc_param_traits.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPC_IPC_PARAM_TRAITS_H_
+#define IPC_IPC_PARAM_TRAITS_H_
+#pragma once
+
+// Our IPC system uses the following partially specialized header to define how
+// a data type is read, written and logged in the IPC system.
+
+namespace IPC {
+
+template <class P> struct ParamTraits {
+};
+
+template <class P>
+struct SimilarTypeTraits {
+ typedef P Type;
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_PARAM_TRAITS_H_
diff --git a/ipc/ipc_platform_file.cc b/ipc/ipc_platform_file.cc
new file mode 100644
index 000000000000..2b15cebfb885
--- /dev/null
+++ b/ipc/ipc_platform_file.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 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_platform_file.h"
+
+namespace IPC {
+
+PlatformFileForTransit GetFileHandleForProcess(base::PlatformFile handle,
+ base::ProcessHandle process,
+ bool close_source_handle) {
+ IPC::PlatformFileForTransit out_handle;
+#if defined(OS_WIN)
+ DWORD options = DUPLICATE_SAME_ACCESS;
+ if (close_source_handle)
+ options |= DUPLICATE_CLOSE_SOURCE;
+ if (!::DuplicateHandle(::GetCurrentProcess(),
+ handle,
+ process,
+ &out_handle,
+ 0,
+ FALSE,
+ options)) {
+ out_handle = IPC::InvalidPlatformFileForTransit();
+ }
+#elif defined(OS_POSIX)
+ // If asked to close the source, we can simply re-use the source fd instead of
+ // dup()ing and close()ing.
+ // When we're not closing the source, we need to duplicate the handle and take
+ // ownership of that. The reason is that this function is often used to
+ // generate IPC messages, and the handle must remain valid until it's sent to
+ // the other process from the I/O thread. Without the dup, calling code might
+ // close the source handle before the message is sent, creating a race
+ // condition.
+ int fd = close_source_handle ? handle : ::dup(handle);
+ out_handle = base::FileDescriptor(fd, true);
+#else
+ #error Not implemented.
+#endif
+ return out_handle;
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_platform_file.h b/ipc/ipc_platform_file.h
new file mode 100644
index 000000000000..daaf21cb14b2
--- /dev/null
+++ b/ipc/ipc_platform_file.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 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_PLATFORM_FILE_H_
+#define IPC_IPC_PLATFORM_FILE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/platform_file.h"
+#include "base/process.h"
+#include "ipc/ipc_export.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
+namespace IPC {
+
+#if defined(OS_WIN)
+typedef base::PlatformFile PlatformFileForTransit;
+#elif defined(OS_POSIX)
+typedef base::FileDescriptor PlatformFileForTransit;
+#endif
+
+inline PlatformFileForTransit InvalidPlatformFileForTransit() {
+#if defined(OS_WIN)
+ return base::kInvalidPlatformFileValue;
+#elif defined(OS_POSIX)
+ return base::FileDescriptor();
+#endif
+}
+
+inline base::PlatformFile PlatformFileForTransitToPlatformFile(
+ const PlatformFileForTransit& transit) {
+#if defined(OS_WIN)
+ return transit;
+#elif defined(OS_POSIX)
+ return transit.fd;
+#endif
+}
+
+// Returns a file handle equivalent to |file| that can be used in |process|.
+IPC_EXPORT PlatformFileForTransit GetFileHandleForProcess(
+ base::PlatformFile file,
+ base::ProcessHandle process,
+ bool close_source_handle);
+
+} // namespace IPC
+
+#endif // IPC_IPC_PLATFORM_FILE_H_
diff --git a/ipc/ipc_switches.cc b/ipc/ipc_switches.cc
new file mode 100644
index 000000000000..bcb12256e545
--- /dev/null
+++ b/ipc/ipc_switches.cc
@@ -0,0 +1,24 @@
+// 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.
+
+// The value of this switch tells the child process which
+// IPC channel the browser expects to use to communicate with it.
+const char kProcessChannelID[] = "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 char kDebugChildren[] = "debug-children";
+
+} // namespace switches
+
diff --git a/ipc/ipc_switches.h b/ipc/ipc_switches.h
new file mode 100644
index 000000000000..7c7a833028df
--- /dev/null
+++ b/ipc/ipc_switches.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#include "ipc/ipc_export.h"
+
+namespace switches {
+
+IPC_EXPORT extern const char kProcessChannelID[];
+IPC_EXPORT extern const char 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 000000000000..c135ef38eb3f
--- /dev/null
+++ b/ipc/ipc_sync_channel.cc
@@ -0,0 +1,513 @@
+// Copyright (c) 2011 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/threading/thread_local.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/synchronization/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;
+ }
+
+ // Called on IPC thread when a synchronous message or reply arrives.
+ void QueueMessage(const Message& msg, SyncChannel::SyncContext* context) {
+ bool was_task_pending;
+ {
+ base::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,
+ scoped_refptr<SyncContext>(context)));
+ }
+ }
+
+ 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(SyncContext* context) {
+ {
+ base::AutoLock auto_lock(message_lock_);
+ task_pending_ = false;
+ }
+ context->DispatchMessages();
+ }
+
+ void DispatchMessages(SyncContext* dispatching_context) {
+ SyncMessageQueue delayed_queue;
+ while (true) {
+ Message* message;
+ scoped_refptr<SyncChannel::SyncContext> context;
+ {
+ base::AutoLock auto_lock(message_lock_);
+ if (message_queue_.empty()) {
+ message_queue_ = delayed_queue;
+ break;
+ }
+
+ message = message_queue_.front().message;
+ context = message_queue_.front().context;
+ message_queue_.pop_front();
+ }
+ if (context->restrict_dispatch() && context != dispatching_context) {
+ delayed_queue.push_back(QueuedMessage(message, context));
+ } else {
+ context->OnDispatchMessage(*message);
+ delete message;
+ }
+ }
+ }
+
+ // SyncChannel calls this in its destructor.
+ void RemoveContext(SyncContext* context) {
+ base::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_; }
+ base::MessageLoopProxy* 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;
+ }
+ }
+ }
+
+ base::WaitableEventWatcher* top_send_done_watcher() {
+ return top_send_done_watcher_;
+ }
+
+ void set_top_send_done_watcher(base::WaitableEventWatcher* watcher) {
+ top_send_done_watcher_ = watcher;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<ReceivedSyncMsgQueue>;
+
+ // See the comment in SyncChannel::SyncChannel for why this event is created
+ // as manual reset.
+ ReceivedSyncMsgQueue() :
+ dispatch_event_(true, false),
+ listener_message_loop_(base::MessageLoopProxy::current()),
+ task_pending_(false),
+ listener_count_(0),
+ top_send_done_watcher_(NULL) {
+ }
+
+ ~ReceivedSyncMsgQueue() {}
+
+ // 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_;
+ scoped_refptr<base::MessageLoopProxy> listener_message_loop_;
+ base::Lock message_lock_;
+ bool task_pending_;
+ int listener_count_;
+
+ // The current send done event watcher for this thread. Used to maintain
+ // a local global stack of send done watchers to ensure that nested sync
+ // message loops complete correctly.
+ base::WaitableEventWatcher* top_send_done_watcher_;
+};
+
+base::LazyInstance<base::ThreadLocalPointer<SyncChannel::ReceivedSyncMsgQueue> >
+ SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_(base::LINKER_INITIALIZED);
+
+SyncChannel::SyncContext::SyncContext(
+ Channel::Listener* listener,
+ base::MessageLoopProxy* ipc_thread,
+ WaitableEvent* shutdown_event)
+ : ChannelProxy::Context(listener, ipc_thread),
+ received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()),
+ shutdown_event_(shutdown_event),
+ restrict_dispatch_(false) {
+}
+
+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) {
+ // Create the tracking information for this message. This object is stored
+ // by value since all members are pointers that are cheap to copy. These
+ // pointers are cleaned up in the Pop() function.
+ //
+ // 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));
+ base::AutoLock auto_lock(deserializers_lock_);
+ deserializers_.push_back(pending);
+}
+
+bool SyncChannel::SyncContext::Pop() {
+ bool result;
+ {
+ base::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() {
+ base::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(this);
+}
+
+bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) {
+ base::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();
+}
+
+bool SyncChannel::SyncContext::OnMessageReceived(const Message& msg) {
+ // Give the filters a chance at processing this message.
+ if (TryFilters(msg))
+ return true;
+
+ if (TryToUnblockListener(&msg))
+ return true;
+
+ if (msg.should_unblock()) {
+ received_sync_msgs_->QueueMessage(msg, this);
+ return true;
+ }
+
+ if (msg.is_reply()) {
+ received_sync_msgs_->QueueReply(msg, this);
+ return true;
+ }
+
+ 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() {
+ CancelPendingSends();
+ shutdown_watcher_.StopWatching();
+ Context::OnChannelClosed();
+}
+
+void SyncChannel::SyncContext::OnSendTimeout(int message_id) {
+ base::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() {
+ base::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) {
+ if (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();
+ } else {
+ // We got the reply, timed out or the process shutdown.
+ DCHECK_EQ(GetSendDoneEvent(), event);
+ MessageLoop::current()->QuitNow();
+ }
+}
+
+
+SyncChannel::SyncChannel(
+ const IPC::ChannelHandle& channel_handle,
+ Channel::Mode mode,
+ Channel::Listener* listener,
+ base::MessageLoopProxy* ipc_message_loop,
+ bool create_pipe_now,
+ WaitableEvent* shutdown_event)
+ : ChannelProxy(
+ channel_handle, mode, ipc_message_loop,
+ new SyncContext(listener, 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() {
+}
+
+void SyncChannel::SetRestrictDispatchToSameChannel(bool value) {
+ sync_context()->set_restrict_dispatch(value);
+}
+
+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.
+ // *this* might get deleted, so only call static functions at this point.
+ WaitForReply(context, pump_messages_event);
+
+ return context->Pop();
+}
+
+void SyncChannel::WaitForReply(
+ SyncContext* context, WaitableEvent* pump_messages_event) {
+ context->DispatchMessages();
+ while (true) {
+ WaitableEvent* objects[] = {
+ context->GetDispatchEvent(),
+ context->GetSendDoneEvent(),
+ pump_messages_event
+ };
+
+ unsigned count = pump_messages_event ? 3: 2;
+ size_t 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.
+ context->GetDispatchEvent()->Reset();
+ context->DispatchMessages();
+ continue;
+ }
+
+ if (result == 2 /* pump_messages_event */)
+ WaitForReplyWithNestedMessageLoop(context); // Run a nested message loop.
+
+ break;
+ }
+}
+
+void SyncChannel::WaitForReplyWithNestedMessageLoop(SyncContext* context) {
+ base::WaitableEventWatcher send_done_watcher;
+
+ ReceivedSyncMsgQueue* sync_msg_queue = context->received_sync_msgs();
+ DCHECK(sync_msg_queue != NULL);
+
+ base::WaitableEventWatcher* old_send_done_event_watcher =
+ sync_msg_queue->top_send_done_watcher();
+
+ base::WaitableEventWatcher::Delegate* old_delegate = NULL;
+ base::WaitableEvent* old_event = NULL;
+
+ // Maintain a local global stack of send done delegates to ensure that
+ // nested sync calls complete in the correct sequence, i.e. the
+ // outermost call completes first, etc.
+ if (old_send_done_event_watcher) {
+ old_delegate = old_send_done_event_watcher->delegate();
+ old_event = old_send_done_event_watcher->GetWatchedEvent();
+ old_send_done_event_watcher->StopWatching();
+ }
+
+ sync_msg_queue->set_top_send_done_watcher(&send_done_watcher);
+
+ send_done_watcher.StartWatching(context->GetSendDoneEvent(), context);
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+
+ sync_msg_queue->set_top_send_done_watcher(old_send_done_event_watcher);
+ if (old_send_done_event_watcher && old_event) {
+ old_send_done_event_watcher->StartWatching(old_event, old_delegate);
+ }
+}
+
+void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) {
+ DCHECK(event == sync_context()->GetDispatchEvent());
+ // The call to DispatchMessages might delete this object, so reregister
+ // the object watcher first.
+ event->Reset();
+ dispatch_watcher_.StartWatching(event, this);
+ sync_context()->DispatchMessages();
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_sync_channel.h b/ipc/ipc_sync_channel.h
new file mode 100644
index 000000000000..29844da5629a
--- /dev/null
+++ b/ipc/ipc_sync_channel.h
@@ -0,0 +1,200 @@
+// Copyright (c) 2011 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__
+#pragma once
+
+#include <string>
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event_watcher.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_sync_message.h"
+
+namespace base {
+class WaitableEvent;
+};
+
+namespace IPC {
+
+class SyncMessage;
+class MessageReplyDeserializer;
+
+// This is similar to ChannelProxy, with the added feature of supporting sending
+// synchronous messages.
+//
+// Overview of how the sync channel works
+// --------------------------------------
+// When the sending thread sends a synchronous message, we create a bunch
+// of tracking info (created in SendWithTimeout, stored in the PendingSyncMsg
+// structure) associated with the message that we identify by the unique
+// "MessageId" on the SyncMessage. Among the things we save is the
+// "Deserializer" which is provided by the sync message. This object is in
+// charge of reading the parameters from the reply message and putting them in
+// the output variables provided by its caller.
+//
+// The info gets stashed in a queue since we could have a nested stack of sync
+// messages (each side could send sync messages in response to sync messages,
+// so it works like calling a function). The message is sent to the I/O thread
+// for dispatch and the original thread blocks waiting for the reply.
+//
+// SyncContext maintains the queue in a threadsafe way and listens for replies
+// on the I/O thread. When a reply comes in that matches one of the messages
+// it's looking for (using the unique message ID), it will execute the
+// deserializer stashed from before, and unblock the original thread.
+//
+//
+// Significant complexity results from the fact that messages are still coming
+// in while the original thread is blocked. Normal async messages are queued
+// and dispatched after the blocking call is complete. Sync messages must
+// be dispatched in a reentrant manner to avoid deadlock.
+//
+//
+// 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 IPC_EXPORT SyncChannel : public ChannelProxy,
+ public base::WaitableEventWatcher::Delegate {
+ public:
+ SyncChannel(const IPC::ChannelHandle& channel_handle,
+ Channel::Mode mode,
+ Channel::Listener* listener,
+ base::MessageLoopProxy* ipc_message_loop,
+ bool create_pipe_now,
+ base::WaitableEvent* shutdown_event);
+ virtual ~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;
+ }
+
+ // Sets this channel to only dispatch its incoming unblocking messages when it
+ // is itself blocked on sending a sync message, not when other channels are.
+ //
+ // Normally, any unblocking message coming from any channel can be dispatched
+ // when any (possibly other) channel is blocked on sending a message. This is
+ // needed in some cases to unblock certain loops (e.g. necessary when some
+ // processes share a window hierarchy), but may cause re-entrancy issues in
+ // some cases where such loops are not possible. This flags allows the tagging
+ // of some particular channels to not re-enter in such cases.
+ void SetRestrictDispatchToSameChannel(bool 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,
+ base::MessageLoopProxy* ipc_thread,
+ base::WaitableEvent* shutdown_event);
+
+ // Adds information about an outgoing sync message to the context so that
+ // we know how to deserialize the reply.
+ void Push(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_; }
+
+ ReceivedSyncMsgQueue* received_sync_msgs() {
+ return received_sync_msgs_;
+ }
+
+ void set_restrict_dispatch(bool value) { restrict_dispatch_ = value; }
+ bool restrict_dispatch() const { return restrict_dispatch_; }
+
+ private:
+ virtual ~SyncContext();
+ // ChannelProxy methods that we override.
+
+ // Called on the listener thread.
+ virtual void Clear();
+
+ // Called on the IPC thread.
+ virtual bool 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);
+
+ typedef std::deque<PendingSyncMsg> PendingSyncMessageQueue;
+ PendingSyncMessageQueue deserializers_;
+ base::Lock deserializers_lock_;
+
+ scoped_refptr<ReceivedSyncMsgQueue> received_sync_msgs_;
+
+ base::WaitableEvent* shutdown_event_;
+ base::WaitableEventWatcher shutdown_watcher_;
+ bool restrict_dispatch_;
+ };
+
+ 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.
+ static void WaitForReply(
+ SyncContext* context, base::WaitableEvent* pump_messages_event);
+
+ // Runs a nested message loop until a reply arrives, times out, or the process
+ // shuts down.
+ static void WaitForReplyWithNestedMessageLoop(SyncContext* context);
+
+ bool sync_messages_with_no_timeout_allowed_;
+
+ // Used to signal events between the IPC and listener threads.
+ base::WaitableEventWatcher dispatch_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncChannel);
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_SYNC_SENDER_H__
diff --git a/ipc/ipc_sync_message.cc b/ipc/ipc_sync_message.cc
new file mode 100644
index 000000000000..3a36bf327160
--- /dev/null
+++ b/ipc/ipc_sync_message.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#include <stack>
+
+#include "base/atomic_sequence_num.h"
+//#include "base/logging.h"
+#include "log.h"
+#include "base/synchronization/waitable_event.h"
+#include "ipc/ipc_sync_message.h"
+
+namespace IPC {
+
+#define kSyncMessageHeaderSize 4
+
+static base::AtomicSequenceNumber g_next_id(base::LINKER_INITIALIZED);
+
+ static base::WaitableEvent* dummy_event = NULL; //new base::WaitableEvent(true, true);
+
+SyncMessage::SyncMessage(
+ int32 routing_id,
+ uint32 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 = g_next_id.GetNext();
+ 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()) << "is_sync(" << msg->is_sync() <<")"<<std::endl;
+
+ 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 000000000000..2c6ed8b7c483
--- /dev/null
+++ b/ipc/ipc_sync_message.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2011 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_
+#pragma once
+
+#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 IPC_EXPORT SyncMessage : public Message {
+ public:
+ SyncMessage(int32 routing_id, uint32 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_;
+};
+
+// Used to deserialize parameters from a reply to a synchronous message
+class IPC_EXPORT MessageReplyDeserializer {
+ public:
+ virtual ~MessageReplyDeserializer() {}
+ 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;
+};
+
+// When sending a synchronous message, this structure contains an object
+// that knows how to deserialize the response.
+struct PendingSyncMsg {
+ PendingSyncMsg(int id,
+ MessageReplyDeserializer* d,
+ base::WaitableEvent* e)
+ : id(id), deserializer(d), done_event(e), send_result(false) { }
+ int id;
+ MessageReplyDeserializer* deserializer;
+ base::WaitableEvent* done_event;
+ bool send_result;
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_SYNC_MESSAGE_H_
diff --git a/ipc/ipc_sync_message_filter.cc b/ipc/ipc_sync_message_filter.cc
new file mode 100644
index 000000000000..cffbaa674aed
--- /dev/null
+++ b/ipc/ipc_sync_message_filter.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipc/ipc_sync_message_filter.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "ipc/ipc_sync_message.h"
+
+namespace IPC {
+
+SyncMessageFilter::SyncMessageFilter(base::WaitableEvent* shutdown_event)
+ : channel_(NULL),
+ listener_loop_(MessageLoop::current()),
+ io_loop_(NULL),
+ shutdown_event_(shutdown_event) {
+}
+
+SyncMessageFilter::~SyncMessageFilter() {
+}
+
+bool SyncMessageFilter::Send(Message* message) {
+ {
+ base::AutoLock auto_lock(lock_);
+ if (!io_loop_) {
+ delete message;
+ return false;
+ }
+ }
+
+ if (!message->is_sync()) {
+ io_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &SyncMessageFilter::SendOnIOThread, message));
+ return true;
+ }
+
+ base::WaitableEvent done_event(true, false);
+ PendingSyncMsg pending_message(
+ SyncMessage::GetMessageId(*message),
+ reinterpret_cast<SyncMessage*>(message)->GetReplyDeserializer(),
+ &done_event);
+
+ {
+ base::AutoLock auto_lock(lock_);
+ // Can't use this class on the main thread or else it can lead to deadlocks.
+ // Also by definition, can't use this on IO thread since we're blocking it.
+ DCHECK(MessageLoop::current() != listener_loop_);
+ DCHECK(MessageLoop::current() != io_loop_);
+ pending_sync_messages_.insert(&pending_message);
+ }
+
+ io_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &SyncMessageFilter::SendOnIOThread, message));
+
+ base::WaitableEvent* events[2] = { shutdown_event_, &done_event };
+ base::WaitableEvent::WaitMany(events, 2);
+
+ {
+ base::AutoLock auto_lock(lock_);
+ delete pending_message.deserializer;
+ pending_sync_messages_.erase(&pending_message);
+ }
+
+ return pending_message.send_result;
+}
+
+void SyncMessageFilter::SendOnIOThread(Message* message) {
+ if (channel_) {
+ channel_->Send(message);
+ return;
+ }
+
+ if (message->is_sync()) {
+ // We don't know which thread sent it, but it doesn't matter, just signal
+ // them all.
+ SignalAllEvents();
+ }
+
+ delete message;
+}
+
+void SyncMessageFilter::SignalAllEvents() {
+ base::AutoLock auto_lock(lock_);
+ for (PendingSyncMessages::iterator iter = pending_sync_messages_.begin();
+ iter != pending_sync_messages_.end(); ++iter) {
+ (*iter)->done_event->Signal();
+ }
+}
+
+void SyncMessageFilter::OnFilterAdded(Channel* channel) {
+ channel_ = channel;
+ base::AutoLock auto_lock(lock_);
+ io_loop_ = MessageLoop::current();
+}
+
+void SyncMessageFilter::OnChannelError() {
+ channel_ = NULL;
+ SignalAllEvents();
+}
+
+void SyncMessageFilter::OnChannelClosing() {
+ channel_ = NULL;
+ SignalAllEvents();
+}
+
+bool SyncMessageFilter::OnMessageReceived(const Message& message) {
+ base::AutoLock auto_lock(lock_);
+ for (PendingSyncMessages::iterator iter = pending_sync_messages_.begin();
+ iter != pending_sync_messages_.end(); ++iter) {
+ if (SyncMessage::IsMessageReplyTo(message, (*iter)->id)) {
+ if (!message.is_reply_error()) {
+ (*iter)->send_result =
+ (*iter)->deserializer->SerializeOutputParameters(message);
+ }
+ (*iter)->done_event->Signal();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_sync_message_filter.h b/ipc/ipc_sync_message_filter.h
new file mode 100644
index 000000000000..0dc471adacd3
--- /dev/null
+++ b/ipc/ipc_sync_message_filter.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 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_FILTER_H_
+#define IPC_IPC_SYNC_MESSAGE_FILTER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_sync_message.h"
+#include <set>
+
+namespace base {
+class WaitableEvent;
+}
+
+class MessageLoop;
+
+namespace IPC {
+
+class MessageReplyDeserializer;
+
+// This MessageFilter allows sending synchronous IPC messages from a thread
+// other than the listener thread associated with the SyncChannel. It does not
+// support fancy features that SyncChannel does, such as handling recursion or
+// receiving messages while waiting for a response. Note that this object can
+// be used to send simultaneous synchronous messages from different threads.
+class IPC_EXPORT SyncMessageFilter : public ChannelProxy::MessageFilter,
+ public Message::Sender {
+ public:
+ explicit SyncMessageFilter(base::WaitableEvent* shutdown_event);
+ virtual ~SyncMessageFilter();
+
+ // Message::Sender implementation.
+ virtual bool Send(Message* message);
+
+ // ChannelProxy::MessageFilter implementation.
+ virtual void OnFilterAdded(Channel* channel);
+ virtual void OnChannelError();
+ virtual void OnChannelClosing();
+ virtual bool OnMessageReceived(const Message& message);
+
+ private:
+ void SendOnIOThread(Message* message);
+ // Signal all the pending sends as done, used in an error condition.
+ void SignalAllEvents();
+
+ // The channel to which this filter was added.
+ Channel* channel_;
+
+ MessageLoop* listener_loop_; // The process's main thread.
+ MessageLoop* io_loop_; // The message loop where the Channel lives.
+
+ typedef std::set<PendingSyncMsg*> PendingSyncMessages;
+ PendingSyncMessages pending_sync_messages_;
+
+ // Locks data members above.
+ base::Lock lock_;
+
+ base::WaitableEvent* shutdown_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncMessageFilter);
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_SYNC_MESSAGE_FILTER_H_
diff --git a/ipc/param_traits_log_macros.h b/ipc/param_traits_log_macros.h
new file mode 100644
index 000000000000..8259a0d0a0bd
--- /dev/null
+++ b/ipc/param_traits_log_macros.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 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_PARAM_TRAITS_LOG_MACROS_H_
+#define IPC_PARAM_TRAITS_LOG_MACROS_H_
+#pragma once
+
+#include <string>
+
+// Null out all the macros that need nulling.
+#include "ipc/ipc_message_null_macros.h"
+
+// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur.
+#undef IPC_STRUCT_BEGIN
+#undef IPC_STRUCT_MEMBER
+#undef IPC_STRUCT_END
+#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name)
+#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name)
+#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END()
+
+// Set up so next include will generate log methods.
+#undef IPC_STRUCT_TRAITS_BEGIN
+#undef IPC_STRUCT_TRAITS_MEMBER
+#undef IPC_STRUCT_TRAITS_PARENT
+#undef IPC_STRUCT_TRAITS_END
+#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \
+ void ParamTraits<struct_name>::Log(const param_type& p, std::string* l) { \
+ bool needs_comma = false; \
+ l->append("(");
+#define IPC_STRUCT_TRAITS_MEMBER(name) \
+ if (needs_comma) \
+ l->append(", "); \
+ LogParam(p.name, l); \
+ needs_comma = true;
+#define IPC_STRUCT_TRAITS_PARENT(type) \
+ if (needs_comma) \
+ l->append(", "); \
+ ParamTraits<type>::Log(p, l); \
+ needs_comma = true;
+#define IPC_STRUCT_TRAITS_END() \
+ l->append(")"); \
+ }
+
+#undef IPC_ENUM_TRAITS
+#define IPC_ENUM_TRAITS(enum_name) \
+ void ParamTraits<enum_name>::Log(const param_type& p, std::string* l) { \
+ LogParam(static_cast<int>(p), l); \
+ }
+
+#endif // IPC_PARAM_TRAITS_LOG_MACROS_H_
+
diff --git a/ipc/param_traits_macros.h b/ipc/param_traits_macros.h
new file mode 100644
index 000000000000..df57adfb32e0
--- /dev/null
+++ b/ipc/param_traits_macros.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 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_PARAM_TRAITS_MACROS_H_
+#define IPC_PARAM_TRAITS_MACROS_H_
+
+#include <string>
+
+// Traits generation for structs.
+#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \
+ namespace IPC { \
+ template <> \
+ struct ParamTraits<struct_name> { \
+ typedef struct_name param_type; \
+ static void Write(Message* m, const param_type& p); \
+ static bool Read(const Message* m, void** iter, param_type* p); \
+ static void Log(const param_type& p, std::string* l); \
+ }; \
+ }
+
+#define IPC_STRUCT_TRAITS_MEMBER(name)
+#define IPC_STRUCT_TRAITS_PARENT(type)
+#define IPC_STRUCT_TRAITS_END()
+
+// Traits generation for enums.
+#define IPC_ENUM_TRAITS(enum_name) \
+ namespace IPC { \
+ template <> \
+ struct ParamTraits<enum_name> { \
+ typedef enum_name param_type; \
+ static void Write(Message* m, const param_type& p); \
+ static bool Read(const Message* m, void** iter, param_type* p); \
+ static void Log(const param_type& p, std::string* l); \
+ }; \
+ }
+
+#endif // IPC_PARAM_TRAITS_MACROS_H_
+
diff --git a/ipc/param_traits_read_macros.h b/ipc/param_traits_read_macros.h
new file mode 100644
index 000000000000..f0b88bc7ebe3
--- /dev/null
+++ b/ipc/param_traits_read_macros.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 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_PARAM_TRAITS_READ_MACROS_H_
+#define IPC_PARAM_TRAITS_READ_MACROS_H_
+#pragma once
+
+// Null out all the macros that need nulling.
+#include "ipc/ipc_message_null_macros.h"
+
+// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur.
+#undef IPC_STRUCT_BEGIN
+#undef IPC_STRUCT_MEMBER
+#undef IPC_STRUCT_END
+#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name)
+#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name)
+#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END()
+
+// Set up so next include will generate read methods.
+#undef IPC_STRUCT_TRAITS_BEGIN
+#undef IPC_STRUCT_TRAITS_MEMBER
+#undef IPC_STRUCT_TRAITS_PARENT
+#undef IPC_STRUCT_TRAITS_END
+#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \
+ bool ParamTraits<struct_name>:: \
+ Read(const Message* m, void** iter, param_type* p) { \
+ return
+#define IPC_STRUCT_TRAITS_MEMBER(name) ReadParam(m, iter, &p->name) &&
+#define IPC_STRUCT_TRAITS_PARENT(type) ParamTraits<type>::Read(m, iter, p) &&
+#define IPC_STRUCT_TRAITS_END() 1; }
+
+#undef IPC_ENUM_TRAITS
+#define IPC_ENUM_TRAITS(enum_name) \
+ bool ParamTraits<enum_name>:: \
+ Read(const Message* m, void** iter, param_type* p) { \
+ int type; \
+ if (!m->ReadInt(iter, &type)) \
+ return false; \
+ *p = static_cast<param_type>(type); \
+ return true; \
+ }
+
+#endif // IPC_PARAM_TRAITS_READ_MACROS_H_
+
diff --git a/ipc/param_traits_write_macros.h b/ipc/param_traits_write_macros.h
new file mode 100644
index 000000000000..844c3855eb72
--- /dev/null
+++ b/ipc/param_traits_write_macros.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 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_PARAM_TRAITS_WRITE_MACROS_H_
+#define IPC_PARAM_TRAITS_WRITE_MACROS_H_
+#pragma once
+
+// Null out all the macros that need nulling.
+#include "ipc/ipc_message_null_macros.h"
+
+// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur.
+#undef IPC_STRUCT_BEGIN
+#undef IPC_STRUCT_MEMBER
+#undef IPC_STRUCT_END
+#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name)
+#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name)
+#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END()
+
+// Set up so next include will generate write methods.
+#undef IPC_STRUCT_TRAITS_BEGIN
+#undef IPC_STRUCT_TRAITS_MEMBER
+#undef IPC_STRUCT_TRAITS_PARENT
+#undef IPC_STRUCT_TRAITS_END
+#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \
+ void ParamTraits<struct_name>::Write(Message* m, const param_type& p) {
+#define IPC_STRUCT_TRAITS_MEMBER(name) WriteParam(m, p.name);
+#define IPC_STRUCT_TRAITS_PARENT(type) ParamTraits<type>::Write(m, p);
+#define IPC_STRUCT_TRAITS_END() }
+
+#undef IPC_ENUM_TRAITS
+#define IPC_ENUM_TRAITS(enum_name) \
+ void ParamTraits<enum_name>::Write(Message* m, const param_type& p) { \
+ m->WriteInt(static_cast<int>(p)); \
+ }
+
+#endif // IPC_PARAM_TRAITS_WRITE_MACROS_H_
+
diff --git a/ipc/struct_constructor_macros.h b/ipc/struct_constructor_macros.h
new file mode 100644
index 000000000000..67bfcfb6e2ae
--- /dev/null
+++ b/ipc/struct_constructor_macros.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPC_STRUCT_CONSTRUCTOR_MACROS_H_
+#define IPC_STRUCT_CONSTRUCTOR_MACROS_H_
+
+// Null out all the macros that need nulling.
+#include "ipc/ipc_message_null_macros.h"
+
+// Set up so next include will generate constructors.
+#undef IPC_STRUCT_BEGIN
+#undef IPC_STRUCT_MEMBER
+#undef IPC_STRUCT_END
+#define IPC_STRUCT_BEGIN(struct_name) struct_name::struct_name() : NoParams()
+#define IPC_STRUCT_MEMBER(type, name) , name()
+#define IPC_STRUCT_END() {}
+
+#endif // IPC_STRUCT_CONSTRUCTOR_MACROS_H_
+
diff --git a/ipc/struct_destructor_macros.h b/ipc/struct_destructor_macros.h
new file mode 100644
index 000000000000..bf3dc953bcb6
--- /dev/null
+++ b/ipc/struct_destructor_macros.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2011 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_STRUCT_DESTRUCTOR_MACROS_H_
+#define IPC_STRUCT_DESTRUCTOR_MACROS_H_
+
+// Null out all the macros that need nulling.
+#include "ipc/ipc_message_null_macros.h"
+
+// Set up so next include will generate destructors.
+#undef IPC_STRUCT_BEGIN
+#define IPC_STRUCT_BEGIN(struct_name) struct_name::~struct_name() {}
+
+#endif // IPC_STRUCT_DESTRUCTOR_MACROS_H_
+
diff --git a/ipc/sync_socket_unittest.cc b/ipc/sync_socket_unittest.cc
new file mode 100644
index 000000000000..1a4ae13d89cd
--- /dev/null
+++ b/ipc/sync_socket_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sync_socket.h"
+
+#include <stdio.h>
+#include <string>
+#include <sstream>
+
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "build/build_config.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_message_utils.h"
+#include "ipc/ipc_message_utils_impl.h"
+#include "ipc/ipc_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
+enum IPCMessageIds {
+ UNUSED_IPC_TYPE,
+ SERVER_FIRST_IPC_TYPE, // SetHandle message sent to server.
+ SERVER_SECOND_IPC_TYPE, // Shutdown message sent to server.
+ CLIENT_FIRST_IPC_TYPE // Response message sent to client.
+};
+
+namespace {
+const char kHelloString[] = "Hello, SyncSocket Client";
+const size_t kHelloStringLength = arraysize(kHelloString);
+} // namespace
+
+// Message class to pass a base::SyncSocket::Handle to another process.
+// This is not as easy as it sounds, because of the differences in transferring
+// Windows HANDLEs versus posix file descriptors.
+#if defined(OS_WIN)
+class MsgClassSetHandle
+ : public IPC::MessageWithTuple< Tuple1<base::SyncSocket::Handle> > {
+ public:
+ enum { ID = SERVER_FIRST_IPC_TYPE };
+ explicit MsgClassSetHandle(const base::SyncSocket::Handle arg1)
+ : IPC::MessageWithTuple< Tuple1<base::SyncSocket::Handle> >(
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MsgClassSetHandle);
+};
+#elif defined(OS_POSIX)
+class MsgClassSetHandle
+ : public IPC::MessageWithTuple< Tuple1<base::FileDescriptor> > {
+ public:
+ enum { ID = SERVER_FIRST_IPC_TYPE };
+ explicit MsgClassSetHandle(const base::FileDescriptor& arg1)
+ : IPC::MessageWithTuple< Tuple1<base::FileDescriptor> >(
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MsgClassSetHandle);
+};
+#else
+# error "What platform?"
+#endif // defined(OS_WIN)
+
+// Message class to pass a response to the server.
+class MsgClassResponse
+ : public IPC::MessageWithTuple< Tuple1<std::string> > {
+ public:
+ enum { ID = CLIENT_FIRST_IPC_TYPE };
+ explicit MsgClassResponse(const std::string& arg1)
+ : IPC::MessageWithTuple< Tuple1<std::string> >(
+ MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MsgClassResponse);
+};
+
+// Message class to tell the server to shut down.
+class MsgClassShutdown
+ : public IPC::MessageWithTuple< Tuple0 > {
+ public:
+ enum { ID = SERVER_SECOND_IPC_TYPE };
+ MsgClassShutdown()
+ : IPC::MessageWithTuple< Tuple0 >(
+ MSG_ROUTING_CONTROL, ID, MakeTuple()) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MsgClassShutdown);
+};
+
+// The SyncSocket server listener class processes two sorts of
+// messages from the client.
+class SyncSocketServerListener : public IPC::Channel::Listener {
+ public:
+ SyncSocketServerListener() : chan_(NULL) {
+ }
+
+ void Init(IPC::Channel* chan) {
+ chan_ = chan;
+ }
+
+ virtual bool OnMessageReceived(const IPC::Message& msg) {
+ if (msg.routing_id() == MSG_ROUTING_CONTROL) {
+ IPC_BEGIN_MESSAGE_MAP(SyncSocketServerListener, msg)
+ IPC_MESSAGE_HANDLER(MsgClassSetHandle, OnMsgClassSetHandle)
+ IPC_MESSAGE_HANDLER(MsgClassShutdown, OnMsgClassShutdown)
+ IPC_END_MESSAGE_MAP()
+ }
+ return true;
+ }
+
+ private:
+ // This sort of message is sent first, causing the transfer of
+ // the handle for the SyncSocket. This message sends a buffer
+ // on the SyncSocket and then sends a response to the client.
+#if defined(OS_WIN)
+ void OnMsgClassSetHandle(const base::SyncSocket::Handle handle) {
+ SetHandle(handle);
+ }
+#elif defined(OS_POSIX)
+ void OnMsgClassSetHandle(const base::FileDescriptor& fd_struct) {
+ SetHandle(fd_struct.fd);
+ }
+#else
+# error "What platform?"
+#endif // defined(OS_WIN)
+
+ void SetHandle(base::SyncSocket::Handle handle) {
+ base::SyncSocket sync_socket(handle);
+ EXPECT_EQ(sync_socket.Send(static_cast<const void*>(kHelloString),
+ kHelloStringLength), kHelloStringLength);
+ IPC::Message* msg = new MsgClassResponse(kHelloString);
+ EXPECT_TRUE(chan_->Send(msg));
+ }
+
+ // When the client responds, it sends back a shutdown message,
+ // which causes the message loop to exit.
+ void OnMsgClassShutdown() {
+ MessageLoop::current()->Quit();
+ }
+
+ IPC::Channel* chan_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSocketServerListener);
+};
+
+// Runs the fuzzing server child mode. Returns when the preset number
+// of messages have been received.
+MULTIPROCESS_TEST_MAIN(RunSyncSocketServer) {
+ MessageLoopForIO main_message_loop;
+ SyncSocketServerListener listener;
+ IPC::Channel chan(kSyncSocketChannel, IPC::Channel::MODE_CLIENT, &listener);
+ EXPECT_TRUE(chan.Connect());
+ listener.Init(&chan);
+ MessageLoop::current()->Run();
+ return 0;
+}
+
+// The SyncSocket client listener only processes one sort of message,
+// a response from the server.
+class SyncSocketClientListener : public IPC::Channel::Listener {
+ public:
+ SyncSocketClientListener() {
+ }
+
+ void Init(base::SyncSocket* socket, IPC::Channel* chan) {
+ socket_ = socket;
+ chan_ = chan;
+ }
+
+ virtual bool OnMessageReceived(const IPC::Message& msg) {
+ if (msg.routing_id() == MSG_ROUTING_CONTROL) {
+ IPC_BEGIN_MESSAGE_MAP(SyncSocketClientListener, msg)
+ IPC_MESSAGE_HANDLER(MsgClassResponse, OnMsgClassResponse)
+ IPC_END_MESSAGE_MAP()
+ }
+ return true;
+ }
+
+ private:
+ // When a response is received from the server, it sends the same
+ // string as was written on the SyncSocket. These are compared
+ // and a shutdown message is sent back to the server.
+ void OnMsgClassResponse(const std::string& str) {
+ // We rely on the order of sync_socket.Send() and chan_->Send() in
+ // the SyncSocketServerListener object.
+ EXPECT_EQ(kHelloStringLength, socket_->Peek());
+ char buf[kHelloStringLength];
+ socket_->Receive(static_cast<void*>(buf), kHelloStringLength);
+ EXPECT_EQ(strcmp(str.c_str(), buf), 0);
+ // After receiving from the socket there should be no bytes left.
+ EXPECT_EQ(0U, socket_->Peek());
+ IPC::Message* msg = new MsgClassShutdown();
+ EXPECT_TRUE(chan_->Send(msg));
+ MessageLoop::current()->Quit();
+ }
+
+ base::SyncSocket* socket_;
+ IPC::Channel* chan_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSocketClientListener);
+};
+
+class SyncSocketTest : public IPCChannelTest {
+};
+
+TEST_F(SyncSocketTest, SanityTest) {
+ SyncSocketClientListener listener;
+ IPC::Channel chan(kSyncSocketChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ base::ProcessHandle server_process = SpawnChild(SYNC_SOCKET_SERVER, &chan);
+ ASSERT_TRUE(server_process);
+ // Create a pair of SyncSockets.
+ base::SyncSocket* pair[2];
+ base::SyncSocket::CreatePair(pair);
+ // Immediately after creation there should be no pending bytes.
+ EXPECT_EQ(0U, pair[0]->Peek());
+ EXPECT_EQ(0U, pair[1]->Peek());
+ base::SyncSocket::Handle target_handle;
+ // Connect the channel and listener.
+ ASSERT_TRUE(chan.Connect());
+ listener.Init(pair[0], &chan);
+#if defined(OS_WIN)
+ // On windows we need to duplicate the handle into the server process.
+ BOOL retval = DuplicateHandle(GetCurrentProcess(), pair[1]->handle(),
+ server_process, &target_handle,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+ EXPECT_TRUE(retval);
+ // Set up a message to pass the handle to the server.
+ IPC::Message* msg = new MsgClassSetHandle(target_handle);
+#else
+ target_handle = pair[1]->handle();
+ // Set up a message to pass the handle to the server.
+ base::FileDescriptor filedesc(target_handle, false);
+ IPC::Message* msg = new MsgClassSetHandle(filedesc);
+#endif // defined(OS_WIN)
+ EXPECT_TRUE(chan.Send(msg));
+ // Use the current thread as the I/O thread.
+ MessageLoop::current()->Run();
+ // Shut down.
+ delete pair[0];
+ delete pair[1];
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000));
+ base::CloseProcessHandle(server_process);
+}