summaryrefslogtreecommitdiffstats
path: root/chrome/common
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-03 02:29:45 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-03 02:29:45 +0000
commit82e5ee816a5f05d230c5356205e93d803650d0b9 (patch)
tree6b6e9d1bef407feef8ef24eedc9f343e2d536609 /chrome/common
parent8cf3f1dae79db8cfbf9dc6b75c140eabad918a6e (diff)
downloadchromium_src-82e5ee816a5f05d230c5356205e93d803650d0b9.zip
chromium_src-82e5ee816a5f05d230c5356205e93d803650d0b9.tar.gz
chromium_src-82e5ee816a5f05d230c5356205e93d803650d0b9.tar.bz2
Revert "Move IPC code to ipc/"
This reverts commit r13062 which, unsurprisingly, broke the build. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13063 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common')
-rw-r--r--chrome/common/child_process_host.cc3
-rw-r--r--chrome/common/child_process_host.h2
-rw-r--r--chrome/common/child_thread.cc3
-rw-r--r--chrome/common/child_thread.h2
-rw-r--r--chrome/common/chrome_counters.cc5
-rw-r--r--chrome/common/chrome_counters.h3
-rw-r--r--chrome/common/chrome_switches.cc13
-rw-r--r--chrome/common/chrome_switches.h6
-rw-r--r--chrome/common/common.vcproj64
-rw-r--r--chrome/common/common_message_utils.h138
-rw-r--r--chrome/common/file_descriptor_set_posix.cc118
-rw-r--r--chrome/common/file_descriptor_set_posix.h108
-rw-r--r--chrome/common/file_descriptor_set_unittest.cc157
-rw-r--r--chrome/common/ipc_channel.h123
-rw-r--r--chrome/common/ipc_channel_posix.cc803
-rw-r--r--chrome/common/ipc_channel_posix.h112
-rw-r--r--chrome/common/ipc_channel_proxy.cc305
-rw-r--r--chrome/common/ipc_channel_proxy.h206
-rw-r--r--chrome/common/ipc_channel_win.cc440
-rw-r--r--chrome/common/ipc_channel_win.h85
-rw-r--r--chrome/common/ipc_fuzzing_tests.cc430
-rw-r--r--chrome/common/ipc_logging.cc320
-rw-r--r--chrome/common/ipc_logging.h113
-rw-r--r--chrome/common/ipc_maybe.h19
-rw-r--r--chrome/common/ipc_message.cc124
-rw-r--r--chrome/common/ipc_message.h279
-rw-r--r--chrome/common/ipc_message_macros.h1152
-rw-r--r--chrome/common/ipc_message_unittest.cc (renamed from chrome/common/gurl_serialisation_unittest.cc)6
-rw-r--r--chrome/common/ipc_message_utils.cc (renamed from chrome/common/common_message_utils.cc)75
-rw-r--r--chrome/common/ipc_message_utils.h1381
-rw-r--r--chrome/common/ipc_send_fds_test.cc186
-rw-r--r--chrome/common/ipc_sync_channel.cc453
-rw-r--r--chrome/common/ipc_sync_channel.h159
-rw-r--r--chrome/common/ipc_sync_channel_unittest.cc1009
-rw-r--r--chrome/common/ipc_sync_message.cc126
-rw-r--r--chrome/common/ipc_sync_message.h96
-rw-r--r--chrome/common/ipc_sync_message_unittest.cc249
-rw-r--r--chrome/common/ipc_sync_message_unittest.h98
-rw-r--r--chrome/common/ipc_test_sink.cc51
-rw-r--r--chrome/common/ipc_test_sink.h84
-rw-r--r--chrome/common/ipc_tests.cc481
-rw-r--r--chrome/common/ipc_tests.h47
-rw-r--r--chrome/common/ipc_tests.vcproj165
-rw-r--r--chrome/common/logging_chrome.cc21
-rw-r--r--chrome/common/message_router.h2
-rw-r--r--chrome/common/plugin_messages.h11
-rw-r--r--chrome/common/plugin_messages_internal.h2
-rw-r--r--chrome/common/render_messages.h10
-rw-r--r--chrome/common/render_messages_internal.h2
-rw-r--r--chrome/common/resource_dispatcher.h2
-rw-r--r--chrome/common/webkit_param_traits.h2
-rw-r--r--chrome/common/worker_messages.h11
-rw-r--r--chrome/common/worker_messages_internal.h2
53 files changed, 9659 insertions, 205 deletions
diff --git a/chrome/common/child_process_host.cc b/chrome/common/child_process_host.cc
index dc7f1e5..7f934b0 100644
--- a/chrome/common/child_process_host.cc
+++ b/chrome/common/child_process_host.cc
@@ -11,13 +11,12 @@
#include "base/singleton.h"
#include "base/waitable_event.h"
#include "chrome/browser/chrome_thread.h"
-#include "chrome/common/common_message_utils.h"
+#include "chrome/common/ipc_logging.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/plugin_messages.h"
#include "chrome/common/process_watcher.h"
#include "chrome/common/result_codes.h"
-#include "ipc/ipc_logging.h"
namespace {
diff --git a/chrome/common/child_process_host.h b/chrome/common/child_process_host.h
index 1aef54c..d4d2dd9 100644
--- a/chrome/common/child_process_host.h
+++ b/chrome/common/child_process_host.h
@@ -14,7 +14,7 @@
#include "base/waitable_event_watcher.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/common/child_process_info.h"
-#include "ipc/ipc_channel.h"
+#include "chrome/common/ipc_channel.h"
class NotificationType;
diff --git a/chrome/common/child_thread.cc b/chrome/common/child_thread.cc
index 527c58f..94b3186 100644
--- a/chrome/common/child_thread.cc
+++ b/chrome/common/child_thread.cc
@@ -6,11 +6,10 @@
#include "base/string_util.h"
#include "base/command_line.h"
-#include "chrome/common/common_message_utils.h"
#include "chrome/common/child_process.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/ipc_logging.h"
#include "chrome/common/plugin_messages.h"
-#include "ipc/ipc_logging.h"
#include "webkit/glue/webkit_glue.h"
diff --git a/chrome/common/child_thread.h b/chrome/common/child_thread.h
index a4f8603..66fcc09 100644
--- a/chrome/common/child_thread.h
+++ b/chrome/common/child_thread.h
@@ -6,7 +6,7 @@
#define CHROME_COMMON_CHILD_THREAD_H_
#include "base/thread.h"
-#include "ipc/ipc_sync_channel.h"
+#include "chrome/common/ipc_sync_channel.h"
#include "chrome/common/message_router.h"
#include "chrome/common/resource_dispatcher.h"
diff --git a/chrome/common/chrome_counters.cc b/chrome/common/chrome_counters.cc
index fea0906..98a977b 100644
--- a/chrome/common/chrome_counters.cc
+++ b/chrome/common/chrome_counters.cc
@@ -19,6 +19,11 @@ namespace chrome {
// we'd leak the extraneous StatsCounter object once, and that
// would be it. But these are small objects, so this is ok.
+StatsCounter& Counters::ipc_send_counter() {
+ static StatsCounter* ctr = new StatsCounter("IPC.SendMsgCount");
+ return *ctr;
+}
+
StatsCounterTimer& Counters::chrome_main() {
static StatsCounterTimer* ctr = new StatsCounterTimer("Chrome.Init");
return *ctr;
diff --git a/chrome/common/chrome_counters.h b/chrome/common/chrome_counters.h
index 14cfa21..c7975f85 100644
--- a/chrome/common/chrome_counters.h
+++ b/chrome/common/chrome_counters.h
@@ -15,6 +15,9 @@ namespace chrome {
class Counters {
public:
+ // The number of messages sent on IPC channels.
+ static StatsCounter& ipc_send_counter();
+
// The amount of time spent in chrome initialization.
static StatsCounterTimer& chrome_main();
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 350b2a0..c32cbb5 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -50,6 +50,10 @@ const wchar_t kTestShellStartupDialog[] = L"testshell-startup-dialog";
// --plugin-launcher="path\to\purify /Run=yes"
const wchar_t kPluginLauncher[] = L"plugin-launcher";
+// The value of this switch tells the child process which
+// IPC channel the browser expects to use to communicate with it.
+const wchar_t kProcessChannelID[] = L"channel";
+
// The value of this switch tells the app to listen for and broadcast
// testing-related messages on IPC channel with the given ID.
const wchar_t kTestingChannelID[] = L"testing-channel";
@@ -152,6 +156,11 @@ const wchar_t kCountry[] = L"country";
// language[-country] where language is the 2 letter code from ISO-639.
const wchar_t kLang[] = L"lang";
+// Will add kDebugOnStart to every child processes. If a value is passed, it
+// will be used as a filter to determine if the child process should have the
+// kDebugOnStart flag passed on or not.
+const wchar_t kDebugChildren[] = L"debug-children";
+
// Will add kWaitForDebugger 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
// kWaitForDebugger flag passed on or not.
@@ -382,6 +391,10 @@ const wchar_t kRendererCmdPrefix[] = L"renderer-cmd-prefix";
// Temparary option for new ftp implemetation.
const wchar_t kNewFtp[] = L"new-ftp";
+// On POSIX only: use FIFO for IPC channels so that "unrelated" process
+// can connect to a channel, provided it knows its name. For debugging purposes.
+const wchar_t kIPCUseFIFO[] = L"ipc-use-fifo";
+
// If this flag is set open out of process developer tools window instead of
// Console Debugger when user clicks "Debug JavaScript".
const wchar_t kEnableOutOfProcessDevTools[] = L"enable-oop-devtools";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index f6d085b..8b51a7d 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -7,7 +7,7 @@
#ifndef CHROME_COMMON_CHROME_SWITCHES_H__
#define CHROME_COMMON_CHROME_SWITCHES_H__
-#include "ipc/ipc_switches.h"
+#include "base/base_switches.h"
namespace switches {
@@ -23,6 +23,7 @@ extern const wchar_t kPluginStartupDialog[];
extern const wchar_t kTestShellStartupDialog[];
extern const wchar_t kPluginLauncher[];
+extern const wchar_t kProcessChannelID[];
extern const wchar_t kTestingChannelID[];
extern const wchar_t kHomePage[];
extern const wchar_t kRendererProcess[];
@@ -50,6 +51,7 @@ extern const wchar_t kUserAgent[];
extern const wchar_t kJavaScriptFlags[];
extern const wchar_t kCountry[];
extern const wchar_t kLang[];
+extern const wchar_t kDebugChildren[];
extern const wchar_t kWaitForDebuggerChildren[];
extern const wchar_t kLogFilterPrefix[];
@@ -148,6 +150,8 @@ extern const wchar_t kRendererCmdPrefix[];
extern const wchar_t kNewFtp[];
+extern const wchar_t kIPCUseFIFO[];
+
extern const wchar_t kEnableOutOfProcessDevTools[];
extern const wchar_t kEnableWebWorkers[];
diff --git a/chrome/common/common.vcproj b/chrome/common/common.vcproj
index 5f3837a..f392f7d 100644
--- a/chrome/common/common.vcproj
+++ b/chrome/common/common.vcproj
@@ -225,6 +225,66 @@
Name="ipc"
>
<File
+ RelativePath=".\ipc_channel.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_channel_proxy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_channel_proxy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_channel_win.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_logging.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_logging.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_message.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_message.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_message_macros.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_message_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_message_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_sync_channel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_sync_channel.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_sync_message.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_sync_message.h"
+ >
+ </File>
+ <File
RelativePath=".\plugin_messages.h"
>
</File>
@@ -398,10 +458,6 @@
>
</File>
<File
- RelativePath=".\common_message_utils.cc"
- >
- </File>
- <File
RelativePath=".\debug_flags.cc"
>
</File>
diff --git a/chrome/common/common_message_utils.h b/chrome/common/common_message_utils.h
deleted file mode 100644
index 81178db..0000000
--- a/chrome/common/common_message_utils.h
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_COMMON_COMMON_MESSAGE_UTILS_H_
-#define CHROME_COMMON_COMMON_MESSAGE_UTILS_H_
-
-#include "chrome/common/thumbnail_score.h"
-#include "chrome/common/transport_dib.h"
-#include "ipc/ipc_message_utils.h"
-#include "webkit/glue/webcursor.h"
-#include "webkit/glue/window_open_disposition.h"
-
-// Forward declarations.
-class GURL;
-class SkBitmap;
-
-namespace webkit_glue {
-struct WebApplicationInfo;
-} // namespace webkit_glue
-
-namespace IPC {
-
-template <>
-struct ParamTraits<SkBitmap> {
- typedef SkBitmap param_type;
- static void Write(Message* m, const param_type& p);
-
- // Note: This function expects parameter |r| to be of type &SkBitmap since
- // r->SetConfig() and r->SetPixels() are called.
- static bool Read(const Message* m, void** iter, param_type* r);
-
- static void Log(const param_type& p, std::wstring* l);
-};
-
-template<>
-struct ParamTraits<ThumbnailScore> {
- typedef ThumbnailScore param_type;
- static void Write(Message* m, const param_type& p) {
- IPC::ParamTraits<double>::Write(m, p.boring_score);
- IPC::ParamTraits<bool>::Write(m, p.good_clipping);
- IPC::ParamTraits<bool>::Write(m, p.at_top);
- IPC::ParamTraits<base::Time>::Write(m, p.time_at_snapshot);
- }
- static bool Read(const Message* m, void** iter, param_type* r) {
- double boring_score;
- bool good_clipping, at_top;
- base::Time time_at_snapshot;
- if (!IPC::ParamTraits<double>::Read(m, iter, &boring_score) ||
- !IPC::ParamTraits<bool>::Read(m, iter, &good_clipping) ||
- !IPC::ParamTraits<bool>::Read(m, iter, &at_top) ||
- !IPC::ParamTraits<base::Time>::Read(m, iter, &time_at_snapshot))
- return false;
-
- r->boring_score = boring_score;
- r->good_clipping = good_clipping;
- r->at_top = at_top;
- r->time_at_snapshot = time_at_snapshot;
- return true;
- }
- static void Log(const param_type& p, std::wstring* l) {
- l->append(StringPrintf(L"(%f, %d, %d)",
- p.boring_score, p.good_clipping, p.at_top));
- }
-};
-
-template <>
-struct ParamTraits<GURL> {
- typedef GURL 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::wstring* l);
-};
-
-template <>
-struct ParamTraits<WindowOpenDisposition> {
- typedef WindowOpenDisposition 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) {
- int temp;
- bool res = m->ReadInt(iter, &temp);
- *r = static_cast<WindowOpenDisposition>(temp);
- return res;
- }
- static void Log(const param_type& p, std::wstring* l) {
- l->append(StringPrintf(L"%d", p));
- }
-};
-
-template <>
-struct ParamTraits<WebCursor> {
- typedef WebCursor param_type;
- static void Write(Message* m, const param_type& p) {
- p.Serialize(m);
- }
- static bool Read(const Message* m, void** iter, param_type* r) {
- return r->Deserialize(m, iter);
- }
- static void Log(const param_type& p, std::wstring* l) {
- l->append(L"<WebCursor>");
- }
-};
-
-template <>
-struct ParamTraits<webkit_glue::WebApplicationInfo> {
- typedef webkit_glue::WebApplicationInfo param_type;
- static void Write(Message* m, const param_type& p);
- static bool Read(const Message* m, void** iter, param_type* r);
- static void Log(const param_type& p, std::wstring* l);
-};
-
-#if defined(OS_WIN)
-template<>
-struct ParamTraits<TransportDIB::Id> {
- typedef TransportDIB::Id param_type;
- static void Write(Message* m, const param_type& p) {
- WriteParam(m, p.handle);
- WriteParam(m, p.sequence_num);
- }
- static bool Read(const Message* m, void** iter, param_type* r) {
- return (ReadParam(m, iter, &r->handle) &&
- ReadParam(m, iter, &r->sequence_num));
- }
- static void Log(const param_type& p, std::wstring* l) {
- l->append(L"TransportDIB(");
- LogParam(p.handle, l);
- l->append(L", ");
- LogParam(p.sequence_num, l);
- l->append(L")");
- }
-};
-#endif
-
-} // namespace IPC
-
-#endif // CHROME_COMMON_COMMON_MESSAGE_UTILS_H_
diff --git a/chrome/common/file_descriptor_set_posix.cc b/chrome/common/file_descriptor_set_posix.cc
new file mode 100644
index 0000000..b57c007
--- /dev/null
+++ b/chrome/common/file_descriptor_set_posix.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/file_descriptor_set_posix.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)
+ close(descriptors_[i].fd);
+ }
+}
+
+bool FileDescriptorSet::Add(int fd) {
+ if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE)
+ return false;
+
+ struct base::FileDescriptor sd;
+ sd.fd = fd;
+ sd.auto_close = false;
+ descriptors_.push_back(sd);
+ return true;
+}
+
+bool FileDescriptorSet::AddAndAutoClose(int fd) {
+ if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE)
+ return false;
+
+ struct base::FileDescriptor sd;
+ sd.fd = fd;
+ sd.auto_close = true;
+ descriptors_.push_back(sd);
+ DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE);
+ return true;
+}
+
+int FileDescriptorSet::GetDescriptorAt(unsigned index) const {
+ if (index >= descriptors_.size())
+ return -1;
+
+ // We should always walk the descriptors in order, so it's reasonable to
+ // enforce this. Consider the case where a compromised renderer sends us
+ // the following message:
+ //
+ // ExampleMsg:
+ // num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m}
+ //
+ // Here the renderer sent us a message which should have a descriptor, but
+ // actually sent two in an attempt to fill our fd table and kill us. By
+ // setting the index of the descriptor in the message to 1 (it should be
+ // 0), we would record a highwater of 1 and then consider all the
+ // descriptors to have been used.
+ //
+ // So we can either track of the use of each descriptor in a bitset, or we
+ // can enforce that we walk the indexes strictly in order.
+ //
+ // There's one more wrinkle: When logging messages, we may reparse them. So
+ // we have an exception: When the consumed_descriptor_highwater_ is at the
+ // end of the array and index 0 is requested, we reset the highwater value.
+ if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size())
+ consumed_descriptor_highwater_ = 0;
+
+ if (index != consumed_descriptor_highwater_)
+ return -1;
+
+ consumed_descriptor_highwater_ = index + 1;
+ return descriptors_[index].fd;
+}
+
+void FileDescriptorSet::GetDescriptors(int* buffer) const {
+ for (std::vector<base::FileDescriptor>::const_iterator
+ i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+ *(buffer++) = i->fd;
+ }
+}
+
+void FileDescriptorSet::CommitAll() {
+ for (std::vector<base::FileDescriptor>::iterator
+ i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+ if (i->auto_close)
+ close(i->fd);
+ }
+ descriptors_.clear();
+ consumed_descriptor_highwater_ = 0;
+}
+
+void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) {
+ DCHECK_LE(count, MAX_DESCRIPTORS_PER_MESSAGE);
+ DCHECK_EQ(descriptors_.size(), 0u);
+ DCHECK_EQ(consumed_descriptor_highwater_, 0u);
+
+ descriptors_.reserve(count);
+ for (unsigned i = 0; i < count; ++i) {
+ struct base::FileDescriptor sd;
+ sd.fd = buffer[i];
+ sd.auto_close = true;
+ descriptors_.push_back(sd);
+ }
+}
diff --git a/chrome/common/file_descriptor_set_posix.h b/chrome/common/file_descriptor_set_posix.h
new file mode 100644
index 0000000..342e6d9
--- /dev/null
+++ b/chrome/common/file_descriptor_set_posix.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
+#define CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_descriptor_posix.h"
+#include "base/ref_counted.h"
+
+// -----------------------------------------------------------------------------
+// A FileDescriptorSet is an ordered set of POSIX file descriptors. These are
+// associated with IPC messages so that descriptors can be transmitted over a
+// UNIX domain socket.
+// -----------------------------------------------------------------------------
+class FileDescriptorSet : public base::RefCountedThreadSafe<FileDescriptorSet> {
+ public:
+ FileDescriptorSet();
+ ~FileDescriptorSet();
+
+ // This is the maximum number of descriptors per message. We need to know this
+ // because the control message kernel interface has to be given a buffer which
+ // is large enough to store all the descriptor numbers. Otherwise the kernel
+ // tells us that it truncated the control data and the extra descriptors are
+ // lost.
+ //
+ // In debugging mode, it's a fatal error to try and add more than this number
+ // of descriptors to a FileDescriptorSet.
+ enum {
+ MAX_DESCRIPTORS_PER_MESSAGE = 4,
+ };
+
+ // ---------------------------------------------------------------------------
+ // Interfaces for building during message serialisation...
+
+ // Add a descriptor to the end of the set. Returns false iff the set is full.
+ bool Add(int fd);
+ // Add a descriptor to the end of the set and automatically close it after
+ // transmission. Returns false iff the set is full.
+ bool AddAndAutoClose(int fd);
+
+ // ---------------------------------------------------------------------------
+
+
+ // ---------------------------------------------------------------------------
+ // Interfaces for accessing during message deserialisation...
+
+ // Return the number of descriptors
+ unsigned size() const { return descriptors_.size(); }
+ // Return true if no unconsumed descriptors remain
+ bool empty() const { return descriptors_.empty(); }
+ // Fetch the nth descriptor from the beginning of the set. Code using this
+ // /must/ access the descriptors in order, except that it may wrap from the
+ // end to index 0 again.
+ //
+ // This interface is designed for the deserialising code as it doesn't
+ // support close flags.
+ // returns: file descriptor, or -1 on error
+ int GetDescriptorAt(unsigned n) const;
+
+ // ---------------------------------------------------------------------------
+
+
+ // ---------------------------------------------------------------------------
+ // Interfaces for transmission...
+
+ // Fill an array with file descriptors without 'consuming' them. CommitAll
+ // must be called after these descriptors have been transmitted.
+ // buffer: (output) a buffer of, at least, size() integers.
+ void GetDescriptors(int* buffer) const;
+ // This must be called after transmitting the descriptors returned by
+ // GetDescriptors. It marks all the descriptors as consumed and closes those
+ // which are auto-close.
+ void CommitAll();
+
+ // ---------------------------------------------------------------------------
+
+
+ // ---------------------------------------------------------------------------
+ // Interfaces for receiving...
+
+ // Set the contents of the set from the given buffer. This set must be empty
+ // before calling. The auto-close flag is set on all the descriptors so that
+ // unconsumed descriptors are closed on destruction.
+ void SetDescriptors(const int* buffer, unsigned count);
+
+ // ---------------------------------------------------------------------------
+
+ private:
+ // A vector of descriptors and close flags. If this message is sent, then
+ // these descriptors are sent as control data. After sending, any descriptors
+ // with a true flag are closed. If this message has been received, then these
+ // are the descriptors which were received and all close flags are true.
+ std::vector<base::FileDescriptor> descriptors_;
+
+ // This contains the index of the next descriptor which should be consumed.
+ // It's used in a couple of ways. Firstly, at destruction we can check that
+ // all the descriptors have been read (with GetNthDescriptor). Secondly, we
+ // can check that they are read in order.
+ mutable unsigned consumed_descriptor_highwater_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorSet);
+};
+
+#endif // CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
diff --git a/chrome/common/file_descriptor_set_unittest.cc b/chrome/common/file_descriptor_set_unittest.cc
new file mode 100644
index 0000000..89144a5
--- /dev/null
+++ b/chrome/common/file_descriptor_set_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This test is POSIX only.
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "base/basictypes.h"
+#include "chrome/common/file_descriptor_set_posix.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// 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());
+ ASSERT_TRUE(set->AddAndAutoClose(kFDBase));
+ ASSERT_EQ(set->size(), 1u);
+ ASSERT_TRUE(!set->empty());
+
+ set->CommitAll();
+}
+
+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());
+
+ static const int fds[] = {kFDBase};
+ set->SetDescriptors(fds, 1);
+ ASSERT_TRUE(!set->empty());
+ ASSERT_EQ(set->size(), 1u);
+
+ set->CommitAll();
+}
+
+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 = open("/dev/null", O_RDONLY);
+ ASSERT_TRUE(set->Add(fd));
+ set->CommitAll();
+
+ const int duped = dup(fd);
+ ASSERT_GE(duped, 0);
+ close(duped);
+ close(fd);
+}
+
+TEST(FileDescriptorSet, DoClose) {
+ scoped_refptr<FileDescriptorSet> set = new FileDescriptorSet;
+
+ const int fd = open("/dev/null", O_RDONLY);
+ ASSERT_TRUE(set->AddAndAutoClose(fd));
+ set->CommitAll();
+
+ const int duped = dup(fd);
+ ASSERT_EQ(duped, -1);
+ close(fd);
+}
diff --git a/chrome/common/ipc_channel.h b/chrome/common/ipc_channel.h
new file mode 100644
index 0000000..9b7f8b2
--- /dev/null
+++ b/chrome/common/ipc_channel.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_H_
+#define CHROME_COMMON_IPC_CHANNEL_H_
+
+#include "chrome/common/ipc_message.h"
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+class Channel : public Message::Sender {
+ // Security tests need access to the pipe handle.
+ friend class ChannelTest;
+
+ public:
+ // Implemented by consumers of a Channel to receive messages.
+ class Listener {
+ public:
+ virtual ~Listener() {}
+
+ // Called when a message is received.
+ virtual void OnMessageReceived(const Message& message) = 0;
+
+ // Called when the channel is connected and we have received the internal
+ // Hello message from the peer.
+ virtual void OnChannelConnected(int32 peer_pid) {}
+
+ // Called when an error is detected that causes the channel to close.
+ // This method is not called when a channel is closed normally.
+ virtual void OnChannelError() {}
+ };
+
+ enum Mode {
+ MODE_SERVER,
+ MODE_CLIENT
+ };
+
+ enum {
+ // The maximum message size in bytes. Attempting to receive a
+ // message of this size or bigger results in a channel error.
+ kMaximumMessageSize = 256 * 1024 * 1024,
+
+ // Ammount of data to read at once from the pipe.
+ kReadBufferSize = 4 * 1024
+ };
+
+ // Initialize a Channel.
+ //
+ // |channel_id| identifies the communication Channel.
+ // |mode| specifies whether this Channel is to operate in server mode or
+ // client mode. In server mode, the Channel is responsible for setting up the
+ // IPC object, whereas in client mode, the Channel merely connects to the
+ // already established IPC object.
+ // |listener| receives a callback on the current thread for each newly
+ // received message.
+ //
+ Channel(const std::wstring& channel_id, Mode mode, Listener* listener);
+
+ ~Channel();
+
+ // Connect the pipe. On the server side, this will initiate
+ // waiting for connections. On the client, it attempts to
+ // connect to a pre-existing pipe. Note, calling Connect()
+ // will not block the calling thread and may complete
+ // asynchronously.
+ bool Connect();
+
+ // Close this Channel explicitly. May be called multiple times.
+ void Close();
+
+ // Modify the Channel's listener.
+ void set_listener(Listener* listener);
+
+ // Send a message over the Channel to the listener on the other end.
+ //
+ // |message| must be allocated using operator new. This object will be
+ // deleted once the contents of the Message have been sent.
+ //
+ // FIXME bug 551500: the channel does not notice failures, so if the
+ // renderer crashes, it will silently succeed, leaking the parameter.
+ // At least the leak will be fixed by...
+ //
+ virtual bool Send(Message* message);
+
+#if defined(OS_POSIX)
+ // On POSIX an IPC::Channel wraps a socketpair(), this method returns the
+ // FD # for the client end of the socket and the equivalent FD# to use for
+ // mapping it into the Child process.
+ // This method may only be called on the server side of a channel.
+ //
+ // If the kTestingChannelID flag is specified on the command line then
+ // a named FIFO is used as the channel transport mechanism rather than a
+ // socketpair() in which case this method returns -1 for both parameters.
+ void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd);
+
+ // Call this method on the server side of the IPC Channel once a client is
+ // connected in order to close the client side of the socketpair().
+ void OnClientConnected();
+#endif // defined(OS_POSIX)
+
+ private:
+ // PIMPL to which all channel calls are delegated.
+ class ChannelImpl;
+ ChannelImpl *channel_impl_;
+
+ // The Hello message is internal to the Channel class. It is sent
+ // by the peer when the channel is connected. The message contains
+ // just the process id (pid). The message has a special routing_id
+ // (MSG_ROUTING_NONE) and type (HELLO_MESSAGE_TYPE).
+ enum {
+ HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type (uint16),
+ // to avoid conflicting with normal
+ // message types, which are enumeration
+ // constants starting from 0.
+ };
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_H_
diff --git a/chrome/common/ipc_channel_posix.cc b/chrome/common/ipc_channel_posix.cc
new file mode 100644
index 0000000..23c008a
--- /dev/null
+++ b/chrome/common/ipc_channel_posix.cc
@@ -0,0 +1,803 @@
+// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/ipc_channel_posix.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <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/lock.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/singleton.h"
+#include "chrome/common/chrome_counters.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/file_descriptor_set_posix.h"
+#include "chrome/common/ipc_logging.h"
+#include "chrome/common/ipc_message_utils.h"
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+namespace {
+
+// When running as a browser, we install the client socket in a specific file
+// descriptor number (@kClientChannelFd). 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 (@kClientChannelFd). 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 it's copy of the client socket and remove the mapping.
+//
+// Case 2: unittests - client and server in the same process
+// The IPC server will install a mapping as before. The client will search
+// for a mapping and find out. It duplicates the file descriptor and
+// connects. Once the client connects, the server will close the original
+// copy of the client socket and remove the mapping. Thus, when the client
+// object closes, it will close the only remaining copy of the client socket
+// in the fd table and the server will see EOF on its side.
+//
+// TODO(port): a client process cannot connect to multiple IPC channels with
+// this scheme.
+
+class PipeMap {
+ public:
+ // Lookup a given channel id. Return -1 if not found.
+ int Lookup(const std::string& channel_id) {
+ AutoLock locked(lock_);
+
+ ChannelToFDMap::const_iterator i = map_.find(channel_id);
+ if (i == map_.end())
+ return -1;
+ return i->second;
+ }
+
+ // Remove the mapping for the given channel id. No error is signaled if the
+ // channel_id doesn't exist
+ void Remove(const std::string& channel_id) {
+ AutoLock locked(lock_);
+
+ ChannelToFDMap::iterator i = map_.find(channel_id);
+ if (i != map_.end())
+ map_.erase(i);
+ }
+
+ // Insert a mapping from @channel_id to @fd. It's a fatal error to insert a
+ // mapping if one already exists for the given channel_id
+ void Insert(const std::string& channel_id, int fd) {
+ AutoLock locked(lock_);
+ DCHECK(fd != -1);
+
+ ChannelToFDMap::const_iterator i = map_.find(channel_id);
+ CHECK(i == map_.end()) << "Creating second IPC server for '"
+ << channel_id
+ << "' while first still exists";
+ map_[channel_id] = fd;
+ }
+
+ private:
+ Lock lock_;
+ typedef std::map<std::string, int> ChannelToFDMap;
+ ChannelToFDMap map_;
+};
+
+// This is the file descriptor number that a client process expects to find its
+// IPC socket.
+static const int kClientChannelFd = 3;
+
+// Used to map a channel name to the equivalent FD # in the client process.
+int ChannelNameToClientFD(const std::string& channel_id) {
+ // See the large block comment above PipeMap for the reasoning here.
+ const int fd = Singleton<PipeMap>()->Lookup(channel_id);
+ if (fd != -1)
+ return dup(fd);
+
+ // If we don't find an entry, we assume that the correct value has been
+ // inserted in the magic slot.
+ return kClientChannelFd;
+}
+
+//------------------------------------------------------------------------------
+sockaddr_un sizecheck;
+const size_t kMaxPipeNameLength = sizeof(sizecheck.sun_path);
+
+// Creates a Fifo with the specified name ready to listen on.
+bool CreateServerFifo(const std::string &pipe_name, int* server_listen_fd) {
+ DCHECK(server_listen_fd);
+ DCHECK_GT(pipe_name.length(), 0u);
+ DCHECK_LT(pipe_name.length(), kMaxPipeNameLength);
+
+ if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) {
+ return false;
+ }
+
+ // Create socket.
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ return false;
+ }
+
+ // Make socket non-blocking
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ close(fd);
+ return false;
+ }
+
+ // Delete any old FS instances.
+ unlink(pipe_name.c_str());
+
+ // Create unix_addr structure
+ struct sockaddr_un unix_addr;
+ memset(&unix_addr, 0, sizeof(unix_addr));
+ unix_addr.sun_family = AF_UNIX;
+ snprintf(unix_addr.sun_path, kMaxPipeNameLength, "%s", pipe_name.c_str());
+ size_t unix_addr_len = offsetof(struct sockaddr_un, sun_path) +
+ strlen(unix_addr.sun_path) + 1;
+
+ // Bind the socket.
+ if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr),
+ unix_addr_len) != 0) {
+ close(fd);
+ return false;
+ }
+
+ // Start listening on the socket.
+ const int listen_queue_length = 1;
+ if (listen(fd, listen_queue_length) != 0) {
+ close(fd);
+ return false;
+ }
+
+ *server_listen_fd = fd;
+ return true;
+}
+
+// Accept a connection on a fifo.
+bool ServerAcceptFifoConnection(int server_listen_fd, int* server_socket) {
+ DCHECK(server_socket);
+
+ int accept_fd = accept(server_listen_fd, NULL, 0);
+ if (accept_fd < 0)
+ return false;
+ if (fcntl(accept_fd, F_SETFL, O_NONBLOCK) == -1) {
+ close(accept_fd);
+ return false;
+ }
+
+ *server_socket = accept_fd;
+ return true;
+}
+
+bool ClientConnectToFifo(const std::string &pipe_name, int* client_socket) {
+ DCHECK(client_socket);
+ DCHECK_LT(pipe_name.length(), kMaxPipeNameLength);
+
+ // Create socket.
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ LOG(ERROR) << "fd is invalid";
+ return false;
+ }
+
+ // Make socket non-blocking
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ LOG(ERROR) << "fcntl failed";
+ close(fd);
+ return false;
+ }
+
+ // Create server side of socket.
+ struct sockaddr_un server_unix_addr;
+ memset(&server_unix_addr, 0, sizeof(server_unix_addr));
+ server_unix_addr.sun_family = AF_UNIX;
+ snprintf(server_unix_addr.sun_path, kMaxPipeNameLength, "%s",
+ pipe_name.c_str());
+ size_t server_unix_addr_len = offsetof(struct sockaddr_un, sun_path) +
+ strlen(server_unix_addr.sun_path) + 1;
+
+ int ret_val = -1;
+ do {
+ ret_val = connect(fd, reinterpret_cast<sockaddr*>(&server_unix_addr),
+ server_unix_addr_len);
+ } while (ret_val == -1 && errno == EINTR);
+ if (ret_val != 0) {
+ close(fd);
+ return false;
+ }
+
+ *client_socket = fd;
+ return true;
+}
+
+} // namespace
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
+ Listener* listener)
+ : mode_(mode),
+ is_blocked_on_write_(false),
+ message_send_bytes_written_(0),
+ uses_fifo_(CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kIPCUseFIFO)),
+ server_listen_pipe_(-1),
+ pipe_(-1),
+ client_pipe_(-1),
+ listener_(listener),
+ waiting_connect_(true),
+ processing_incoming_(false),
+ factory_(this) {
+ if (!CreatePipe(channel_id, mode)) {
+ // The pipe may have been closed already.
+ LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
+ "\" in " << (mode == MODE_SERVER ? "server" : "client") <<
+ " mode error(" << strerror(errno) << ").";
+ }
+}
+
+const std::wstring Channel::ChannelImpl::PipeName(
+ const std::wstring& channel_id) const {
+ // TODO(playmobil): This should live in the Chrome user data directory.
+ // TODO(playmobil): Cleanup any stale fifos.
+ return L"/var/tmp/chrome_" + channel_id;
+}
+
+bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
+ Mode mode) {
+ DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
+ pipe_name_ = WideToUTF8(PipeName(channel_id));
+
+ if (uses_fifo_) {
+ // TODO(playmobil): Should we just change pipe_name to be a normal string
+ // everywhere?
+
+ if (mode == MODE_SERVER) {
+ if (!CreateServerFifo(pipe_name_, &server_listen_pipe_)) {
+ return false;
+ }
+ } else {
+ if (!ClientConnectToFifo(pipe_name_, &pipe_)) {
+ return false;
+ }
+ waiting_connect_ = false;
+ }
+ } else {
+ // socketpair()
+ if (mode == MODE_SERVER) {
+ int pipe_fds[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) {
+ 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) {
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return false;
+ }
+ pipe_ = pipe_fds[0];
+ client_pipe_ = pipe_fds[1];
+
+ Singleton<PipeMap>()->Insert(pipe_name_, client_pipe_);
+ } else {
+ pipe_ = ChannelNameToClientFD(pipe_name_);
+ DCHECK(pipe_ > 0);
+ waiting_connect_ = false;
+ }
+ }
+
+ // Create the Hello message to be sent when Connect is called
+ scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
+ HELLO_MESSAGE_TYPE,
+ IPC::Message::PRIORITY_NORMAL));
+ if (!msg->WriteInt(base::GetCurrentProcId())) {
+ Close();
+ return false;
+ }
+
+ output_queue_.push(msg.release());
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+ if (mode_ == MODE_SERVER && uses_fifo_) {
+ if (server_listen_pipe_ == -1) {
+ return false;
+ }
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ server_listen_pipe_,
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &server_listen_connection_watcher_,
+ this);
+ } else {
+ if (pipe_ == -1) {
+ return false;
+ }
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_,
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &read_watcher_,
+ this);
+ waiting_connect_ = false;
+ }
+
+ if (!waiting_connect_)
+ return ProcessOutgoingMessages();
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessIncomingMessages() {
+ ssize_t bytes_read = 0;
+
+ struct msghdr msg = {0};
+ struct iovec iov = {input_buf_, Channel::kReadBufferSize};
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = input_cmsg_buf_;
+
+ for (;;) {
+ msg.msg_controllen = sizeof(input_cmsg_buf_);
+
+ if (bytes_read == 0) {
+ if (pipe_ == -1)
+ return false;
+
+ // Read from pipe.
+ // recvmsg() returns 0 if the connection has closed or EAGAIN if no data
+ // is waiting on the pipe.
+ do {
+ bytes_read = recvmsg(pipe_, &msg, MSG_DONTWAIT);
+ } while (bytes_read == -1 && errno == EINTR);
+
+ if (bytes_read < 0) {
+ if (errno == EAGAIN) {
+ return true;
+ } else {
+ LOG(ERROR) << "pipe error (" << pipe_ << "): " << strerror(errno);
+ return false;
+ }
+ } else if (bytes_read == 0) {
+ // The pipe has closed...
+ Close();
+ return false;
+ }
+ }
+ DCHECK(bytes_read);
+
+ if (client_pipe_ != -1) {
+ Singleton<PipeMap>()->Remove(pipe_name_);
+ close(client_pipe_);
+ client_pipe_ = -1;
+ }
+
+ // a pointer to an array of |num_wire_fds| file descriptors from the read
+ const int* wire_fds = NULL;
+ unsigned num_wire_fds = 0;
+
+ // walk the list of control messages and, if we find an array of file
+ // descriptors, save a pointer to the array
+
+ // This next if statement is to work around an OSX issue where
+ // CMSG_FIRSTHDR will return non-NULL in the case that controllen == 0.
+ // Here's a test case:
+ //
+ // int main() {
+ // struct msghdr msg;
+ // msg.msg_control = &msg;
+ // msg.msg_controllen = 0;
+ // if (CMSG_FIRSTHDR(&msg))
+ // printf("Bug found!\n");
+ // }
+ if (msg.msg_controllen > 0) {
+ // On OSX, CMSG_FIRSTHDR doesn't handle the case where controllen is 0
+ // and will return a pointer into nowhere.
+ for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ DCHECK(payload_len % sizeof(int) == 0);
+ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ num_wire_fds = payload_len / 4;
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ LOG(ERROR) << "SCM_RIGHTS message was truncated"
+ << " cmsg_len:" << cmsg->cmsg_len
+ << " fd:" << pipe_;
+ for (unsigned i = 0; i < num_wire_fds; ++i)
+ close(wire_fds[i]);
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ // Process messages from input buffer.
+ const char *p;
+ const char *end;
+ if (input_overflow_buf_.empty()) {
+ p = input_buf_;
+ end = p + bytes_read;
+ } else {
+ if (input_overflow_buf_.size() >
+ static_cast<size_t>(kMaximumMessageSize - bytes_read)) {
+ input_overflow_buf_.clear();
+ LOG(ERROR) << "IPC message is too big";
+ return false;
+ }
+ input_overflow_buf_.append(input_buf_, bytes_read);
+ p = input_overflow_buf_.data();
+ end = p + input_overflow_buf_.size();
+ }
+
+ // A pointer to an array of |num_fds| file descriptors which includes any
+ // fds that have spilled over from a previous read.
+ const int* fds;
+ unsigned num_fds;
+ unsigned fds_i = 0; // the index of the first unused descriptor
+
+ if (input_overflow_fds_.empty()) {
+ fds = wire_fds;
+ num_fds = num_wire_fds;
+ } else {
+ const size_t prev_size = input_overflow_fds_.size();
+ input_overflow_fds_.resize(prev_size + num_wire_fds);
+ memcpy(&input_overflow_fds_[prev_size], wire_fds,
+ num_wire_fds * sizeof(int));
+ fds = &input_overflow_fds_[0];
+ num_fds = input_overflow_fds_.size();
+ }
+
+ while (p < end) {
+ const char* message_tail = Message::FindNext(p, end);
+ if (message_tail) {
+ int len = static_cast<int>(message_tail - p);
+ Message m(p, len);
+ if (m.header()->num_fds) {
+ // the message has file descriptors
+ const char* error = NULL;
+ if (m.header()->num_fds > num_fds - fds_i) {
+ // the message has been completely received, but we didn't get
+ // enough file descriptors.
+ error = "Message needs unreceived descriptors";
+ }
+
+ if (m.header()->num_fds >
+ FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
+ // There are too many descriptors in this message
+ error = "Message requires an excessive number of descriptors";
+ }
+
+ if (error) {
+ LOG(WARNING) << error
+ << " channel:" << this
+ << " message-type:" << m.type()
+ << " header()->num_fds:" << m.header()->num_fds
+ << " num_fds:" << num_fds
+ << " fds_i:" << fds_i;
+ // close the existing file descriptors so that we don't leak them
+ for (unsigned i = fds_i; i < num_fds; ++i)
+ close(fds[i]);
+ input_overflow_fds_.clear();
+ // abort the connection
+ return false;
+ }
+
+ m.file_descriptor_set()->SetDescriptors(
+ &fds[fds_i], m.header()->num_fds);
+ fds_i += m.header()->num_fds;
+ }
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "received message on channel @" << this <<
+ " with type " << m.type();
+#endif
+ if (m.routing_id() == MSG_ROUTING_NONE &&
+ m.type() == HELLO_MESSAGE_TYPE) {
+ // The Hello message contains only the process id.
+ listener_->OnChannelConnected(MessageIterator(m).NextInt());
+ } else {
+ listener_->OnMessageReceived(m);
+ }
+ p = message_tail;
+ } else {
+ // Last message is partial.
+ break;
+ }
+ }
+ input_overflow_buf_.assign(p, end - p);
+ input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
+
+ // When the input data buffer is empty, the overflow fds should be too. If
+ // this is not the case, we probably have a rogue renderer which is trying
+ // to fill our descriptor table.
+ if (input_overflow_buf_.empty() && !input_overflow_fds_.empty()) {
+ // We close these descriptors in Close()
+ return false;
+ }
+
+ bytes_read = 0; // Get more data.
+ }
+
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessOutgoingMessages() {
+ DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
+ // no connection?
+ is_blocked_on_write_ = false;
+
+ if (output_queue_.empty())
+ return true;
+
+ if (pipe_ == -1)
+ return false;
+
+ // Write out all the messages we can till the write blocks or there are no
+ // more outgoing messages.
+ while (!output_queue_.empty()) {
+ Message* msg = output_queue_.front();
+
+ size_t amt_to_write = msg->size() - message_send_bytes_written_;
+ DCHECK(amt_to_write != 0);
+ const char *out_bytes = reinterpret_cast<const char*>(msg->data()) +
+ message_send_bytes_written_;
+ ssize_t bytes_written = -1;
+ do {
+ struct msghdr msgh = {0};
+ struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write};
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ char buf[CMSG_SPACE(
+ sizeof(int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]))];
+
+ if (message_send_bytes_written_ == 0 &&
+ !msg->file_descriptor_set()->empty()) {
+ // This is the first chunk of a message which has descriptors to send
+ struct cmsghdr *cmsg;
+ const unsigned num_fds = msg->file_descriptor_set()->size();
+
+ DCHECK_LE(num_fds, FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE);
+
+ msgh.msg_control = buf;
+ msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds);
+ cmsg = CMSG_FIRSTHDR(&msgh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
+ msg->file_descriptor_set()->GetDescriptors(
+ reinterpret_cast<int*>(CMSG_DATA(cmsg)));
+ msgh.msg_controllen = cmsg->cmsg_len;
+
+ msg->header()->num_fds = num_fds;
+ }
+
+ bytes_written = sendmsg(pipe_, &msgh, MSG_DONTWAIT);
+ if (bytes_written > 0)
+ msg->file_descriptor_set()->CommitAll();
+ } while (bytes_written == -1 && errno == EINTR);
+
+ if (bytes_written < 0 && errno != EAGAIN) {
+ LOG(ERROR) << "pipe error: " << strerror(errno);
+ return false;
+ }
+
+ if (static_cast<size_t>(bytes_written) != amt_to_write) {
+ if (bytes_written > 0) {
+ // If write() fails with EAGAIN then bytes_written will be -1.
+ message_send_bytes_written_ += bytes_written;
+ }
+
+ // Tell libevent to call us back once things are unblocked.
+ is_blocked_on_write_ = true;
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_,
+ false, // One shot
+ MessageLoopForIO::WATCH_WRITE,
+ &write_watcher_,
+ this);
+ return true;
+ } else {
+ message_send_bytes_written_ = 0;
+
+ // Message sent OK!
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent message @" << msg << " on channel @" << this <<
+ " with type " << msg->type();
+#endif
+ output_queue_.pop();
+ delete msg;
+ }
+ }
+ return true;
+}
+
+bool Channel::ChannelImpl::Send(Message* message) {
+ chrome::Counters::ipc_send_counter().Increment();
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sending message @" << message << " on channel @" << this
+ << " with type " << message->type()
+ << " (" << output_queue_.size() << " in queue)";
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::current()->OnSendMessage(message, L"");
+#endif
+
+ output_queue_.push(message);
+ if (!waiting_connect_) {
+ if (!is_blocked_on_write_) {
+ if (!ProcessOutgoingMessages())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void Channel::ChannelImpl::GetClientFileDescriptorMapping(int *src_fd,
+ int *dest_fd) {
+ DCHECK(mode_ == MODE_SERVER);
+ *src_fd = client_pipe_;
+ *dest_fd = kClientChannelFd;
+}
+
+void Channel::ChannelImpl::OnClientConnected() {
+ // WARNING: this isn't actually called when a client connects.
+ DCHECK(mode_ == MODE_SERVER);
+}
+
+// Called by libevent when we can read from th pipe without blocking.
+void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
+ bool send_server_hello_msg = false;
+ if (waiting_connect_ && mode_ == MODE_SERVER) {
+ // In the case of a socketpair() the server starts listening on its end
+ // of the pipe in Connect().
+ DCHECK(uses_fifo_);
+
+ if (!ServerAcceptFifoConnection(server_listen_pipe_, &pipe_)) {
+ Close();
+ }
+
+ // No need to watch the listening socket any longer since only one client
+ // can connect. So unregister with libevent.
+ server_listen_connection_watcher_.StopWatchingFileDescriptor();
+
+ // Start watching our end of the socket.
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_,
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &read_watcher_,
+ this);
+
+ waiting_connect_ = false;
+ send_server_hello_msg = true;
+ }
+
+ if (!waiting_connect_ && fd == pipe_) {
+ if (!ProcessIncomingMessages()) {
+ Close();
+ listener_->OnChannelError();
+ }
+ }
+
+ // If we're a server and handshaking, then we want to make sure that we
+ // only send our handshake message after we've processed the client's.
+ // This gives us a chance to kill the client if the incoming handshake
+ // is invalid.
+ if (send_server_hello_msg) {
+ // This should be our first write so there's no chance we can block here...
+ DCHECK(is_blocked_on_write_ == false);
+ ProcessOutgoingMessages();
+ }
+}
+
+// Called by libevent when we can write to the pipe without blocking.
+void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) {
+ if (!ProcessOutgoingMessages()) {
+ Close();
+ listener_->OnChannelError();
+ }
+}
+
+void Channel::ChannelImpl::Close() {
+ // Close can be called multiple time, so we need to make sure we're
+ // idempotent.
+
+ // Unregister libevent for the listening socket and close it.
+ server_listen_connection_watcher_.StopWatchingFileDescriptor();
+
+ if (server_listen_pipe_ != -1) {
+ close(server_listen_pipe_);
+ server_listen_pipe_ = -1;
+ }
+
+ // Unregister libevent for the FIFO and close it.
+ read_watcher_.StopWatchingFileDescriptor();
+ write_watcher_.StopWatchingFileDescriptor();
+ if (pipe_ != -1) {
+ close(pipe_);
+ pipe_ = -1;
+ }
+ if (client_pipe_ != -1) {
+ Singleton<PipeMap>()->Remove(pipe_name_);
+ close(client_pipe_);
+ client_pipe_ = -1;
+ }
+
+ // Unlink the FIFO
+ unlink(pipe_name_.c_str());
+
+ while (!output_queue_.empty()) {
+ Message* m = output_queue_.front();
+ output_queue_.pop();
+ delete m;
+ }
+
+ // Close any outstanding, received file descriptors
+ for (std::vector<int>::iterator
+ i = input_overflow_fds_.begin(); i != input_overflow_fds_.end(); ++i) {
+ close(*i);
+ }
+ input_overflow_fds_.clear();
+}
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const std::wstring& channel_id, Mode mode,
+ Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
+}
+
+Channel::~Channel() {
+ delete channel_impl_;
+}
+
+bool Channel::Connect() {
+ return channel_impl_->Connect();
+}
+
+void Channel::Close() {
+ channel_impl_->Close();
+}
+
+void Channel::set_listener(Listener* listener) {
+ channel_impl_->set_listener(listener);
+}
+
+bool Channel::Send(Message* message) {
+ return channel_impl_->Send(message);
+}
+
+void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) {
+ return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
+}
+
+void Channel::OnClientConnected() {
+ return channel_impl_->OnClientConnected();
+}
+
+
+} // namespace IPC
diff --git a/chrome/common/ipc_channel_posix.h b/chrome/common/ipc_channel_posix.h
new file mode 100644
index 0000000..fbdd59b
--- /dev/null
+++ b/chrome/common/ipc_channel_posix.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_POSIX_H_
+#define CHROME_COMMON_IPC_CHANNEL_POSIX_H_
+
+#include "chrome/common/ipc_channel.h"
+
+#include <sys/socket.h> // for CMSG macros
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/message_loop.h"
+#include "chrome/common/file_descriptor_set_posix.h"
+
+namespace IPC {
+
+class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
+ public:
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener);
+ ~ChannelImpl() { Close(); }
+ bool Connect();
+ void Close();
+ void set_listener(Listener* listener) { listener_ = listener; }
+ bool Send(Message* message);
+ void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd);
+ void OnClientConnected();
+
+ private:
+ const std::wstring PipeName(const std::wstring& channel_id) const;
+ bool CreatePipe(const std::wstring& channel_id, Mode mode);
+
+ bool ProcessIncomingMessages();
+ bool ProcessOutgoingMessages();
+
+ void OnFileCanReadWithoutBlocking(int fd);
+ void OnFileCanWriteWithoutBlocking(int fd);
+
+ Mode mode_;
+
+ // After accepting one client connection on our server socket we want to
+ // stop listening.
+ MessageLoopForIO::FileDescriptorWatcher server_listen_connection_watcher_;
+ MessageLoopForIO::FileDescriptorWatcher read_watcher_;
+ MessageLoopForIO::FileDescriptorWatcher write_watcher_;
+
+ // Indicates whether we're currently blocked waiting for a write to complete.
+ bool is_blocked_on_write_;
+
+ // If sending a message blocks then we use this variable
+ // to keep track of where we are.
+ size_t message_send_bytes_written_;
+
+ // If the kTestingChannelID flag is specified, we use a FIFO instead of
+ // a socketpair().
+ bool uses_fifo_;
+
+ int server_listen_pipe_;
+ int pipe_;
+ int client_pipe_; // The client end of our socketpair().
+ std::string pipe_name_;
+
+ Listener* listener_;
+
+ // Messages to be sent are queued here.
+ std::queue<Message*> output_queue_;
+
+ // We read from the pipe into this buffer
+ char input_buf_[Channel::kReadBufferSize];
+
+ enum {
+ // We assume a worst case: kReadBufferSize bytes of messages, where each
+ // message has no payload and a full complement of descriptors.
+ MAX_READ_FDS = (Channel::kReadBufferSize / sizeof(IPC::Message::Header)) *
+ FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE,
+ };
+
+ // This is a control message buffer large enough to hold kMaxReadFDs
+#if defined(OS_MACOSX)
+ // TODO(agl): OSX appears to have non-constant CMSG macros!
+ char input_cmsg_buf_[1024];
+#else
+ char input_cmsg_buf_[CMSG_SPACE(sizeof(int) * MAX_READ_FDS)];
+#endif
+
+ // Large messages that span multiple pipe buffers, get built-up using
+ // this buffer.
+ std::string input_overflow_buf_;
+ std::vector<int> input_overflow_fds_;
+
+ // In server-mode, we have to wait for the client to connect before we
+ // can begin reading. We make use of the input_state_ when performing
+ // the connect operation in overlapped mode.
+ bool waiting_connect_;
+
+ // This flag is set when processing incoming messages. It is used to
+ // avoid recursing through ProcessIncomingMessages, which could cause
+ // problems. TODO(darin): make this unnecessary
+ bool processing_incoming_;
+
+ ScopedRunnableMethodFactory<ChannelImpl> factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_POSIX_H_
diff --git a/chrome/common/ipc_channel_proxy.cc b/chrome/common/ipc_channel_proxy.cc
new file mode 100644
index 0000000..a9def01
--- /dev/null
+++ b/chrome/common/ipc_channel_proxy.cc
@@ -0,0 +1,305 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "base/thread.h"
+#include "chrome/common/ipc_channel_proxy.h"
+#include "chrome/common/ipc_logging.h"
+#include "chrome/common/ipc_message_utils.h"
+
+namespace IPC {
+
+//-----------------------------------------------------------------------------
+
+ChannelProxy::Context::Context(Channel::Listener* listener,
+ MessageFilter* filter,
+ MessageLoop* ipc_message_loop)
+ : listener_message_loop_(MessageLoop::current()),
+ listener_(listener),
+ ipc_message_loop_(ipc_message_loop),
+ channel_(NULL),
+ peer_pid_(0),
+ channel_connected_called_(false) {
+ if (filter)
+ filters_.push_back(filter);
+}
+
+void ChannelProxy::Context::CreateChannel(const std::wstring& id,
+ const Channel::Mode& mode) {
+ DCHECK(channel_ == NULL);
+ channel_id_ = id;
+ channel_ = new Channel(id, mode, this);
+}
+
+bool ChannelProxy::Context::TryFilters(const Message& message) {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging* logger = Logging::current();
+ if (logger->Enabled())
+ logger->OnPreDispatchMessage(message);
+#endif
+
+ for (size_t i = 0; i < filters_.size(); ++i) {
+ if (filters_[i]->OnMessageReceived(message)) {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (logger->Enabled())
+ logger->OnPostDispatchMessage(message, channel_id_);
+#endif
+ return true;
+ }
+ }
+ return false;
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnMessageReceived(const Message& message) {
+ // First give a chance to the filters to process this message.
+ if (!TryFilters(message))
+ OnMessageReceivedNoFilter(message);
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) {
+ // NOTE: This code relies on the listener's message loop not going away while
+ // this thread is active. That should be a reasonable assumption, but it
+ // feels risky. We may want to invent some more indirect way of referring to
+ // a MessageLoop if this becomes a problem.
+ listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Context::OnDispatchMessage, message));
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) {
+ peer_pid_ = peer_pid;
+ for (size_t i = 0; i < filters_.size(); ++i)
+ filters_[i]->OnChannelConnected(peer_pid);
+
+ // See above comment about using listener_message_loop_ here.
+ listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Context::OnDispatchConnected));
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelError() {
+ // See above comment about using listener_message_loop_ here.
+ listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Context::OnDispatchError));
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelOpened() {
+ DCHECK(channel_ != NULL);
+
+ // Assume a reference to ourselves on behalf of this thread. This reference
+ // will be released when we are closed.
+ AddRef();
+
+ if (!channel_->Connect()) {
+ OnChannelError();
+ return;
+ }
+
+ for (size_t i = 0; i < filters_.size(); ++i)
+ filters_[i]->OnFilterAdded(channel_);
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnChannelClosed() {
+ // It's okay for IPC::ChannelProxy::Close to be called more than once, which
+ // would result in this branch being taken.
+ if (!channel_)
+ return;
+
+ for (size_t i = 0; i < filters_.size(); ++i) {
+ filters_[i]->OnChannelClosing();
+ filters_[i]->OnFilterRemoved();
+ }
+
+ // We don't need the filters anymore.
+ filters_.clear();
+
+ delete channel_;
+ channel_ = NULL;
+
+ // Balance with the reference taken during startup. This may result in
+ // self-destruction.
+ Release();
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnSendMessage(Message* message) {
+ if (!channel_->Send(message))
+ OnChannelError();
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnAddFilter(MessageFilter* filter) {
+ filters_.push_back(filter);
+
+ // If the channel has already been created, then we need to send this message
+ // so that the filter gets access to the Channel.
+ if (channel_)
+ filter->OnFilterAdded(channel_);
+
+ // Balances the AddRef in ChannelProxy::AddFilter.
+ filter->Release();
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) {
+ for (size_t i = 0; i < filters_.size(); ++i) {
+ if (filters_[i].get() == filter) {
+ filter->OnFilterRemoved();
+ filters_.erase(filters_.begin() + i);
+ return;
+ }
+ }
+
+ NOTREACHED() << "filter to be removed not found";
+}
+
+// Called on the listener's thread
+void ChannelProxy::Context::OnDispatchMessage(const Message& message) {
+ if (!listener_)
+ return;
+
+ OnDispatchConnected();
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging* logger = Logging::current();
+ if (message.type() == IPC_LOGGING_ID) {
+ logger->OnReceivedLoggingMessage(message);
+ return;
+ }
+
+ if (logger->Enabled())
+ logger->OnPreDispatchMessage(message);
+#endif
+
+ listener_->OnMessageReceived(message);
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (logger->Enabled())
+ logger->OnPostDispatchMessage(message, channel_id_);
+#endif
+}
+
+// Called on the listener's thread
+void ChannelProxy::Context::OnDispatchConnected() {
+ if (channel_connected_called_)
+ return;
+
+ channel_connected_called_ = true;
+ if (listener_)
+ listener_->OnChannelConnected(peer_pid_);
+}
+
+// Called on the listener's thread
+void ChannelProxy::Context::OnDispatchError() {
+ if (listener_)
+ listener_->OnChannelError();
+}
+
+//-----------------------------------------------------------------------------
+
+ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode,
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_thread)
+ : context_(new Context(listener, filter, ipc_thread)) {
+ Init(channel_id, mode, ipc_thread, true);
+}
+
+ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode,
+ MessageLoop* ipc_thread, Context* context,
+ bool create_pipe_now)
+ : context_(context) {
+ Init(channel_id, mode, ipc_thread, create_pipe_now);
+}
+
+void ChannelProxy::Init(const std::wstring& channel_id, Channel::Mode mode,
+ MessageLoop* ipc_thread_loop, bool create_pipe_now) {
+ if (create_pipe_now) {
+ // Create the channel immediately. This effectively sets up the
+ // low-level pipe so that the client can connect. Without creating
+ // the pipe immediately, it is possible for a listener to attempt
+ // to connect and get an error since the pipe doesn't exist yet.
+ context_->CreateChannel(channel_id, mode);
+ } else {
+#if defined(OS_POSIX)
+ // TODO(playmobil): On POSIX, IPC::Channel uses a socketpair(), one side of
+ // which needs to be mapped into the child process' address space.
+ // To know the value of the client side FD we need to have already
+ // created a socketpair which currently occurs in IPC::Channel's
+ // constructor.
+ // If we lazilly construct the IPC::Channel then the caller has no way
+ // of knowing the FD #.
+ //
+ // We can solve this either by having the Channel's creation launch the
+ // subprocess itself or by creating the socketpair() externally.
+ NOTIMPLEMENTED();
+#endif // defined(OS_POSIX)
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::CreateChannel, channel_id, mode));
+ }
+
+ // complete initialization on the background thread
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnChannelOpened));
+}
+
+void ChannelProxy::Close() {
+ // Clear the backpointer to the listener so that any pending calls to
+ // Context::OnDispatchMessage or OnDispatchError will be ignored. It is
+ // possible that the channel could be closed while it is receiving messages!
+ context_->Clear();
+
+ if (context_->ipc_message_loop()) {
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnChannelClosed));
+ }
+}
+
+bool ChannelProxy::Send(Message* message) {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::current()->OnSendMessage(message, context_->channel_id());
+#endif
+
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnSendMessage, message));
+ return true;
+}
+
+void ChannelProxy::AddFilter(MessageFilter* filter) {
+ // We want to addref the filter to prevent it from
+ // being destroyed before the OnAddFilter call is invoked.
+ filter->AddRef();
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnAddFilter, filter));
+}
+
+void ChannelProxy::RemoveFilter(MessageFilter* filter) {
+ context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ context_.get(), &Context::OnRemoveFilter, filter));
+}
+
+#if defined(OS_POSIX)
+// See the TODO regarding lazy initialization of the channel in
+// ChannelProxy::Init().
+// We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe.
+void ChannelProxy::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) {
+ Channel *channel = context_.get()->channel_;
+ DCHECK(channel); // Channel must have been created first.
+ channel->GetClientFileDescriptorMapping(src_fd, dest_fd);
+}
+
+// We assume that IP::Channel::OnClientConnected() is thread-safe.
+void ChannelProxy::OnClientConnected() {
+ Channel *channel = context_.get()->channel_;
+ DCHECK(channel); // Channel must have been created first.
+ channel->OnClientConnected();
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
+} // namespace IPC
diff --git a/chrome/common/ipc_channel_proxy.h b/chrome/common/ipc_channel_proxy.h
new file mode 100644
index 0000000..80a9807
--- /dev/null
+++ b/chrome/common/ipc_channel_proxy.h
@@ -0,0 +1,206 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_PROXY_H__
+#define CHROME_COMMON_IPC_CHANNEL_PROXY_H__
+
+#include <vector>
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "chrome/common/ipc_channel.h"
+
+class MessageLoop;
+
+namespace IPC {
+
+//-----------------------------------------------------------------------------
+// IPC::ChannelProxy
+//
+// This class is a helper class that is useful when you wish to run an IPC
+// channel on a background thread. It provides you with the option of either
+// handling IPC messages on that background thread or having them dispatched to
+// your main thread (the thread on which the IPC::ChannelProxy is created).
+//
+// The API for an IPC::ChannelProxy is very similar to that of an IPC::Channel.
+// When you send a message to an IPC::ChannelProxy, the message is routed to
+// the background thread, where it is then passed to the IPC::Channel's Send
+// method. This means that you can send a message from your thread and your
+// message will be sent over the IPC channel when possible instead of being
+// delayed until your thread returns to its message loop. (Often IPC messages
+// will queue up on the IPC::Channel when there is a lot of traffic, and the
+// channel will not get cycles to flush its message queue until the thread, on
+// which it is running, returns to its message loop.)
+//
+// An IPC::ChannelProxy can have a MessageFilter associated with it, which will
+// be notified of incoming messages on the IPC::Channel's thread. This gives
+// the consumer of IPC::ChannelProxy the ability to respond to incoming
+// messages on this background thread instead of on their own thread, which may
+// be bogged down with other processing. The result can be greatly improved
+// latency for messages that can be handled on a background thread.
+//
+// The consumer of IPC::ChannelProxy is responsible for allocating the Thread
+// instance where the IPC::Channel will be created and operated.
+//
+class ChannelProxy : public Message::Sender {
+ public:
+ // A class that receives messages on the thread where the IPC channel is
+ // running. It can choose to prevent the default action for an IPC message.
+ class MessageFilter : public base::RefCountedThreadSafe<MessageFilter> {
+ public:
+ virtual ~MessageFilter() {}
+
+ // Called on the background thread to provide the filter with access to the
+ // channel. Called when the IPC channel is initialized or when AddFilter
+ // is called if the channel is already initialized.
+ virtual void OnFilterAdded(Channel* channel) {}
+
+ // Called on the background thread when the filter has been removed from
+ // the ChannelProxy and when the Channel is closing. After a filter is
+ // removed, it will not be called again.
+ virtual void OnFilterRemoved() {}
+
+ // Called to inform the filter that the IPC channel is connected and we
+ // have received the internal Hello message from the peer.
+ virtual void OnChannelConnected(int32 peer_pid) {}
+
+ // Called to inform the filter that the IPC channel will be destroyed.
+ // OnFilterRemoved is called immediately after this.
+ virtual void OnChannelClosing() {}
+
+ // Return true to indicate that the message was handled, or false to let
+ // the message be handled in the default way.
+ virtual bool OnMessageReceived(const Message& message) {
+ return false;
+ }
+ };
+
+ // Initializes a channel proxy. The channel_id and mode parameters are
+ // passed directly to the underlying IPC::Channel. The listener is called on
+ // the thread that creates the ChannelProxy. The filter's OnMessageReceived
+ // method is called on the thread where the IPC::Channel is running. The
+ // filter may be null if the consumer is not interested in handling messages
+ // on the background thread. Any message not handled by the filter will be
+ // dispatched to the listener. The given message loop indicates where the
+ // IPC::Channel should be created.
+ ChannelProxy(const std::wstring& channel_id, Channel::Mode mode,
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_thread_loop);
+
+ ~ChannelProxy() {
+ Close();
+ }
+
+ // Close the IPC::Channel. This operation completes asynchronously, once the
+ // background thread processes the command to close the channel. It is ok to
+ // call this method multiple times. Redundant calls are ignored.
+ //
+ // WARNING: The MessageFilter object held by the ChannelProxy is also
+ // released asynchronously, and it may in fact have its final reference
+ // released on the background thread. The caller should be careful to deal
+ // with / allow for this possibility.
+ void Close();
+
+ // Send a message asynchronously. The message is routed to the background
+ // thread where it is passed to the IPC::Channel's Send method.
+ virtual bool Send(Message* message);
+
+ // Used to intercept messages as they are received on the background thread.
+ //
+ // Ordinarily, messages sent to the ChannelProxy are routed to the matching
+ // listener on the worker thread. This API allows code to intercept messages
+ // before they are sent to the worker thread.
+ void AddFilter(MessageFilter* filter);
+ void RemoveFilter(MessageFilter* filter);
+
+#if defined(OS_POSIX)
+ // Calls through to the underlying channel's methods.
+ // TODO(playmobil): For now this is only implemented in the case of
+ // create_pipe_now = true, we need to figure this out for the latter case.
+ void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd);
+ void OnClientConnected();
+#endif // defined(OS_POSIX)
+
+ protected:
+ class Context;
+ // A subclass uses this constructor if it needs to add more information
+ // to the internal state. If create_pipe_now is true, the pipe is created
+ // immediately. Otherwise it's created on the IO thread.
+ ChannelProxy(const std::wstring& channel_id, Channel::Mode mode,
+ MessageLoop* ipc_thread_loop, Context* context,
+ bool create_pipe_now);
+
+ // Used internally to hold state that is referenced on the IPC thread.
+ class Context : public base::RefCountedThreadSafe<Context>,
+ public Channel::Listener {
+ public:
+ Context(Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_thread);
+ virtual ~Context() { }
+ MessageLoop* ipc_message_loop() const { return ipc_message_loop_; }
+ const std::wstring& channel_id() const { return channel_id_; }
+
+ // Dispatches a message on the listener thread.
+ void OnDispatchMessage(const Message& message);
+
+ protected:
+ // IPC::Channel::Listener methods:
+ virtual void OnMessageReceived(const Message& message);
+ virtual void OnChannelConnected(int32 peer_pid);
+ virtual void OnChannelError();
+
+ // Like OnMessageReceived but doesn't try the filters.
+ void OnMessageReceivedNoFilter(const Message& message);
+
+ // Gives the filters a chance at processing |message|.
+ // Returns true if the message was processed, false otherwise.
+ bool TryFilters(const Message& message);
+
+ // Like Open and Close, but called on the IPC thread.
+ virtual void OnChannelOpened();
+ virtual void OnChannelClosed();
+
+ // Called on the consumers thread when the ChannelProxy is closed. At that
+ // point the consumer is telling us that they don't want to receive any
+ // more messages, so we honor that wish by forgetting them!
+ virtual void Clear() { listener_ = NULL; }
+
+ private:
+ friend class ChannelProxy;
+ // Create the Channel
+ void CreateChannel(const std::wstring& id, const Channel::Mode& mode);
+
+ // Methods called via InvokeLater:
+ void OnSendMessage(Message* message_ptr);
+ void OnAddFilter(MessageFilter* filter);
+ void OnRemoveFilter(MessageFilter* filter);
+ void OnDispatchConnected();
+ void OnDispatchError();
+
+ MessageLoop* listener_message_loop_;
+ Channel::Listener* listener_;
+
+ // List of filters. This is only accessed on the IPC thread.
+ std::vector<scoped_refptr<MessageFilter> > filters_;
+ MessageLoop* ipc_message_loop_;
+ Channel* channel_;
+ std::wstring channel_id_;
+ int peer_pid_;
+ bool channel_connected_called_;
+ };
+
+ Context* context() { return context_; }
+
+ private:
+ void Init(const std::wstring& channel_id, Channel::Mode mode,
+ MessageLoop* ipc_thread_loop, bool create_pipe_now);
+
+ // By maintaining this indirection (ref-counted) to our internal state, we
+ // can safely be destroyed while the background thread continues to do stuff
+ // that involves this data.
+ scoped_refptr<Context> context_;
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_PROXY_H__
diff --git a/chrome/common/ipc_channel_win.cc b/chrome/common/ipc_channel_win.cc
new file mode 100644
index 0000000..38e442a
--- /dev/null
+++ b/chrome/common/ipc_channel_win.cc
@@ -0,0 +1,440 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/ipc_channel_win.h"
+
+#include <windows.h>
+#include <sstream>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/non_thread_safe.h"
+#include "base/win_util.h"
+#include "chrome/common/chrome_counters.h"
+#include "chrome/common/ipc_logging.h"
+#include "chrome/common/ipc_message_utils.h"
+
+namespace IPC {
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
+ memset(&context.overlapped, 0, sizeof(context.overlapped));
+ context.handler = channel;
+}
+
+Channel::ChannelImpl::State::~State() {
+ COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
+ starts_with_io_context);
+}
+
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
+ Listener* listener)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
+ pipe_(INVALID_HANDLE_VALUE),
+ listener_(listener),
+ waiting_connect_(mode == MODE_SERVER),
+ processing_incoming_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+ if (!CreatePipe(channel_id, mode)) {
+ // The pipe may have been closed already.
+ LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
+ "\" in " << (mode == 0 ? "server" : "client") << " mode.";
+ }
+}
+
+void Channel::ChannelImpl::Close() {
+ if (thread_check_.get()) {
+ DCHECK(thread_check_->CalledOnValidThread());
+ }
+
+ bool waited = false;
+ if (input_state_.is_pending || output_state_.is_pending) {
+ CancelIo(pipe_);
+ waited = true;
+ }
+
+ // Closing the handle at this point prevents us from issuing more requests
+ // form OnIOCompleted().
+ if (pipe_ != INVALID_HANDLE_VALUE) {
+ CloseHandle(pipe_);
+ pipe_ = INVALID_HANDLE_VALUE;
+ }
+
+ // Make sure all IO has completed.
+ base::Time start = base::Time::Now();
+ while (input_state_.is_pending || output_state_.is_pending) {
+ MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
+ }
+ if (waited) {
+ // We want to see if we block the message loop for too long.
+ UMA_HISTOGRAM_TIMES("AsyncIO.IPCChannelClose", base::Time::Now() - start);
+ }
+
+ while (!output_queue_.empty()) {
+ Message* m = output_queue_.front();
+ output_queue_.pop();
+ delete m;
+ }
+}
+
+bool Channel::ChannelImpl::Send(Message* message) {
+ DCHECK(thread_check_->CalledOnValidThread());
+ chrome::Counters::ipc_send_counter().Increment();
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sending message @" << message << " on channel @" << this
+ << " with type " << message->type()
+ << " (" << output_queue_.size() << " in queue)";
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ Logging::current()->OnSendMessage(message, L"");
+#endif
+
+ output_queue_.push(message);
+ // ensure waiting to write
+ if (!waiting_connect_) {
+ if (!output_state_.is_pending) {
+ if (!ProcessOutgoingMessages(NULL, 0))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+const std::wstring Channel::ChannelImpl::PipeName(
+ const std::wstring& channel_id) const {
+ std::wostringstream ss;
+ // XXX(darin): get application name from somewhere else
+ ss << L"\\\\.\\pipe\\chrome." << channel_id;
+ return ss.str();
+}
+
+bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
+ Mode mode) {
+ DCHECK(pipe_ == INVALID_HANDLE_VALUE);
+ const std::wstring pipe_name = PipeName(channel_id);
+ if (mode == MODE_SERVER) {
+ SECURITY_ATTRIBUTES security_attributes = {0};
+ security_attributes.bInheritHandle = FALSE;
+ security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ if (!win_util::GetLogonSessionOnlyDACL(
+ reinterpret_cast<SECURITY_DESCRIPTOR**>(
+ &security_attributes.lpSecurityDescriptor))) {
+ NOTREACHED();
+ }
+
+ pipe_ = CreateNamedPipeW(pipe_name.c_str(),
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+ FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1, // number of pipe instances
+ // output buffer size (XXX tune)
+ Channel::kReadBufferSize,
+ // input buffer size (XXX tune)
+ Channel::kReadBufferSize,
+ 5000, // timeout in milliseconds (XXX tune)
+ &security_attributes);
+ LocalFree(security_attributes.lpSecurityDescriptor);
+ } else {
+ pipe_ = CreateFileW(pipe_name.c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
+ FILE_FLAG_OVERLAPPED,
+ NULL);
+ }
+ if (pipe_ == INVALID_HANDLE_VALUE) {
+ // If this process is being closed, the pipe may be gone already.
+ LOG(WARNING) << "failed to create pipe: " << GetLastError();
+ return false;
+ }
+
+ // Create the Hello message to be sent when Connect is called
+ scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
+ HELLO_MESSAGE_TYPE,
+ IPC::Message::PRIORITY_NORMAL));
+ if (!m->WriteInt(GetCurrentProcessId())) {
+ CloseHandle(pipe_);
+ pipe_ = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ output_queue_.push(m.release());
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+ DLOG(WARNING) << "Connect called twice";
+
+ if (!thread_check_.get())
+ thread_check_.reset(new NonThreadSafe());
+
+ if (pipe_ == INVALID_HANDLE_VALUE)
+ return false;
+
+ MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
+
+ // Check to see if there is a client connected to our pipe...
+ if (waiting_connect_)
+ ProcessConnection();
+
+ if (!input_state_.is_pending) {
+ // Complete setup asynchronously. By not setting input_state_.is_pending
+ // to true, we indicate to OnIOCompleted that this is the special
+ // initialization signal.
+ MessageLoopForIO::current()->PostTask(FROM_HERE, factory_.NewRunnableMethod(
+ &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
+ }
+
+ if (!waiting_connect_)
+ ProcessOutgoingMessages(NULL, 0);
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessConnection() {
+ DCHECK(thread_check_->CalledOnValidThread());
+ if (input_state_.is_pending)
+ input_state_.is_pending = false;
+
+ // Do we have a client connected to our pipe?
+ if (INVALID_HANDLE_VALUE == pipe_)
+ return false;
+
+ BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
+
+ DWORD err = GetLastError();
+ if (ok) {
+ // Uhm, the API documentation says that this function should never
+ // return success when used in overlapped mode.
+ NOTREACHED();
+ return false;
+ }
+
+ switch (err) {
+ case ERROR_IO_PENDING:
+ input_state_.is_pending = true;
+ break;
+ case ERROR_PIPE_CONNECTED:
+ waiting_connect_ = false;
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessIncomingMessages(
+ MessageLoopForIO::IOContext* context,
+ DWORD bytes_read) {
+ DCHECK(thread_check_->CalledOnValidThread());
+ if (input_state_.is_pending) {
+ input_state_.is_pending = false;
+ DCHECK(context);
+
+ if (!context || !bytes_read)
+ return false;
+ } else {
+ // This happens at channel initialization.
+ DCHECK(!bytes_read && context == &input_state_.context);
+ }
+
+ for (;;) {
+ if (bytes_read == 0) {
+ if (INVALID_HANDLE_VALUE == pipe_)
+ return false;
+
+ // Read from pipe...
+ BOOL ok = ReadFile(pipe_,
+ input_buf_,
+ Channel::kReadBufferSize,
+ &bytes_read,
+ &input_state_.context.overlapped);
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ input_state_.is_pending = true;
+ return true;
+ }
+ LOG(ERROR) << "pipe error: " << err;
+ return false;
+ }
+ input_state_.is_pending = true;
+ return true;
+ }
+ DCHECK(bytes_read);
+
+ // Process messages from input buffer.
+
+ const char* p, *end;
+ if (input_overflow_buf_.empty()) {
+ p = input_buf_;
+ end = p + bytes_read;
+ } else {
+ if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
+ input_overflow_buf_.clear();
+ LOG(ERROR) << "IPC message is too big";
+ return false;
+ }
+ input_overflow_buf_.append(input_buf_, bytes_read);
+ p = input_overflow_buf_.data();
+ end = p + input_overflow_buf_.size();
+ }
+
+ while (p < end) {
+ const char* message_tail = Message::FindNext(p, end);
+ if (message_tail) {
+ int len = static_cast<int>(message_tail - p);
+ const Message m(p, len);
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "received message on channel @" << this <<
+ " with type " << m.type();
+#endif
+ if (m.routing_id() == MSG_ROUTING_NONE &&
+ m.type() == HELLO_MESSAGE_TYPE) {
+ // The Hello message contains only the process id.
+ listener_->OnChannelConnected(MessageIterator(m).NextInt());
+ } else {
+ listener_->OnMessageReceived(m);
+ }
+ p = message_tail;
+ } else {
+ // Last message is partial.
+ break;
+ }
+ }
+ input_overflow_buf_.assign(p, end - p);
+
+ bytes_read = 0; // Get more data.
+ }
+
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessOutgoingMessages(
+ MessageLoopForIO::IOContext* context,
+ DWORD bytes_written) {
+ DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
+ // no connection?
+ DCHECK(thread_check_->CalledOnValidThread());
+
+ if (output_state_.is_pending) {
+ DCHECK(context);
+ output_state_.is_pending = false;
+ if (!context || bytes_written == 0) {
+ DWORD err = GetLastError();
+ LOG(ERROR) << "pipe error: " << err;
+ return false;
+ }
+ // Message was sent.
+ DCHECK(!output_queue_.empty());
+ Message* m = output_queue_.front();
+ output_queue_.pop();
+ delete m;
+ }
+
+ if (output_queue_.empty())
+ return true;
+
+ if (INVALID_HANDLE_VALUE == pipe_)
+ return false;
+
+ // Write to pipe...
+ Message* m = output_queue_.front();
+ BOOL ok = WriteFile(pipe_,
+ m->data(),
+ m->size(),
+ &bytes_written,
+ &output_state_.context.overlapped);
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ output_state_.is_pending = true;
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent pending message @" << m << " on channel @" <<
+ this << " with type " << m->type();
+#endif
+
+ return true;
+ }
+ LOG(ERROR) << "pipe error: " << err;
+ return false;
+ }
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent message @" << m << " on channel @" << this <<
+ " with type " << m->type();
+#endif
+
+ output_state_.is_pending = true;
+ return true;
+}
+
+void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered, DWORD error) {
+ bool ok;
+ DCHECK(thread_check_->CalledOnValidThread());
+ if (context == &input_state_.context) {
+ if (waiting_connect_) {
+ if (!ProcessConnection())
+ return;
+ // We may have some messages queued up to send...
+ if (!output_queue_.empty() && !output_state_.is_pending)
+ ProcessOutgoingMessages(NULL, 0);
+ if (input_state_.is_pending)
+ return;
+ // else, fall-through and look for incoming messages...
+ }
+ // we don't support recursion through OnMessageReceived yet!
+ DCHECK(!processing_incoming_);
+ processing_incoming_ = true;
+ ok = ProcessIncomingMessages(context, bytes_transfered);
+ processing_incoming_ = false;
+ } else {
+ DCHECK(context == &output_state_.context);
+ ok = ProcessOutgoingMessages(context, bytes_transfered);
+ }
+ if (!ok && INVALID_HANDLE_VALUE != pipe_) {
+ // We don't want to re-enter Close().
+ Close();
+ listener_->OnChannelError();
+ }
+}
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const std::wstring& channel_id, Mode mode,
+ Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
+}
+
+Channel::~Channel() {
+ delete channel_impl_;
+}
+
+bool Channel::Connect() {
+ return channel_impl_->Connect();
+}
+
+void Channel::Close() {
+ channel_impl_->Close();
+}
+
+void Channel::set_listener(Listener* listener) {
+ channel_impl_->set_listener(listener);
+}
+
+bool Channel::Send(Message* message) {
+ return channel_impl_->Send(message);
+}
+
+} // namespace IPC
diff --git a/chrome/common/ipc_channel_win.h b/chrome/common/ipc_channel_win.h
new file mode 100644
index 0000000..2a601e1
--- /dev/null
+++ b/chrome/common/ipc_channel_win.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_WIN_H_
+#define CHROME_COMMON_IPC_CHANNEL_WIN_H_
+
+#include "chrome/common/ipc_channel.h"
+
+#include <queue>
+#include <string>
+
+#include "base/message_loop.h"
+
+class NonThreadSafe;
+
+namespace IPC {
+
+class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
+ public:
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener);
+ ~ChannelImpl() { Close(); }
+ bool Connect();
+ void Close();
+ void set_listener(Listener* listener) { listener_ = listener; }
+ bool Send(Message* message);
+ private:
+ const std::wstring PipeName(const std::wstring& channel_id) const;
+ bool CreatePipe(const std::wstring& channel_id, Mode mode);
+
+ bool ProcessConnection();
+ bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
+ DWORD bytes_read);
+ bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
+ DWORD bytes_written);
+
+ // MessageLoop::IOHandler implementation.
+ virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered, DWORD error);
+ private:
+ struct State {
+ explicit State(ChannelImpl* channel);
+ ~State();
+ MessageLoopForIO::IOContext context;
+ bool is_pending;
+ };
+
+ State input_state_;
+ State output_state_;
+
+ HANDLE pipe_;
+
+ Listener* listener_;
+
+ // Messages to be sent are queued here.
+ std::queue<Message*> output_queue_;
+
+ // We read from the pipe into this buffer
+ char input_buf_[Channel::kReadBufferSize];
+
+ // Large messages that span multiple pipe buffers, get built-up using
+ // this buffer.
+ std::string input_overflow_buf_;
+
+ // In server-mode, we have to wait for the client to connect before we
+ // can begin reading. We make use of the input_state_ when performing
+ // the connect operation in overlapped mode.
+ bool waiting_connect_;
+
+ // This flag is set when processing incoming messages. It is used to
+ // avoid recursing through ProcessIncomingMessages, which could cause
+ // problems. TODO(darin): make this unnecessary
+ bool processing_incoming_;
+
+ ScopedRunnableMethodFactory<ChannelImpl> factory_;
+
+ scoped_ptr<NonThreadSafe> thread_check_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_WIN_H_
diff --git a/chrome/common/ipc_fuzzing_tests.cc b/chrome/common/ipc_fuzzing_tests.cc
new file mode 100644
index 0000000..8148723
--- /dev/null
+++ b/chrome/common/ipc_fuzzing_tests.cc
@@ -0,0 +1,430 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <iostream>
+#include <string>
+#include <sstream>
+
+#include "base/message_loop.h"
+#include "base/platform_thread.h"
+#include "base/process_util.h"
+#include "chrome/common/ipc_channel.h"
+#include "chrome/common/ipc_channel_proxy.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "chrome/common/ipc_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+TEST(IPCMessageIntegrity, ReadBeyondBufferStr) {
+ //This was BUG 984408.
+ uint32 v1 = kuint32max - 1;
+ int v2 = 666;
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(v1));
+ EXPECT_TRUE(m.WriteInt(v2));
+
+ void* iter = NULL;
+ std::string vs;
+ EXPECT_FALSE(m.ReadString(&iter, &vs));
+}
+
+TEST(IPCMessageIntegrity, ReadBeyondBufferWStr) {
+ //This was BUG 984408.
+ uint32 v1 = kuint32max - 1;
+ int v2 = 777;
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(v1));
+ EXPECT_TRUE(m.WriteInt(v2));
+
+ void* iter = NULL;
+ std::wstring vs;
+ EXPECT_FALSE(m.ReadWString(&iter, &vs));
+}
+
+TEST(IPCMessageIntegrity, ReadBytesBadIterator) {
+ // This was BUG 1035467.
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(1));
+ EXPECT_TRUE(m.WriteInt(2));
+
+ void* iter = NULL;
+ const char* data = NULL;
+ EXPECT_FALSE(m.ReadBytes(&iter, &data, sizeof(int)));
+}
+
+TEST(IPCMessageIntegrity, ReadVectorNegativeSize) {
+ // A slight variation of BUG 984408. Note that the pickling of vector<char>
+ // has a specialized template which is not vulnerable to this bug. So here
+ // try to hit the non-specialized case vector<P>.
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(-1)); // This is the count of elements.
+ EXPECT_TRUE(m.WriteInt(1));
+ EXPECT_TRUE(m.WriteInt(2));
+ EXPECT_TRUE(m.WriteInt(3));
+
+ std::vector<double> vec;
+ void* iter = 0;
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec));
+}
+
+TEST(IPCMessageIntegrity, ReadVectorTooLarge1) {
+ // This was BUG 1006367. This is the large but positive length case. Again
+ // we try to hit the non-specialized case vector<P>.
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(0x21000003)); // This is the count of elements.
+ EXPECT_TRUE(m.WriteInt64(1));
+ EXPECT_TRUE(m.WriteInt64(2));
+
+ std::vector<int64> vec;
+ void* iter = 0;
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec));
+}
+
+TEST(IPCMessageIntegrity, ReadVectorTooLarge2) {
+ // This was BUG 1006367. This is the large but positive with an additional
+ // integer overflow when computing the actual byte size. Again we try to hit
+ // the non-specialized case vector<P>.
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(0x71000000)); // This is the count of elements.
+ EXPECT_TRUE(m.WriteInt64(1));
+ EXPECT_TRUE(m.WriteInt64(2));
+
+ std::vector<int64> vec;
+ void* iter = 0;
+ EXPECT_FALSE(ReadParam(&m, &iter, &vec));
+}
+
+// We don't actually use the messages defined in this file, but we do this
+// to get to the IPC macros.
+#define MESSAGES_INTERNAL_FILE "chrome/common/ipc_sync_message_unittest.h"
+#include "chrome/common/ipc_message_macros.h"
+
+enum IPCMessageIds {
+ UNUSED_IPC_TYPE,
+ SERVER_FIRST_IPC_TYPE, // 1st Test message tag.
+ SERVER_SECOND_IPC_TYPE, // 2nd Test message tag.
+ SERVER_THIRD_IPC_TYPE, // 3rd Test message tag.
+ CLIENT_MALFORMED_IPC, // Sent to client if server detects bad message.
+ CLIENT_UNHANDLED_IPC // Sent to client if server detects unhanded IPC.
+};
+
+// Generic message class that is an int followed by a wstring.
+class MsgClassIS : public IPC::MessageWithTuple< Tuple2<int, std::wstring> > {
+ public:
+ enum { ID = SERVER_FIRST_IPC_TYPE };
+ MsgClassIS(const int& arg1, const std::wstring& arg2)
+ : IPC::MessageWithTuple< Tuple2<int, std::wstring> >(
+ MSG_ROUTING_CONTROL, ID, MakeTuple(arg1, arg2)) {}
+};
+
+// Generic message class that is a wstring followed by an int.
+class MsgClassSI : public IPC::MessageWithTuple< Tuple2<std::wstring, int> > {
+ public:
+ enum { ID = SERVER_SECOND_IPC_TYPE };
+ MsgClassSI(const std::wstring& arg1, const int& arg2)
+ : IPC::MessageWithTuple< Tuple2<std::wstring, int> >(
+ MSG_ROUTING_CONTROL, ID, MakeTuple(arg1, arg2)) {}
+};
+
+// Message to create a mutex in the IPC server, using the received name.
+class MsgDoMutex : public IPC::MessageWithTuple< Tuple2<std::wstring, int> > {
+ public:
+ enum { ID = SERVER_THIRD_IPC_TYPE };
+ MsgDoMutex(const std::wstring& mutex_name, const int& unused)
+ : IPC::MessageWithTuple< Tuple2<std::wstring, int> >(
+ MSG_ROUTING_CONTROL, ID, MakeTuple(mutex_name, unused)) {}
+};
+
+class SimpleListener : public IPC::Channel::Listener {
+ public:
+ SimpleListener() : other_(NULL) {
+ }
+ void Init(IPC::Message::Sender* s) {
+ other_ = s;
+ }
+ protected:
+ IPC::Message::Sender* other_;
+};
+
+enum {
+ FUZZER_ROUTING_ID = 5
+};
+
+// The fuzzer server class. It runs in a child process and expects
+// only two IPC calls; after that it exits the message loop which
+// terminates the child process.
+class FuzzerServerListener : public SimpleListener {
+ public:
+ FuzzerServerListener() : message_count_(2), pending_messages_(0) {
+ }
+ virtual void OnMessageReceived(const IPC::Message& msg) {
+ if (msg.routing_id() == MSG_ROUTING_CONTROL) {
+ ++pending_messages_;
+ IPC_BEGIN_MESSAGE_MAP(FuzzerServerListener, msg)
+ IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage)
+ IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage)
+ IPC_END_MESSAGE_MAP()
+ if (pending_messages_) {
+ // Probably a problem de-serializing the message.
+ ReplyMsgNotHandled(msg.type());
+ }
+ }
+ }
+
+ private:
+ void OnMsgClassISMessage(int value, const std::wstring& text) {
+ UseData(MsgClassIS::ID, value, text);
+ RoundtripAckReply(FUZZER_ROUTING_ID, MsgClassIS::ID, value);
+ Cleanup();
+ }
+
+ void OnMsgClassSIMessage(const std::wstring& text, int value) {
+ UseData(MsgClassSI::ID, value, text);
+ RoundtripAckReply(FUZZER_ROUTING_ID, MsgClassSI::ID, value);
+ Cleanup();
+ }
+
+ bool RoundtripAckReply(int routing, int type_id, int reply) {
+ IPC::Message* message = new IPC::Message(routing, type_id,
+ IPC::Message::PRIORITY_NORMAL);
+ message->WriteInt(reply + 1);
+ message->WriteInt(reply);
+ return other_->Send(message);
+ }
+
+ void Cleanup() {
+ --message_count_;
+ --pending_messages_;
+ if (0 == message_count_)
+ MessageLoop::current()->Quit();
+ }
+
+ void ReplyMsgNotHandled(int type_id) {
+ RoundtripAckReply(FUZZER_ROUTING_ID, CLIENT_UNHANDLED_IPC, type_id);
+ Cleanup();
+ }
+
+ void UseData(int caller, int value, const std::wstring& text) {
+ std::wostringstream wos;
+ wos << L"IPC fuzzer:" << caller << " [" << value << L" " << text << L"]\n";
+ std::wstring output = wos.str();
+ LOG(WARNING) << output.c_str();
+ };
+
+ int message_count_;
+ int pending_messages_;
+};
+
+class FuzzerClientListener : public SimpleListener {
+ public:
+ FuzzerClientListener() : last_msg_(NULL) {
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& msg) {
+ last_msg_ = new IPC::Message(msg);
+ MessageLoop::current()->Quit();
+ }
+
+ bool ExpectMessage(int value, int type_id) {
+ if (!MsgHandlerInternal(type_id))
+ return false;
+ int msg_value1 = 0;
+ int msg_value2 = 0;
+ void* iter = NULL;
+ if (!last_msg_->ReadInt(&iter, &msg_value1))
+ return false;
+ if (!last_msg_->ReadInt(&iter, &msg_value2))
+ return false;
+ if ((msg_value2 + 1) != msg_value1)
+ return false;
+ if (msg_value2 != value)
+ return false;
+
+ delete last_msg_;
+ last_msg_ = NULL;
+ return true;
+ }
+
+ bool ExpectMsgNotHandled(int type_id) {
+ return ExpectMessage(type_id, CLIENT_UNHANDLED_IPC);
+ }
+
+ private:
+ bool MsgHandlerInternal(int type_id) {
+ MessageLoop::current()->Run();
+ if (NULL == last_msg_)
+ return false;
+ if (FUZZER_ROUTING_ID != last_msg_->routing_id())
+ return false;
+ return (type_id == last_msg_->type());
+ };
+
+ IPC::Message* last_msg_;
+};
+
+// Runs the fuzzing server child mode. Returns when the preset number
+// of messages have been received.
+MULTIPROCESS_TEST_MAIN(RunFuzzServer) {
+ MessageLoopForIO main_message_loop;
+ FuzzerServerListener listener;
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_CLIENT, &listener);
+ chan.Connect();
+ listener.Init(&chan);
+ MessageLoop::current()->Run();
+ return 0;
+}
+
+class IPCFuzzingTest : public IPCChannelTest {
+};
+
+// This test makes sure that the FuzzerClientListener and FuzzerServerListener
+// are working properly by generating two well formed IPC calls.
+TEST_F(IPCFuzzingTest, SanityTest) {
+ FuzzerClientListener listener;
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan);
+ ASSERT_TRUE(server_process);
+ PlatformThread::Sleep(1000);
+ ASSERT_TRUE(chan.Connect());
+ listener.Init(&chan);
+
+ IPC::Message* msg = NULL;
+ int value = 43;
+ msg = new MsgClassIS(value, L"expect 43");
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(value, MsgClassIS::ID));
+
+ msg = new MsgClassSI(L"expect 44", ++value);
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(value, MsgClassSI::ID));
+
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000));
+ base::CloseProcessHandle(server_process);
+}
+
+// This test uses a payload that is smaller than expected.
+// This generates an error while unpacking the IPC buffer which in
+// In debug this triggers an assertion and in release it is ignored(!!). Right
+// after we generate another valid IPC to make sure framing is working
+// properly.
+#ifdef NDEBUG
+TEST_F(IPCFuzzingTest, MsgBadPayloadShort) {
+ FuzzerClientListener listener;
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan);
+ ASSERT_TRUE(server_process);
+ PlatformThread::Sleep(1000);
+ ASSERT_TRUE(chan.Connect());
+ listener.Init(&chan);
+
+ IPC::Message* msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(666);
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMsgNotHandled(MsgClassIS::ID));
+
+ msg = new MsgClassSI(L"expect one", 1);
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(1, MsgClassSI::ID));
+
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000));
+ base::CloseProcessHandle(server_process);
+}
+#endif // NDEBUG
+
+// This test uses a payload that has too many arguments, but so the payload
+// size is big enough so the unpacking routine does not generate an error as
+// in the case of MsgBadPayloadShort test.
+// This test does not pinpoint a flaw (per se) as by design we don't carry
+// type information on the IPC message.
+TEST_F(IPCFuzzingTest, MsgBadPayloadArgs) {
+ FuzzerClientListener listener;
+ IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ base::ProcessHandle server_process = SpawnChild(FUZZER_SERVER, &chan);
+ ASSERT_TRUE(server_process);
+ PlatformThread::Sleep(1000);
+ ASSERT_TRUE(chan.Connect());
+ listener.Init(&chan);
+
+ IPC::Message* msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteWString(L"d");
+ msg->WriteInt(0);
+ msg->WriteInt(0x65); // Extra argument.
+
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(0, MsgClassSI::ID));
+
+ // Now send a well formed message to make sure the receiver wasn't
+ // thrown out of sync by the extra argument.
+ msg = new MsgClassIS(3, L"expect three");
+ chan.Send(msg);
+ EXPECT_TRUE(listener.ExpectMessage(3, MsgClassIS::ID));
+
+ EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000));
+ base::CloseProcessHandle(server_process);
+}
+
+// This class is for testing the IPC_BEGIN_MESSAGE_MAP_EX macros.
+class ServerMacroExTest {
+ public:
+ ServerMacroExTest() : unhandled_msgs_(0) {
+ }
+ virtual bool OnMessageReceived(const IPC::Message& msg) {
+ bool msg_is_ok = false;
+ IPC_BEGIN_MESSAGE_MAP_EX(ServerMacroExTest, msg, msg_is_ok)
+ IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage)
+ IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage)
+ IPC_MESSAGE_UNHANDLED(++unhandled_msgs_)
+ IPC_END_MESSAGE_MAP_EX()
+ return msg_is_ok;
+ }
+
+ int unhandled_msgs() const {
+ return unhandled_msgs_;
+ }
+
+ private:
+ void OnMsgClassISMessage(int value, const std::wstring& text) {
+ }
+ void OnMsgClassSIMessage(const std::wstring& text, int value) {
+ }
+
+ int unhandled_msgs_;
+};
+
+TEST_F(IPCFuzzingTest, MsgMapExMacro) {
+ IPC::Message* msg = NULL;
+ ServerMacroExTest server;
+
+ // Test the regular messages.
+ msg = new MsgClassIS(3, L"text3");
+ EXPECT_TRUE(server.OnMessageReceived(*msg));
+ delete msg;
+ msg = new MsgClassSI(L"text2", 2);
+ EXPECT_TRUE(server.OnMessageReceived(*msg));
+ delete msg;
+
+#ifdef NDEBUG
+ // Test a bad message.
+ msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(2);
+ EXPECT_FALSE(server.OnMessageReceived(*msg));
+ delete msg;
+
+ msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(0x64);
+ msg->WriteInt(0x32);
+ EXPECT_FALSE(server.OnMessageReceived(*msg));
+ delete msg;
+
+ EXPECT_EQ(0, server.unhandled_msgs());
+#endif
+}
diff --git a/chrome/common/ipc_logging.cc b/chrome/common/ipc_logging.cc
new file mode 100644
index 0000000..cb63533
--- /dev/null
+++ b/chrome/common/ipc_logging.cc
@@ -0,0 +1,320 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/ipc_logging.h"
+
+#if defined(OS_POSIX)
+#ifdef IPC_MESSAGE_LOG_ENABLED
+// This will cause render_messages.h etc to define ViewMsgLog and friends.
+#define IPC_MESSAGE_MACROS_LOG_ENABLED
+#endif
+#endif
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/time.h"
+#include "base/waitable_event.h"
+#include "base/waitable_event_watcher.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/ipc_sync_message.h"
+#include "chrome/common/ipc_message_utils.h"
+
+// This include list should contain all _messages.h header files so that they
+// can get *MsgLog function etc. This makes ipc logs much more informative.
+#include "chrome/common/render_messages.h"
+#include "chrome/test/automation/automation_messages.h"
+
+#if defined(OS_WIN)
+// Pulling this file in Mac/Linux causes a lot of binaries to need to bring in
+// WebKit and all the dependencies, which results in a very large number of
+// linker errors.
+#include "chrome/common/plugin_messages.h"
+#endif
+
+#if defined(OS_POSIX)
+#include "base/string_util.h"
+#include <unistd.h>
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+
+using base::Time;
+
+// IPC::Logging is allocated as a singleton, so we don't need any kind of
+// special retention program.
+template <>
+struct RunnableMethodTraits<IPC::Logging> {
+ static void RetainCallee(IPC::Logging*) {}
+ static void ReleaseCallee(IPC::Logging*) {}
+};
+
+namespace IPC {
+
+const wchar_t kLoggingEventName[] = L"ChromeIPCLog.%d";
+const int kLogSendDelayMs = 100;
+
+// We use a pointer to the function table to avoid any linker dependencies on
+// all the traits used as IPC message parameters.
+Logging::LogFunction *Logging::log_function_mapping_;
+
+Logging::Logging()
+ : logging_event_on_(NULL),
+ logging_event_off_(NULL),
+ enabled_(false),
+ queue_invoke_later_pending_(false),
+ sender_(NULL),
+ main_thread_(MessageLoop::current()),
+ consumer_(NULL) {
+ // Create an event for this browser instance that's set when logging is
+ // enabled, so child processes can know when logging is enabled.
+
+#if defined(OS_WIN)
+ // On Windows we have a couple of named events which switch logging on and
+ // off.
+ int browser_pid;
+ const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
+ std::wstring process_type =
+ parsed_command_line.GetSwitchValue(switches::kProcessType);
+ if (process_type.empty()) {
+ browser_pid = GetCurrentProcessId();
+ } else {
+ std::wstring channel_name =
+ parsed_command_line.GetSwitchValue(switches::kProcessChannelID);
+
+ browser_pid = _wtoi(channel_name.c_str());
+ DCHECK(browser_pid != 0);
+ }
+
+ std::wstring event_name = GetEventName(browser_pid, true);
+ logging_event_on_.reset(new base::WaitableEvent(
+ CreateEvent(NULL, TRUE, FALSE, event_name.c_str())));
+
+ event_name = GetEventName(browser_pid, false);
+ logging_event_off_.reset(new base::WaitableEvent(
+ CreateEvent(NULL, TRUE, FALSE, event_name.c_str())));
+
+ RegisterWaitForEvent(true);
+#elif defined(OS_POSIX)
+ if (getenv("CHROME_IPC_LOGGING"))
+ enabled_ = true;
+ SetLoggerFunctions(g_log_function_mapping);
+#endif
+
+ MessageLoop::current()->AddDestructionObserver(this);
+}
+
+Logging::~Logging() {
+}
+
+Logging* Logging::current() {
+ return Singleton<Logging>::get();
+}
+
+void Logging::RegisterWaitForEvent(bool enabled) {
+ watcher_.StopWatching();
+ watcher_.StartWatching(
+ enabled ? logging_event_on_.get() : logging_event_off_.get(), this);
+}
+
+void Logging::OnWaitableEventSignaled(base::WaitableEvent* event) {
+ enabled_ = event == logging_event_on_.get();
+ RegisterWaitForEvent(!enabled_);
+}
+
+void Logging::WillDestroyCurrentMessageLoop() {
+ watcher_.StopWatching();
+}
+
+void Logging::SetLoggerFunctions(LogFunction *functions) {
+ log_function_mapping_ = functions;
+}
+
+#if defined(OS_WIN)
+std::wstring Logging::GetEventName(bool enabled) {
+ return current()->GetEventName(GetCurrentProcessId(), enabled);
+}
+#endif
+
+std::wstring Logging::GetEventName(int browser_pid, bool enabled) {
+ std::wstring result = StringPrintf(kLoggingEventName, browser_pid);
+ result += enabled ? L"on" : L"off";
+ return result;
+}
+
+void Logging::SetConsumer(Consumer* consumer) {
+ consumer_ = consumer;
+}
+
+void Logging::Enable() {
+ logging_event_off_->Reset();
+ logging_event_on_->Signal();
+}
+
+void Logging::Disable() {
+ logging_event_on_->Reset();
+ logging_event_off_->Signal();
+}
+
+void Logging::OnSendLogs() {
+ queue_invoke_later_pending_ = false;
+ if (!sender_)
+ return;
+
+ Message* msg = new Message(
+ MSG_ROUTING_CONTROL, IPC_LOGGING_ID, Message::PRIORITY_NORMAL);
+ WriteParam(msg, queued_logs_);
+ queued_logs_.clear();
+ sender_->Send(msg);
+}
+
+void Logging::SetIPCSender(IPC::Message::Sender* sender) {
+ sender_ = sender;
+}
+
+void Logging::OnReceivedLoggingMessage(const Message& message) {
+ std::vector<LogData> data;
+ void* iter = NULL;
+ if (!ReadParam(&message, &iter, &data))
+ return;
+
+ for (size_t i = 0; i < data.size(); ++i) {
+ Log(data[i]);
+ }
+}
+
+void Logging::OnSendMessage(Message* message, const std::wstring& 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(L"", *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::wstring& channel_id) {
+ if (!Enabled() ||
+#if defined(OS_WIN)
+ !message.sent_time() ||
+#endif
+ message.dont_log())
+ return;
+
+ LogData data;
+ GenerateLogData(channel_id, message, &data);
+
+ if (MessageLoop::current() == main_thread_) {
+ Log(data);
+ } else {
+ main_thread_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Logging::Log, data));
+ }
+}
+
+void Logging::GetMessageText(uint16 type, std::wstring* name,
+ const Message* message,
+ std::wstring* params) {
+ if (!log_function_mapping_)
+ return;
+
+ int message_class = type >> 12;
+ if (log_function_mapping_[message_class] != NULL) {
+ log_function_mapping_[message_class](type, name, message, params);
+ } else {
+ DLOG(INFO) << "No logger function associated with message class " <<
+ message_class;
+ }
+}
+
+void Logging::Log(const LogData& data) {
+#if defined(OS_WIN)
+ if (consumer_) {
+ // We're in the browser process.
+ consumer_->Log(data);
+ } else {
+ // We're in the renderer or plugin processes.
+ if (sender_) {
+ queued_logs_.push_back(data);
+ if (!queue_invoke_later_pending_) {
+ queue_invoke_later_pending_ = true;
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(
+ this, &Logging::OnSendLogs), kLogSendDelayMs);
+ }
+ }
+ }
+#elif defined(OS_POSIX)
+ // On POSIX, for now, we just dump the log to stderr
+ fprintf(stderr, "ipc %s %d %s %s %s\n",
+ WideToUTF8(data.channel).c_str(),
+ data.type,
+ WideToUTF8(data.flags).c_str(),
+ WideToUTF8(data.message_name).c_str(),
+ WideToUTF8(data.params).c_str());
+#endif
+}
+
+void GenerateLogData(const std::wstring& channel, const Message& message,
+ LogData* data) {
+ if (message.is_reply()) {
+ // "data" should already be filled in.
+ std::wstring params;
+ Logging::GetMessageText(data->type, NULL, &message, &params);
+
+ if (!data->params.empty() && !params.empty())
+ data->params += L", ";
+
+ data->flags += L" DR";
+
+ data->params += params;
+ } else {
+ std::wstring flags;
+ if (message.is_sync())
+ flags = L"S";
+
+ if (message.is_reply())
+ flags += L"R";
+
+ if (message.is_reply_error())
+ flags += L"E";
+
+ std::wstring params, message_name;
+ Logging::GetMessageText(message.type(), &message_name, &message, &params);
+
+ data->channel = channel;
+ data->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/chrome/common/ipc_logging.h b/chrome/common/ipc_logging.h
new file mode 100644
index 0000000..cfdcf21
--- /dev/null
+++ b/chrome/common/ipc_logging.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_LOGGING_H_
+#define CHROME_COMMON_IPC_LOGGING_H_
+
+#include "chrome/common/ipc_message.h" // For IPC_MESSAGE_LOG_ENABLED.
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/singleton.h"
+#include "base/waitable_event_watcher.h"
+#include "chrome/common/ipc_message_utils.h"
+
+class MessageLoop;
+
+namespace IPC {
+
+class Message;
+
+// One instance per process. Needs to be created on the main thread (the UI
+// thread in the browser) but OnPreDispatchMessage/OnPostDispatchMessage
+// can be called on other threads.
+class Logging : public base::WaitableEventWatcher::Delegate,
+ public MessageLoop::DestructionObserver {
+ public:
+ // Implemented by consumers of log messages.
+ class Consumer {
+ public:
+ virtual void Log(const LogData& data) = 0;
+ };
+
+ void SetConsumer(Consumer* consumer);
+
+ ~Logging();
+ static Logging* current();
+
+ void Enable();
+ void Disable();
+ bool Enabled() const { return enabled_; }
+
+ // Called by child processes to give the logger object the channel to send
+ // logging data to the browser process.
+ void SetIPCSender(Message::Sender* sender);
+
+ // Called in the browser process when logging data from a child process is
+ // received.
+ void OnReceivedLoggingMessage(const Message& message);
+
+ void OnSendMessage(Message* message, const std::wstring& channel_id);
+ void OnPreDispatchMessage(const Message& message);
+ void OnPostDispatchMessage(const Message& message,
+ const std::wstring& channel_id);
+
+ // Returns the name of the logging enabled/disabled events so that the
+ // sandbox can add them to to the policy. If true, gets the name of the
+ // enabled event, if false, gets the name of the disabled event.
+ static std::wstring GetEventName(bool enabled);
+
+ // Like the *MsgLog functions declared for each message class, except this
+ // calls the correct one based on the message type automatically. Defined in
+ // ipc_logging.cc.
+ static void GetMessageText(uint16 type, std::wstring* name,
+ const Message* message, std::wstring* params);
+
+ // WaitableEventWatcher::Delegate implementation
+ void OnWaitableEventSignaled(base::WaitableEvent* event);
+
+ // MessageLoop::DestructionObserver implementation
+ void WillDestroyCurrentMessageLoop();
+
+ typedef void (*LogFunction)(uint16 type,
+ std::wstring* name,
+ const Message* msg,
+ std::wstring* params);
+
+ static void SetLoggerFunctions(LogFunction *functions);
+
+ private:
+ friend struct DefaultSingletonTraits<Logging>;
+ Logging();
+
+ std::wstring GetEventName(int browser_pid, bool enabled);
+ void OnSendLogs();
+ void Log(const LogData& data);
+
+ void RegisterWaitForEvent(bool enabled);
+
+ base::WaitableEventWatcher watcher_;
+
+ scoped_ptr<base::WaitableEvent> logging_event_on_;
+ scoped_ptr<base::WaitableEvent> logging_event_off_;
+ bool enabled_;
+
+ std::vector<LogData> queued_logs_;
+ bool queue_invoke_later_pending_;
+
+ Message::Sender* sender_;
+ MessageLoop* main_thread_;
+
+ Consumer* consumer_;
+
+ static LogFunction *log_function_mapping_;
+};
+
+} // namespace IPC
+
+#endif // IPC_MESSAGE_LOG_ENABLED
+
+#endif // CHROME_COMMON_IPC_LOGGING_H_
diff --git a/chrome/common/ipc_maybe.h b/chrome/common/ipc_maybe.h
new file mode 100644
index 0000000..de45dc0
--- /dev/null
+++ b/chrome/common/ipc_maybe.h
@@ -0,0 +1,19 @@
+#ifndef CHROME_COMMON_MAYBE_H_
+#define CHROME_COMMON_MAYBE_H_
+
+namespace IPC {
+
+// The Maybe type can be used to avoid serialising a type when it's invalid.
+// This is most useful in conjunction with FileDescriptor, as there's no
+// possible invalid value which can be serialised (one can type to use -1, but
+// the IPC channel will fail). It may also be useful if the invalid value is
+// otherwise expensive to serialise.
+template<typename A>
+struct Maybe {
+ bool valid;
+ A value;
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_MAYBE_H_
diff --git a/chrome/common/ipc_message.cc b/chrome/common/ipc_message.cc
new file mode 100644
index 0000000..d7cc49d
--- /dev/null
+++ b/chrome/common/ipc_message.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/ipc_message.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include "chrome/common/file_descriptor_set_posix.h"
+#endif
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+Message::~Message() {
+}
+
+Message::Message()
+ : Pickle(sizeof(Header)) {
+ header()->routing = header()->type = header()->flags = 0;
+#if defined(OS_POSIX)
+ header()->num_fds = 0;
+#endif
+ InitLoggingVariables();
+}
+
+Message::Message(int32 routing_id, uint16 type, PriorityValue priority)
+ : Pickle(sizeof(Header)) {
+ header()->routing = routing_id;
+ header()->type = type;
+ header()->flags = priority;
+#if defined(OS_POSIX)
+ header()->num_fds = 0;
+#endif
+ InitLoggingVariables();
+}
+
+Message::Message(const char* data, int data_len) : Pickle(data, data_len) {
+ InitLoggingVariables();
+}
+
+Message::Message(const Message& other) : Pickle(other) {
+ InitLoggingVariables();
+#if defined(OS_POSIX)
+ file_descriptor_set_ = other.file_descriptor_set_;
+#endif
+}
+
+void Message::InitLoggingVariables() {
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ received_time_ = 0;
+ dont_log_ = false;
+ log_data_ = NULL;
+#endif
+}
+
+Message& Message::operator=(const Message& other) {
+ *static_cast<Pickle*>(this) = other;
+#if defined(OS_POSIX)
+ file_descriptor_set_ = other.file_descriptor_set_;
+#endif
+ return *this;
+}
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+void Message::set_sent_time(int64 time) {
+ DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0);
+ header()->flags |= HAS_SENT_TIME_BIT;
+ WriteInt64(time);
+}
+
+int64 Message::sent_time() const {
+ if ((header()->flags & HAS_SENT_TIME_BIT) == 0)
+ return 0;
+
+ const char* data = end_of_payload();
+ data -= sizeof(int64);
+ return *(reinterpret_cast<const int64*>(data));
+}
+
+void Message::set_received_time(int64 time) const {
+ received_time_ = time;
+}
+#endif
+
+#if defined(OS_POSIX)
+bool Message::WriteFileDescriptor(const base::FileDescriptor& descriptor) {
+ // We write the index of the descriptor so that we don't have to
+ // keep the current descriptor as extra decoding state when deserialising.
+ WriteInt(file_descriptor_set()->size());
+ if (descriptor.auto_close) {
+ return file_descriptor_set()->AddAndAutoClose(descriptor.fd);
+ } else {
+ return file_descriptor_set()->Add(descriptor.fd);
+ }
+}
+
+bool Message::ReadFileDescriptor(void** iter,
+ base::FileDescriptor* descriptor) const {
+ int descriptor_index;
+ if (!ReadInt(iter, &descriptor_index))
+ return false;
+
+ FileDescriptorSet* file_descriptor_set = file_descriptor_set_.get();
+ if (!file_descriptor_set)
+ return false;
+
+ descriptor->fd = file_descriptor_set->GetDescriptorAt(descriptor_index);
+ descriptor->auto_close = false;
+
+ return descriptor->fd >= 0;
+}
+
+void Message::EnsureFileDescriptorSet() {
+ if (file_descriptor_set_.get() == NULL)
+ file_descriptor_set_ = new FileDescriptorSet;
+}
+
+#endif
+
+} // namespace IPC
diff --git a/chrome/common/ipc_message.h b/chrome/common/ipc_message.h
new file mode 100644
index 0000000..af4a0df
--- /dev/null
+++ b/chrome/common/ipc_message.h
@@ -0,0 +1,279 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_MESSAGE_H__
+#define CHROME_COMMON_IPC_MESSAGE_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/pickle.h"
+
+#ifndef NDEBUG
+#define IPC_MESSAGE_LOG_ENABLED
+#endif
+
+#if defined(OS_POSIX)
+#include "base/ref_counted.h"
+#endif
+
+namespace base {
+class FileDescriptor;
+}
+
+class FileDescriptorSet;
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+class Channel;
+class Message;
+struct LogData;
+
+class Message : public Pickle {
+ public:
+ // Implemented by objects that can send IPC messages across a channel.
+ class Sender {
+ public:
+ virtual ~Sender() {}
+
+ // Sends the given IPC message. The implementor takes ownership of the
+ // given Message regardless of whether or not this method succeeds. This
+ // is done to make this method easier to use. Returns true on success and
+ // false otherwise.
+ virtual bool Send(Message* msg) = 0;
+ };
+
+ enum PriorityValue {
+ PRIORITY_LOW = 1,
+ PRIORITY_NORMAL,
+ PRIORITY_HIGH
+ };
+
+ virtual ~Message();
+
+ Message();
+
+ // Initialize a message with a user-defined type, priority value, and
+ // destination WebView ID.
+ Message(int32 routing_id, uint16 type, PriorityValue priority);
+
+ // Initializes a message from a const block of data. The data is not copied;
+ // instead the data is merely referenced by this message. Only const methods
+ // should be used on the message when initialized this way.
+ Message(const char* data, int data_len);
+
+ Message(const Message& other);
+ Message& operator=(const Message& other);
+
+ PriorityValue priority() const {
+ return static_cast<PriorityValue>(header()->flags & PRIORITY_MASK);
+ }
+
+ // True if this is a synchronous message.
+ bool is_sync() const {
+ return (header()->flags & SYNC_BIT) != 0;
+ }
+
+ // Set this on a reply to a synchronous message.
+ void set_reply() {
+ header()->flags |= REPLY_BIT;
+ }
+
+ bool is_reply() const {
+ return (header()->flags & REPLY_BIT) != 0;
+ }
+
+ // Set this on a reply to a synchronous message to indicate that no receiver
+ // was found.
+ void set_reply_error() {
+ header()->flags |= REPLY_ERROR_BIT;
+ }
+
+ bool is_reply_error() const {
+ return (header()->flags & REPLY_ERROR_BIT) != 0;
+ }
+
+ // Normally when a receiver gets a message and they're blocked on a
+ // synchronous message Send, they buffer a message. Setting this flag causes
+ // the receiver to be unblocked and the message to be dispatched immediately.
+ void set_unblock(bool unblock) {
+ if (unblock) {
+ header()->flags |= UNBLOCK_BIT;
+ } else {
+ header()->flags &= ~UNBLOCK_BIT;
+ }
+ }
+
+ bool should_unblock() const {
+ return (header()->flags & UNBLOCK_BIT) != 0;
+ }
+
+ // Tells the receiver that the caller is pumping messages while waiting
+ // for the result.
+ bool is_caller_pumping_messages() const {
+ return (header()->flags & PUMPING_MSGS_BIT) != 0;
+ }
+
+ uint16 type() const {
+ return header()->type;
+ }
+
+ int32 routing_id() const {
+ return header()->routing;
+ }
+
+ void set_routing_id(int32 new_id) {
+ header()->routing = new_id;
+ }
+
+ template<class T>
+ static bool Dispatch(const Message* msg, T* obj, void (T::*func)()) {
+ (obj->*func)();
+ return true;
+ }
+
+ template<class T>
+ static bool Dispatch(const Message* msg, T* obj, void (T::*func)() const) {
+ (obj->*func)();
+ return true;
+ }
+
+ template<class T>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&)) {
+ (obj->*func)(*msg);
+ return true;
+ }
+
+ template<class T>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&) const) {
+ (obj->*func)(*msg);
+ return true;
+ }
+
+ // Used for async messages with no parameters.
+ static void Log(const Message* msg, std::wstring* l) {
+ }
+
+ // Find the end of the message data that starts at range_start. Returns NULL
+ // if the entire message is not found in the given data range.
+ static const char* FindNext(const char* range_start, const char* range_end) {
+ return Pickle::FindNext(sizeof(Header), range_start, range_end);
+ }
+
+#if defined(OS_POSIX)
+ // On POSIX, a message supports reading / writing FileDescriptor objects.
+ // This is used to pass a file descriptor to the peer of an IPC channel.
+
+ // Add a descriptor to the end of the set. Returns false iff the set is full.
+ bool WriteFileDescriptor(const base::FileDescriptor& descriptor);
+ // Get a file descriptor from the message. Returns false on error.
+ // iter: a Pickle iterator to the current location in the message.
+ bool ReadFileDescriptor(void** iter, base::FileDescriptor* descriptor) const;
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ // Adds the outgoing time from Time::Now() at the end of the message and sets
+ // a bit to indicate that it's been added.
+ void set_sent_time(int64 time);
+ int64 sent_time() const;
+
+ void set_received_time(int64 time) const;
+ int64 received_time() const { return received_time_; }
+ void set_output_params(const std::wstring& op) const { output_params_ = op; }
+ const std::wstring& output_params() const { return output_params_; }
+ // The following four functions are needed so we can log sync messages with
+ // delayed replies. We stick the log data from the sent message into the
+ // reply message, so that when it's sent and we have the output parameters
+ // we can log it. As such, we set a flag on the sent message to not log it.
+ void set_sync_log_data(LogData* data) const { log_data_ = data; }
+ LogData* sync_log_data() const { return log_data_; }
+ void set_dont_log() const { dont_log_ = true; }
+ bool dont_log() const { return dont_log_; }
+#endif
+
+ protected:
+ friend class Channel;
+ friend class MessageReplyDeserializer;
+ friend class SyncMessage;
+
+ void set_sync() {
+ header()->flags |= SYNC_BIT;
+ }
+
+ // flags
+ enum {
+ PRIORITY_MASK = 0x0003,
+ SYNC_BIT = 0x0004,
+ REPLY_BIT = 0x0008,
+ REPLY_ERROR_BIT = 0x0010,
+ UNBLOCK_BIT = 0x0020,
+ PUMPING_MSGS_BIT= 0x0040,
+ HAS_SENT_TIME_BIT = 0x0080,
+ };
+
+#pragma pack(push, 2)
+ struct Header : Pickle::Header {
+ int32 routing; // ID of the view that this message is destined for
+ uint16 type; // specifies the user-defined message type
+ uint16 flags; // specifies control flags for the message
+#if defined(OS_POSIX)
+ uint32 num_fds; // the number of descriptors included with this message
+#endif
+ };
+#pragma pack(pop)
+
+ Header* header() {
+ return headerT<Header>();
+ }
+ const Header* header() const {
+ return headerT<Header>();
+ }
+
+ void InitLoggingVariables();
+
+#if defined(OS_POSIX)
+ // The set of file descriptors associated with this message.
+ scoped_refptr<FileDescriptorSet> file_descriptor_set_;
+
+ // Ensure that a FileDescriptorSet is allocated
+ void EnsureFileDescriptorSet();
+
+ FileDescriptorSet* file_descriptor_set() {
+ EnsureFileDescriptorSet();
+ return file_descriptor_set_.get();
+ }
+ const FileDescriptorSet* file_descriptor_set() const {
+ return file_descriptor_set_.get();
+ }
+#endif
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ // Used for logging.
+ mutable int64 received_time_;
+ mutable std::wstring output_params_;
+ mutable LogData* log_data_;
+ mutable bool dont_log_;
+#endif
+};
+
+//------------------------------------------------------------------------------
+
+} // namespace IPC
+
+enum SpecialRoutingIDs {
+ // indicates that we don't have a routing ID yet.
+ MSG_ROUTING_NONE = -2,
+
+ // indicates a general message not sent to a particular tab.
+ MSG_ROUTING_CONTROL = kint32max,
+};
+
+#define IPC_REPLY_ID 0xFFF0 // Special message id for replies
+#define IPC_LOGGING_ID 0xFFF1 // Special message id for logging
+
+#endif // CHROME_COMMON_IPC_MESSAGE_H__
diff --git a/chrome/common/ipc_message_macros.h b/chrome/common/ipc_message_macros.h
new file mode 100644
index 0000000..7cba911
--- /dev/null
+++ b/chrome/common/ipc_message_macros.h
@@ -0,0 +1,1152 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This header is meant to be included in multiple passes, hence no traditional
+// header guard.
+//
+// In the first pass, IPC_MESSAGE_MACROS_ENUMS should be defined, which will
+// create enums for each of the messages defined with the IPC_MESSAGE_* macros.
+//
+// In the second pass, either IPC_MESSAGE_MACROS_DEBUGSTRINGS or
+// IPC_MESSAGE_MACROS_CLASSES should be defined (if both, DEBUGSTRINGS takes
+// precedence). Only one .cc file should have DEBUGSTRINGS defined, as this
+// will create helper functions mapping message types to strings. Having
+// CLASSES defined will create classes for each of the messages defined with
+// the IPC_MESSAGE_* macros.
+//
+// "Sync" messages are just synchronous calls, the Send() call doesn't return
+// until a reply comes back. Input parameters are first (const TYPE&), and
+// To declare a sync message, use the IPC_SYNC_ macros. The numbers at the
+// end show how many input/output parameters there are (i.e. 1_2 is 1 in, 2
+// out). The caller does a Send([route id, ], in1, &out1, &out2).
+// The receiver's handler function will be
+// void OnSyncMessageName(const type1& in1, type2* out1, type3* out2)
+//
+//
+// A caller can also send a synchronous message, while the receiver can respond
+// at a later time. This is transparent from the sender's size. The receiver
+// needs to use a different handler that takes in a IPC::Message* as the output
+// type, stash the message, and when it has the data it can Send the message.
+//
+// Use the IPC_MESSAGE_HANDLER_DELAY_REPLY macro instead of IPC_MESSAGE_HANDLER
+// IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_SyncMessageName,
+// OnSyncMessageName)
+//
+// The handler function will look like:
+// void OnSyncMessageName(const type1& in1, IPC::Message* reply_msg);
+//
+// Receiver stashes the IPC::Message* pointer, and when it's ready, it does:
+// ViewHostMsg_SyncMessageName::WriteReplyParams(reply_msg, out1, out2);
+// Send(reply_msg);
+
+#include "chrome/common/ipc_message_utils.h"
+
+
+#ifndef MESSAGES_INTERNAL_FILE
+#error This file should only be included by X_messages.h, which needs to define MESSAGES_INTERNAL_FILE first.
+#endif
+
+// Trick scons and xcode into seeing the possible real dependencies since they
+// don't understand #include MESSAGES_INTERNAL_FILE. See http://crbug.com/7828
+#if 0
+#include "chrome/common/ipc_sync_message_unittest.h"
+#include "chrome/common/plugin_messages_internal.h"
+#include "chrome/common/render_messages_internal.h"
+#include "chrome/renderer/devtools_messages_internal.h"
+#include "chrome/test/automation/automation_messages_internal.h"
+#include "chrome/common/worker_messages_internal.h"
+#endif
+
+#ifndef IPC_MESSAGE_MACROS_INCLUDE_BLOCK
+#define IPC_MESSAGE_MACROS_INCLUDE_BLOCK
+
+// Multi-pass include of X_messages_internal.h. Preprocessor magic allows
+// us to use 1 header to define the enums and classes for our render messages.
+#define IPC_MESSAGE_MACROS_ENUMS
+#include MESSAGES_INTERNAL_FILE
+
+#define IPC_MESSAGE_MACROS_CLASSES
+#include MESSAGES_INTERNAL_FILE
+
+#ifdef IPC_MESSAGE_MACROS_LOG_ENABLED
+#define IPC_MESSAGE_MACROS_LOG
+#include MESSAGES_INTERNAL_FILE
+#endif
+
+#undef MESSAGES_INTERNAL_FILE
+#undef IPC_MESSAGE_MACROS_INCLUDE_BLOCK
+
+#endif
+
+
+// Undefine the macros from the previous pass (if any).
+#undef IPC_BEGIN_MESSAGES
+#undef IPC_END_MESSAGES
+#undef IPC_MESSAGE_CONTROL0
+#undef IPC_MESSAGE_CONTROL1
+#undef IPC_MESSAGE_CONTROL2
+#undef IPC_MESSAGE_CONTROL3
+#undef IPC_MESSAGE_CONTROL4
+#undef IPC_MESSAGE_CONTROL5
+#undef IPC_MESSAGE_ROUTED0
+#undef IPC_MESSAGE_ROUTED1
+#undef IPC_MESSAGE_ROUTED2
+#undef IPC_MESSAGE_ROUTED3
+#undef IPC_MESSAGE_ROUTED4
+#undef IPC_MESSAGE_ROUTED5
+#undef IPC_MESSAGE_ROUTED6
+#undef IPC_SYNC_MESSAGE_CONTROL0_0
+#undef IPC_SYNC_MESSAGE_CONTROL0_1
+#undef IPC_SYNC_MESSAGE_CONTROL0_2
+#undef IPC_SYNC_MESSAGE_CONTROL0_3
+#undef IPC_SYNC_MESSAGE_CONTROL1_0
+#undef IPC_SYNC_MESSAGE_CONTROL1_1
+#undef IPC_SYNC_MESSAGE_CONTROL1_2
+#undef IPC_SYNC_MESSAGE_CONTROL1_3
+#undef IPC_SYNC_MESSAGE_CONTROL2_0
+#undef IPC_SYNC_MESSAGE_CONTROL2_1
+#undef IPC_SYNC_MESSAGE_CONTROL2_2
+#undef IPC_SYNC_MESSAGE_CONTROL2_3
+#undef IPC_SYNC_MESSAGE_CONTROL3_1
+#undef IPC_SYNC_MESSAGE_CONTROL3_2
+#undef IPC_SYNC_MESSAGE_CONTROL3_3
+#undef IPC_SYNC_MESSAGE_CONTROL4_1
+#undef IPC_SYNC_MESSAGE_CONTROL4_2
+#undef IPC_SYNC_MESSAGE_ROUTED0_0
+#undef IPC_SYNC_MESSAGE_ROUTED0_1
+#undef IPC_SYNC_MESSAGE_ROUTED0_2
+#undef IPC_SYNC_MESSAGE_ROUTED0_3
+#undef IPC_SYNC_MESSAGE_ROUTED1_0
+#undef IPC_SYNC_MESSAGE_ROUTED1_1
+#undef IPC_SYNC_MESSAGE_ROUTED1_2
+#undef IPC_SYNC_MESSAGE_ROUTED1_3
+#undef IPC_SYNC_MESSAGE_ROUTED1_4
+#undef IPC_SYNC_MESSAGE_ROUTED2_0
+#undef IPC_SYNC_MESSAGE_ROUTED2_1
+#undef IPC_SYNC_MESSAGE_ROUTED2_2
+#undef IPC_SYNC_MESSAGE_ROUTED2_3
+#undef IPC_SYNC_MESSAGE_ROUTED3_0
+#undef IPC_SYNC_MESSAGE_ROUTED3_1
+#undef IPC_SYNC_MESSAGE_ROUTED3_2
+#undef IPC_SYNC_MESSAGE_ROUTED3_3
+#undef IPC_SYNC_MESSAGE_ROUTED4_0
+#undef IPC_SYNC_MESSAGE_ROUTED4_1
+#undef IPC_SYNC_MESSAGE_ROUTED4_2
+
+#if defined(IPC_MESSAGE_MACROS_ENUMS)
+#undef IPC_MESSAGE_MACROS_ENUMS
+
+// TODO(jabdelmalek): we're using the lowest 12 bits of type for the message
+// id, and the highest 4 bits for the channel type. This constrains us to
+// 16 channel types (currently using 8) and 4K messages per type. Should
+// really make type be 32 bits, but then we break automation with older Chrome
+// builds..
+// Do label##PreStart so that automation messages keep the same id as before.
+#define IPC_BEGIN_MESSAGES(label) \
+ enum label##MsgType { \
+ label##Start = label##MsgStart << 12, \
+ label##PreStart = (label##MsgStart << 12) - 1,
+
+#define IPC_END_MESSAGES(label) \
+ label##End \
+ };
+
+#define IPC_MESSAGE_CONTROL0(msg_class) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL1(msg_class, type1) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED0(msg_class) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED1(msg_class, type1) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \
+ msg_class##__ID,
+
+#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, type6) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+// Message crackers and handlers.
+// Prefer to use the IPC_BEGIN_MESSAGE_MAP_EX to the older macros since they
+// allow you to detect when a message could not be de-serialized. Usage:
+//
+// void MyClass::OnMessageReceived(const IPC::Message& msg) {
+// bool msg_is_good = false;
+// IPC_BEGIN_MESSAGE_MAP_EX(MyClass, msg, msg_is_good)
+// IPC_MESSAGE_HANDLER(MsgClassOne, OnMsgClassOne)
+// ...more handlers here ...
+// IPC_MESSAGE_HANDLER(MsgClassTen, OnMsgClassTen)
+// IPC_END_MESSAGE_MAP_EX()
+// if (!msg_is_good) {
+// // Signal error here or terminate offending process.
+// }
+// }
+
+#define IPC_DEFINE_MESSAGE_MAP(class_name) \
+void class_name::OnMessageReceived(const IPC::Message& msg) \
+ IPC_BEGIN_MESSAGE_MAP(class_name, msg)
+
+#define IPC_BEGIN_MESSAGE_MAP_EX(class_name, msg, msg_is_ok) \
+ { \
+ typedef class_name _IpcMessageHandlerClass; \
+ const IPC::Message& ipc_message__ = msg; \
+ bool& msg_is_ok__ = msg_is_ok; \
+ switch (ipc_message__.type()) { \
+
+#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \
+ { \
+ typedef class_name _IpcMessageHandlerClass; \
+ const IPC::Message& ipc_message__ = msg; \
+ bool msg_is_ok__ = true; \
+ switch (ipc_message__.type()) { \
+
+#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func) \
+ case msg_class::ID: \
+ msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, &member_func); \
+ break;
+
+#define IPC_MESSAGE_HANDLER(msg_class, member_func) \
+ IPC_MESSAGE_FORWARD(msg_class, this, _IpcMessageHandlerClass::member_func)
+
+#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \
+ case msg_class::ID: \
+ msg_class::DispatchDelayReply(&ipc_message__, obj, &member_func); \
+ break;
+
+#define IPC_MESSAGE_HANDLER_DELAY_REPLY(msg_class, member_func) \
+ IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, this, _IpcMessageHandlerClass::member_func)
+
+#define IPC_MESSAGE_HANDLER_GENERIC(msg_class, code) \
+ case msg_class::ID: \
+ code; \
+ break;
+
+#define IPC_REPLY_HANDLER(func) \
+ case IPC_REPLY_ID: \
+ func(ipc_message__); \
+ break;
+
+
+#define IPC_MESSAGE_UNHANDLED(code) \
+ default: \
+ code; \
+ break;
+
+#define IPC_MESSAGE_UNHANDLED_ERROR() \
+ IPC_MESSAGE_UNHANDLED(NOTREACHED() << \
+ "Invalid message with type = " << \
+ ipc_message__.type())
+
+#define IPC_END_MESSAGE_MAP() \
+ DCHECK(msg_is_ok__); \
+ } \
+}
+
+#define IPC_END_MESSAGE_MAP_EX() \
+ } \
+}
+
+#elif defined(IPC_MESSAGE_MACROS_LOG)
+#undef IPC_MESSAGE_MACROS_LOG
+
+#ifndef IPC_LOG_TABLE_CREATED
+#define IPC_LOG_TABLE_CREATED
+typedef void (*LogFunction)(uint16 type,
+ std::wstring* name,
+ const IPC::Message* msg,
+ std::wstring* params);
+
+LogFunction g_log_function_mapping[LastMsgIndex];
+#endif
+
+
+#define IPC_BEGIN_MESSAGES(label) \
+ void label##MsgLog(uint16 type, std::wstring* name, const IPC::Message* msg, std::wstring* params) { \
+ switch (type) {
+
+#define IPC_END_MESSAGES(label) \
+ default: \
+ if (name) \
+ *name = L"[UNKNOWN " L ## #label L" MSG"; \
+ } \
+ } \
+ class LoggerRegisterHelper##label { \
+ public: \
+ LoggerRegisterHelper##label() { \
+ g_log_function_mapping[label##MsgStart] = label##MsgLog; \
+ } \
+ }; \
+ LoggerRegisterHelper##label g_LoggerRegisterHelper##label;
+
+#define IPC_MESSAGE_LOG(msg_class) \
+ case msg_class##__ID: \
+ if (name) \
+ *name = L ## #msg_class; \
+ if (msg && params) \
+ msg_class::Log(msg, params); \
+ break;
+
+#define IPC_MESSAGE_CONTROL0(msg_class) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL1(msg_class, type1) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED0(msg_class) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED1(msg_class, type1) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, type6) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#elif defined(IPC_MESSAGE_MACROS_CLASSES)
+#undef IPC_MESSAGE_MACROS_CLASSES
+
+#define IPC_BEGIN_MESSAGES(label)
+#define IPC_END_MESSAGES(label)
+
+#define IPC_MESSAGE_CONTROL0(msg_class) \
+ class msg_class : public IPC::Message { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class() \
+ : IPC::Message(MSG_ROUTING_CONTROL, \
+ ID, \
+ PRIORITY_NORMAL) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL1(msg_class, type1) \
+ class msg_class : public IPC::MessageWithTuple<type1> { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1) \
+ : IPC::MessageWithTuple<type1>(MSG_ROUTING_CONTROL, \
+ ID, \
+ arg1) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \
+ class msg_class : public IPC::MessageWithTuple< Tuple2<type1, type2> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1, const type2& arg2) \
+ : IPC::MessageWithTuple< Tuple2<type1, type2> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(arg1, arg2)) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple3<type1, type2, type3> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1, const type2& arg2, const type3& arg3) \
+ : IPC::MessageWithTuple< Tuple3<type1, type2, type3> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(arg1, arg2, arg3)) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1, const type2& arg2, const type3& arg3, \
+ const type4& arg4) \
+ : IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(arg1, arg2, arg3, arg4)) {} \
+ };
+
+#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1& arg1, const type2& arg2, \
+ const type3& arg3, const type4& arg4, const type5& arg5) \
+ : IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(arg1, arg2, arg3, arg4, arg5)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED0(msg_class) \
+ class msg_class : public IPC::Message { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id) \
+ : IPC::Message(routing_id, ID, PRIORITY_NORMAL) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED1(msg_class, type1) \
+ class msg_class : public IPC::MessageWithTuple<type1> { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1) \
+ : IPC::MessageWithTuple<type1>(routing_id, ID, arg1) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \
+ class msg_class : public IPC::MessageWithTuple< Tuple2<type1, type2> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2) \
+ : IPC::MessageWithTuple< Tuple2<type1, type2> >( \
+ routing_id, ID, MakeTuple(arg1, arg2)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple3<type1, type2, type3> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \
+ const type3& arg3) \
+ : IPC::MessageWithTuple< Tuple3<type1, type2, type3> >( \
+ routing_id, ID, MakeTuple(arg1, arg2, arg3)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \
+ const type3& arg3, const type4& arg4) \
+ : IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> >( \
+ routing_id, ID, MakeTuple(arg1, arg2, arg3, arg4)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \
+ const type3& arg3, const type4& arg4, const type5& arg5) \
+ : IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> >( \
+ routing_id, ID, MakeTuple(arg1, arg2, arg3, arg4, arg5)) {} \
+ };
+
+#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, \
+ type6) \
+ class msg_class : \
+ public IPC::MessageWithTuple< Tuple6<type1, type2, type3, type4, type5, \
+ type6> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \
+ const type3& arg3, const type4& arg4, const type5& arg5, \
+ const type6& arg6) \
+ : IPC::MessageWithTuple< Tuple6<type1, type2, type3, type4, type5, \
+ type6> >( \
+ routing_id, ID, MakeTuple(arg1, arg2, arg3, arg4, arg5, arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \
+ class msg_class : public IPC::MessageWithReply<Tuple0, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class() \
+ : IPC::MessageWithReply<Tuple0, Tuple0 >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \
+ class msg_class : public IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(type1_out* arg1) \
+ : IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(), MakeRefTuple(*arg1)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(type1_out* arg1, type2_out* arg2) \
+ : IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> >( \
+ MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(), MakeRefTuple(*arg1, *arg2)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple0, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(type1_out* arg1, type2_out* arg2, type3_out* arg3) \
+ : IPC::MessageWithReply<Tuple0, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(), MakeRefTuple(*arg1, *arg2, *arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1) \
+ : IPC::MessageWithReply<type1_in, Tuple0 >( \
+ MSG_ROUTING_CONTROL, ID, \
+ arg1, MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, type1_out* arg2) \
+ : IPC::MessageWithReply<type1_in, Tuple1<type1_out&> >( \
+ MSG_ROUTING_CONTROL, ID, \
+ arg1, MakeRefTuple(*arg2)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, type1_out* arg2, type2_out* arg3) \
+ : IPC::MessageWithReply<type1_in, Tuple2<type1_out&, type2_out&> >( \
+ MSG_ROUTING_CONTROL, ID, \
+ arg1, MakeRefTuple(*arg2, *arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4) \
+ : IPC::MessageWithReply<type1_in, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ arg1, MakeRefTuple(*arg2, *arg3, *arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(arg1, arg2), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> >( \
+ MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(arg1, arg2), MakeRefTuple(*arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4, type3_out* arg5) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple1<type1_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5, type3_out* arg6) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \
+ ID, \
+ MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5, *arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg6) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple1<type1_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg5, type2_out* arg6) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \
+ MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \
+ class msg_class : public IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, type1_out* arg1) \
+ : IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> >( \
+ routing_id, ID, \
+ MakeTuple(), MakeRefTuple(*arg1)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \
+ class msg_class : public IPC::MessageWithReply<Tuple0, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id) \
+ : IPC::MessageWithReply<Tuple0, Tuple0 >( \
+ routing_id, ID, \
+ MakeTuple(), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, type1_out* arg1, type2_out* arg2) \
+ : IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> >( \
+ routing_id, ID, \
+ MakeTuple(), MakeRefTuple(*arg1, *arg2)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple0, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, type1_out* arg1, type2_out* arg2, type3_out* arg3) \
+ : IPC::MessageWithReply<Tuple0, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeTuple(), MakeRefTuple(*arg1, *arg2, *arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1) \
+ : IPC::MessageWithReply<type1_in, Tuple0 >( \
+ routing_id, ID, \
+ arg1, MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, type1_out* arg2) \
+ : IPC::MessageWithReply<type1_in, Tuple1<type1_out&> >( \
+ routing_id, ID, \
+ arg1, MakeRefTuple(*arg2)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3) \
+ : IPC::MessageWithReply<type1_in, Tuple2<type1_out&, type2_out&> >( \
+ routing_id, ID, \
+ arg1, MakeRefTuple(*arg2, *arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4) \
+ : IPC::MessageWithReply<type1_in, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ arg1, MakeRefTuple(*arg2, *arg3, *arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<type1_in, \
+ Tuple4<type1_out&, type2_out&, type3_out&, type4_out&> >{ \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4, type4_out* arg5) \
+ : IPC::MessageWithReply<type1_in, \
+ Tuple4<type1_out&, type2_out&, type3_out&, type4_out&> >(routing_id, ID, \
+ arg1, MakeRefTuple(*arg2, *arg3, *arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 >( \
+ routing_id, ID, \
+ MakeTuple(arg1, arg2), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> >( \
+ routing_id, ID, \
+ MakeTuple(arg1, arg2), MakeRefTuple(*arg3)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple2<type1_out&, type2_out&> >(routing_id, ID, \
+ MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4, type3_out* arg5) \
+ : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, Tuple0>( \
+ routing_id, ID, \
+ MakeTuple(arg1, arg2, arg3), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple1<type1_out&> >(routing_id, ID, \
+ MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple2<type1_out&, type2_out&> >(routing_id, ID, \
+ MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5, type3_out* arg6) \
+ : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5, *arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple0 >(routing_id, ID, \
+ MakeTuple(arg1, arg2, arg3, arg4), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg6) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple1<type1_out&> >(routing_id, ID, \
+ MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg5, type2_out* arg6) \
+ : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \
+ Tuple2<type1_out&, type2_out&> >(routing_id, ID, \
+ MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6)) {} \
+ };
+
+#endif // #if defined()
diff --git a/chrome/common/gurl_serialisation_unittest.cc b/chrome/common/ipc_message_unittest.cc
index 9fdd59d..643626e 100644
--- a/chrome/common/gurl_serialisation_unittest.cc
+++ b/chrome/common/ipc_message_unittest.cc
@@ -4,13 +4,13 @@
#include <string.h>
+#include "chrome/common/ipc_message.h"
+#include "chrome/common/ipc_message_utils.h"
#include "googleurl/src/gurl.h"
-#include "chrome/common/common_message_utils.h"
-#include "ipc/ipc_message.h"
#include "testing/gtest/include/gtest/gtest.h"
// Tests that serialize/deserialize correctly understand each other
-TEST(GURLSerialisation, Serialize) {
+TEST(IPCMessageTest, Serialize) {
const char* serialize_cases[] = {
"http://www.google.com/",
"http://user:pass@host.com:888/foo;bar?baz#nop",
diff --git a/chrome/common/common_message_utils.cc b/chrome/common/ipc_message_utils.cc
index e12edae..36e8052 100644
--- a/chrome/common/common_message_utils.cc
+++ b/chrome/common/ipc_message_utils.cc
@@ -1,9 +1,10 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/common/common_message_utils.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "base/gfx/rect.h"
#include "googleurl/src/gurl.h"
#include "SkBitmap.h"
#include "webkit/glue/dom_operations.h"
@@ -44,6 +45,7 @@ struct SkBitmap_Data {
} // namespace
+
void ParamTraits<SkBitmap>::Write(Message* m, const SkBitmap& p) {
size_t fixed_size = sizeof(SkBitmap_Data);
SkBitmap_Data bmp_data;
@@ -84,6 +86,7 @@ void ParamTraits<SkBitmap>::Log(const SkBitmap& p, std::wstring* l) {
l->append(StringPrintf(L"<SkBitmap>"));
}
+
void ParamTraits<GURL>::Write(Message* m, const GURL& p) {
m->WriteString(p.possibly_invalid_spec());
// TODO(brettw) bug 684583: Add encoding for query params.
@@ -103,6 +106,74 @@ void ParamTraits<GURL>::Log(const GURL& p, std::wstring* l) {
l->append(UTF8ToWide(p.spec()));
}
+
+void ParamTraits<gfx::Point>::Write(Message* m, const gfx::Point& p) {
+ m->WriteInt(p.x());
+ m->WriteInt(p.y());
+}
+
+bool ParamTraits<gfx::Point>::Read(const Message* m, void** iter,
+ gfx::Point* r) {
+ int x, y;
+ if (!m->ReadInt(iter, &x) ||
+ !m->ReadInt(iter, &y))
+ return false;
+ r->set_x(x);
+ r->set_y(y);
+ return true;
+}
+
+void ParamTraits<gfx::Point>::Log(const gfx::Point& p, std::wstring* l) {
+ l->append(StringPrintf(L"(%d, %d)", p.x(), p.y()));
+}
+
+
+void ParamTraits<gfx::Rect>::Write(Message* m, const gfx::Rect& p) {
+ m->WriteInt(p.x());
+ m->WriteInt(p.y());
+ m->WriteInt(p.width());
+ m->WriteInt(p.height());
+}
+
+bool ParamTraits<gfx::Rect>::Read(const Message* m, void** iter, gfx::Rect* r) {
+ int x, y, w, h;
+ if (!m->ReadInt(iter, &x) ||
+ !m->ReadInt(iter, &y) ||
+ !m->ReadInt(iter, &w) ||
+ !m->ReadInt(iter, &h))
+ return false;
+ r->set_x(x);
+ r->set_y(y);
+ r->set_width(w);
+ r->set_height(h);
+ return true;
+}
+
+void ParamTraits<gfx::Rect>::Log(const gfx::Rect& p, std::wstring* l) {
+ l->append(StringPrintf(L"(%d, %d, %d, %d)", p.x(), p.y(),
+ p.width(), p.height()));
+}
+
+
+void ParamTraits<gfx::Size>::Write(Message* m, const gfx::Size& p) {
+ m->WriteInt(p.width());
+ m->WriteInt(p.height());
+}
+
+bool ParamTraits<gfx::Size>::Read(const Message* m, void** iter, gfx::Size* r) {
+ int w, h;
+ if (!m->ReadInt(iter, &w) ||
+ !m->ReadInt(iter, &h))
+ return false;
+ r->set_width(w);
+ r->set_height(h);
+ return true;
+}
+
+void ParamTraits<gfx::Size>::Log(const gfx::Size& p, std::wstring* l) {
+ l->append(StringPrintf(L"(%d, %d)", p.width(), p.height()));
+}
+
void ParamTraits<webkit_glue::WebApplicationInfo>::Write(
Message* m, const webkit_glue::WebApplicationInfo& p) {
WriteParam(m, p.title);
diff --git a/chrome/common/ipc_message_utils.h b/chrome/common/ipc_message_utils.h
new file mode 100644
index 0000000..6e82d40
--- /dev/null
+++ b/chrome/common/ipc_message_utils.h
@@ -0,0 +1,1381 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_MESSAGE_UTILS_H_
+#define CHROME_COMMON_IPC_MESSAGE_UTILS_H_
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "base/file_path.h"
+#include "base/string_util.h"
+#include "base/string16.h"
+#include "base/tuple.h"
+#if defined(OS_POSIX)
+#include "chrome/common/file_descriptor_set_posix.h"
+#endif
+#include "chrome/common/ipc_sync_message.h"
+#include "chrome/common/thumbnail_score.h"
+#include "chrome/common/transport_dib.h"
+#include "webkit/glue/webcursor.h"
+#include "webkit/glue/window_open_disposition.h"
+
+// Forward declarations.
+class GURL;
+class SkBitmap;
+
+namespace gfx {
+class Point;
+class Rect;
+class Size;
+} // namespace gfx
+
+namespace webkit_glue {
+struct WebApplicationInfo;
+} // namespace webkit_glue
+
+// Used by IPC_BEGIN_MESSAGES so that each message class starts from a unique
+// base. Messages have unique IDs across channels in order for the IPC logging
+// code to figure out the message class from its ID.
+enum IPCMessageStart {
+ // By using a start value of 0 for automation messages, we keep backward
+ // compatibility with old builds.
+ AutomationMsgStart = 0,
+ ViewMsgStart,
+ ViewHostMsgStart,
+ PluginProcessMsgStart,
+ PluginProcessHostMsgStart,
+ PluginMsgStart,
+ PluginHostMsgStart,
+ NPObjectMsgStart,
+ TestMsgStart,
+ DevToolsAgentMsgStart,
+ DevToolsClientMsgStart,
+ WorkerProcessMsgStart,
+ WorkerProcessHostMsgStart,
+ WorkerMsgStart,
+ WorkerHostMsgStart,
+ // NOTE: When you add a new message class, also update
+ // IPCStatusView::IPCStatusView to ensure logging works.
+ // NOTE: this enum is used by IPC_MESSAGE_MACRO to generate a unique message
+ // id. Only 4 bits are used for the message type, so if this enum needs more
+ // than 16 entries, that code needs to be updated.
+ LastMsgIndex
+};
+
+COMPILE_ASSERT(LastMsgIndex <= 16, need_to_update_IPC_MESSAGE_MACRO);
+
+namespace IPC {
+
+//-----------------------------------------------------------------------------
+// An iterator class for reading the fields contained within a Message.
+
+class MessageIterator {
+ public:
+ explicit MessageIterator(const Message& m) : msg_(m), iter_(NULL) {
+ }
+ int NextInt() const {
+ int val;
+ if (!msg_.ReadInt(&iter_, &val))
+ NOTREACHED();
+ return val;
+ }
+ intptr_t NextIntPtr() const {
+ intptr_t val;
+ if (!msg_.ReadIntPtr(&iter_, &val))
+ NOTREACHED();
+ return val;
+ }
+ const std::string NextString() const {
+ std::string val;
+ if (!msg_.ReadString(&iter_, &val))
+ NOTREACHED();
+ return val;
+ }
+ const std::wstring NextWString() const {
+ std::wstring val;
+ if (!msg_.ReadWString(&iter_, &val))
+ NOTREACHED();
+ return val;
+ }
+ const void NextData(const char** data, int* length) const {
+ if (!msg_.ReadData(&iter_, data, length)) {
+ NOTREACHED();
+ }
+ }
+ private:
+ const Message& msg_;
+ mutable void* iter_;
+};
+
+//-----------------------------------------------------------------------------
+// ParamTraits specializations, etc.
+
+template <class P> struct ParamTraits {};
+
+template <class P>
+static inline void WriteParam(Message* m, const P& p) {
+ ParamTraits<P>::Write(m, p);
+}
+
+template <class P>
+static inline bool ReadParam(const Message* m, void** iter, P* p) {
+ return ParamTraits<P>::Read(m, iter, p);
+}
+
+template <class P>
+static inline void LogParam(const P& p, std::wstring* l) {
+ ParamTraits<P>::Log(p, l);
+}
+
+template <>
+struct ParamTraits<bool> {
+ typedef bool param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteBool(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadBool(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(p ? L"true" : L"false");
+ }
+};
+
+template <>
+struct ParamTraits<int> {
+ typedef int param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadInt(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%d", p));
+ }
+};
+
+template <>
+struct ParamTraits<long> {
+ typedef long param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteLong(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadLong(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%l", p));
+ }
+};
+
+template <>
+struct ParamTraits<size_t> {
+ typedef size_t param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteSize(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadSize(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%u", p));
+ }
+};
+
+#if defined(OS_MACOSX)
+// On Linux size_t & uint32 can be the same type.
+// TODO(playmobil): Fix compilation if this is not the case.
+template <>
+struct ParamTraits<uint32> {
+ typedef uint32 param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteUInt32(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadUInt32(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%u", p));
+ }
+};
+#endif // defined(OS_MACOSX)
+
+template <>
+struct ParamTraits<int64> {
+ typedef int64 param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt64(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadInt64(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%I64d", p));
+ }
+};
+
+template <>
+struct ParamTraits<uint64> {
+ typedef uint64 param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt64(static_cast<int64>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadInt64(iter, reinterpret_cast<int64*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%I64u", p));
+ }
+};
+
+template <>
+struct ParamTraits<double> {
+ typedef double param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(param_type)) {
+ memcpy(r, data, sizeof(param_type));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"e", p));
+ }
+};
+
+template <>
+struct ParamTraits<wchar_t> {
+ typedef wchar_t param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(param_type)) {
+ memcpy(r, data, sizeof(param_type));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%lc", p));
+ }
+};
+
+template <>
+struct ParamTraits<base::Time> {
+ typedef base::Time param_type;
+ static void Write(Message* m, const param_type& p) {
+ ParamTraits<int64>::Write(m, p.ToInternalValue());
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int64 value;
+ if (!ParamTraits<int64>::Read(m, iter, &value))
+ return false;
+ *r = base::Time::FromInternalValue(value);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ ParamTraits<int64>::Log(p.ToInternalValue(), l);
+ }
+};
+
+#if defined(OS_WIN)
+template <>
+struct ParamTraits<LOGFONT> {
+ typedef LOGFONT param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(LOGFONT));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(LOGFONT)) {
+ memcpy(r, data, sizeof(LOGFONT));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"<LOGFONT>"));
+ }
+};
+
+template <>
+struct ParamTraits<MSG> {
+ typedef MSG param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(MSG));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(MSG)) {
+ memcpy(r, data, sizeof(MSG));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+};
+#endif // defined(OS_WIN)
+
+template <>
+struct ParamTraits<SkBitmap> {
+ typedef SkBitmap param_type;
+ static void Write(Message* m, const param_type& p);
+
+ // Note: This function expects parameter |r| to be of type &SkBitmap since
+ // r->SetConfig() and r->SetPixels() are called.
+ static bool Read(const Message* m, void** iter, param_type* r);
+
+ static void Log(const param_type& p, std::wstring* l);
+};
+
+template <>
+struct ParamTraits<std::string> {
+ typedef std::string param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteString(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadString(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(UTF8ToWide(p));
+ }
+};
+
+template <>
+struct ParamTraits<std::vector<unsigned char> > {
+ typedef std::vector<unsigned char> param_type;
+ static void Write(Message* m, const param_type& p) {
+ if (p.size() == 0) {
+ m->WriteData(NULL, 0);
+ } else {
+ m->WriteData(reinterpret_cast<const char*>(&p.front()),
+ static_cast<int>(p.size()));
+ }
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ if (!m->ReadData(iter, &data, &data_size) || data_size < 0)
+ return false;
+ r->resize(data_size);
+ if (data_size)
+ memcpy(&r->front(), data, data_size);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ for (size_t i = 0; i < p.size(); ++i)
+ l->push_back(p[i]);
+ }
+};
+
+template <>
+struct ParamTraits<std::vector<char> > {
+ typedef std::vector<char> param_type;
+ static void Write(Message* m, const param_type& p) {
+ if (p.size() == 0) {
+ m->WriteData(NULL, 0);
+ } else {
+ m->WriteData(&p.front(), static_cast<int>(p.size()));
+ }
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ if (!m->ReadData(iter, &data, &data_size) || data_size < 0)
+ return false;
+ r->resize(data_size);
+ if (data_size)
+ memcpy(&r->front(), data, data_size);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ for (size_t i = 0; i < p.size(); ++i)
+ l->push_back(p[i]);
+ }
+};
+
+template <class P>
+struct ParamTraits<std::vector<P> > {
+ typedef std::vector<P> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, static_cast<int>(p.size()));
+ for (size_t i = 0; i < p.size(); i++)
+ WriteParam(m, p[i]);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int size;
+ if (!m->ReadLength(iter, &size))
+ return false;
+ // Resizing beforehand is not safe, see BUG 1006367 for details.
+ if (m->IteratorHasRoomFor(*iter, size * sizeof(P))) {
+ r->resize(size);
+ for (int i = 0; i < size; i++) {
+ if (!ReadParam(m, iter, &(*r)[i]))
+ return false;
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ P element;
+ if (!ReadParam(m, iter, &element))
+ return false;
+ r->push_back(element);
+ }
+ }
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ for (size_t i = 0; i < p.size(); ++i) {
+ if (i != 0)
+ l->append(L" ");
+
+ LogParam((p[i]), l);
+ }
+ }
+};
+
+template <class K, class V>
+struct ParamTraits<std::map<K, V> > {
+ typedef std::map<K, V> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, static_cast<int>(p.size()));
+ typename param_type::const_iterator iter;
+ for (iter = p.begin(); iter != p.end(); ++iter) {
+ WriteParam(m, iter->first);
+ WriteParam(m, iter->second);
+ }
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int size;
+ if (!ReadParam(m, iter, &size) || size < 0)
+ return false;
+ for (int i = 0; i < size; ++i) {
+ K k;
+ if (!ReadParam(m, iter, &k))
+ return false;
+ V& value = (*r)[k];
+ if (!ReadParam(m, iter, &value))
+ return false;
+ }
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(L"<std::map>");
+ }
+};
+
+
+template <>
+struct ParamTraits<std::wstring> {
+ typedef std::wstring param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteWString(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadWString(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(p);
+ }
+};
+
+// If WCHAR_T_IS_UTF16 is defined, then string16 is a std::wstring so we don't
+// need this trait.
+#if !defined(WCHAR_T_IS_UTF16)
+template <>
+struct ParamTraits<string16> {
+ typedef string16 param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteString16(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return m->ReadString16(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(UTF16ToWide(p));
+ }
+};
+#endif
+
+template <>
+struct ParamTraits<GURL> {
+ typedef GURL 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::wstring* l);
+};
+
+// and, a few more useful types...
+#if defined(OS_WIN)
+template <>
+struct ParamTraits<HANDLE> {
+ typedef HANDLE param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(intptr_t));
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraits<HCURSOR> {
+ typedef HCURSOR param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(intptr_t));
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraits<HWND> {
+ typedef HWND param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(intptr_t));
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraits<HRGN> {
+ typedef HRGN param_type;
+ static void Write(Message* m, const param_type& p) {
+ int data_size = GetRegionData(p, 0, NULL);
+ if (data_size) {
+ char* bytes = new char[data_size];
+ GetRegionData(p, data_size, reinterpret_cast<LPRGNDATA>(bytes));
+ m->WriteData(reinterpret_cast<const char*>(bytes), data_size);
+ delete [] bytes;
+ } else {
+ m->WriteData(NULL, 0);
+ }
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ bool res = FALSE;
+ const char *data;
+ int data_size = 0;
+ res = m->ReadData(iter, &data, &data_size);
+ if (data_size) {
+ *r = ExtCreateRegion(NULL, data_size,
+ reinterpret_cast<CONST RGNDATA*>(data));
+ } else {
+ res = TRUE;
+ *r = CreateRectRgn(0, 0, 0, 0);
+ }
+ return res;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"0x%X", p));
+ }
+};
+
+template <>
+struct ParamTraits<HACCEL> {
+ typedef HACCEL param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ DCHECK_EQ(sizeof(param_type), sizeof(intptr_t));
+ return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r));
+ }
+};
+
+template <>
+struct ParamTraits<POINT> {
+ typedef POINT param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt(p.x);
+ m->WriteInt(p.y);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int x, y;
+ if (!m->ReadInt(iter, &x) || !m->ReadInt(iter, &y))
+ return false;
+ r->x = x;
+ r->y = y;
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"(%d, %d)", p.x, p.y));
+ }
+};
+#endif // defined(OS_WIN)
+
+template <>
+struct ParamTraits<FilePath> {
+ typedef FilePath param_type;
+ static void Write(Message* m, const param_type& p) {
+ ParamTraits<FilePath::StringType>::Write(m, p.value());
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ FilePath::StringType value;
+ if (!ParamTraits<FilePath::StringType>::Read(m, iter, &value))
+ return false;
+ *r = FilePath(value);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ ParamTraits<FilePath::StringType>::Log(p.value(), l);
+ }
+};
+
+template <>
+struct ParamTraits<gfx::Point> {
+ typedef gfx::Point param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::wstring* l);
+};
+
+template <>
+struct ParamTraits<gfx::Rect> {
+ typedef gfx::Rect param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::wstring* l);
+};
+
+template <>
+struct ParamTraits<gfx::Size> {
+ typedef gfx::Size param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::wstring* l);
+};
+
+#if defined(OS_POSIX)
+// FileDescriptors may be serialised over IPC channels on POSIX. On the
+// receiving side, the FileDescriptor is a valid duplicate of the file
+// descriptor which was transmitted: *it is not just a copy of the integer like
+// HANDLEs on Windows*. The only exception is if the file descriptor is < 0. In
+// this case, the receiving end will see a value of -1. *Zero is a valid file
+// descriptor*.
+//
+// The received file descriptor will have the |auto_close| flag set to true. The
+// code which handles the message is responsible for taking ownership of it.
+// File descriptors are OS resources and must be closed when no longer needed.
+//
+// When sending a file descriptor, the file descriptor must be valid at the time
+// of transmission. Since transmission is not synchronous, one should consider
+// dup()ing any file descriptors to be transmitted and setting the |auto_close|
+// flag, which causes the file descriptor to be closed after writing.
+template<>
+struct ParamTraits<base::FileDescriptor> {
+ typedef base::FileDescriptor param_type;
+ static void Write(Message* m, const param_type& p) {
+ const bool valid = p.fd >= 0;
+ WriteParam(m, valid);
+
+ if (valid) {
+ if (!m->WriteFileDescriptor(p))
+ NOTREACHED();
+ }
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ bool valid;
+ if (!ReadParam(m, iter, &valid))
+ return false;
+
+ if (!valid) {
+ r->fd = -1;
+ r->auto_close = false;
+ return true;
+ }
+
+ return m->ReadFileDescriptor(iter, r);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ if (p.auto_close) {
+ l->append(StringPrintf(L"FD(%d auto-close)", p.fd));
+ } else {
+ l->append(StringPrintf(L"FD(%d)", p.fd));
+ }
+ }
+};
+#endif // defined(OS_POSIX)
+
+template<>
+struct ParamTraits<ThumbnailScore> {
+ typedef ThumbnailScore param_type;
+ static void Write(Message* m, const param_type& p) {
+ IPC::ParamTraits<double>::Write(m, p.boring_score);
+ IPC::ParamTraits<bool>::Write(m, p.good_clipping);
+ IPC::ParamTraits<bool>::Write(m, p.at_top);
+ IPC::ParamTraits<base::Time>::Write(m, p.time_at_snapshot);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ double boring_score;
+ bool good_clipping, at_top;
+ base::Time time_at_snapshot;
+ if (!IPC::ParamTraits<double>::Read(m, iter, &boring_score) ||
+ !IPC::ParamTraits<bool>::Read(m, iter, &good_clipping) ||
+ !IPC::ParamTraits<bool>::Read(m, iter, &at_top) ||
+ !IPC::ParamTraits<base::Time>::Read(m, iter, &time_at_snapshot))
+ return false;
+
+ r->boring_score = boring_score;
+ r->good_clipping = good_clipping;
+ r->at_top = at_top;
+ r->time_at_snapshot = time_at_snapshot;
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"(%f, %d, %d)",
+ p.boring_score, p.good_clipping, p.at_top));
+ }
+};
+
+template <>
+struct ParamTraits<WindowOpenDisposition> {
+ typedef WindowOpenDisposition 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) {
+ int temp;
+ bool res = m->ReadInt(iter, &temp);
+ *r = static_cast<WindowOpenDisposition>(temp);
+ return res;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(StringPrintf(L"%d", p));
+ }
+};
+
+#if defined(OS_WIN)
+template <>
+struct ParamTraits<XFORM> {
+ typedef XFORM param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteData(reinterpret_cast<const char*>(&p), sizeof(XFORM));
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ const char *data;
+ int data_size = 0;
+ bool result = m->ReadData(iter, &data, &data_size);
+ if (result && data_size == sizeof(XFORM)) {
+ memcpy(r, data, sizeof(XFORM));
+ } else {
+ result = false;
+ NOTREACHED();
+ }
+
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(L"<XFORM>");
+ }
+};
+#endif // defined(OS_WIN)
+
+template <>
+struct ParamTraits<WebCursor> {
+ typedef WebCursor param_type;
+ static void Write(Message* m, const param_type& p) {
+ p.Serialize(m);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return r->Deserialize(m, iter);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(L"<WebCursor>");
+ }
+};
+
+struct LogData {
+ std::wstring channel;
+ uint16 type;
+ std::wstring flags;
+ int64 sent; // Time that the message was sent (i.e. at Send()).
+ int64 receive; // Time before it was dispatched (i.e. before calling
+ // OnMessageReceived).
+ int64 dispatch; // Time after it was dispatched (i.e. after calling
+ // OnMessageReceived).
+ std::wstring message_name;
+ std::wstring params;
+};
+
+template <>
+struct ParamTraits<LogData> {
+ typedef LogData param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.channel);
+ WriteParam(m, static_cast<int>(p.type));
+ WriteParam(m, p.flags);
+ WriteParam(m, p.sent);
+ WriteParam(m, p.receive);
+ WriteParam(m, p.dispatch);
+ WriteParam(m, p.params);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ int type;
+ bool result =
+ ReadParam(m, iter, &r->channel) &&
+ ReadParam(m, iter, &type) &&
+ ReadParam(m, iter, &r->flags) &&
+ ReadParam(m, iter, &r->sent) &&
+ ReadParam(m, iter, &r->receive) &&
+ ReadParam(m, iter, &r->dispatch) &&
+ ReadParam(m, iter, &r->params);
+ r->type = static_cast<uint16>(type);
+ return result;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ // Doesn't make sense to implement this!
+ }
+};
+
+
+template <>
+struct ParamTraits<webkit_glue::WebApplicationInfo> {
+ typedef webkit_glue::WebApplicationInfo param_type;
+ static void Write(Message* m, const param_type& p);
+ static bool Read(const Message* m, void** iter, param_type* r);
+ static void Log(const param_type& p, std::wstring* l);
+};
+
+
+#if defined(OS_WIN)
+template<>
+struct ParamTraits<TransportDIB::Id> {
+ typedef TransportDIB::Id param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.handle);
+ WriteParam(m, p.sequence_num);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->handle) &&
+ ReadParam(m, iter, &r->sequence_num));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(L"TransportDIB(");
+ LogParam(p.handle, l);
+ l->append(L", ");
+ LogParam(p.sequence_num, l);
+ l->append(L")");
+ }
+};
+#endif
+
+template <>
+struct ParamTraits<Message> {
+ static void Write(Message* m, const Message& p) {
+ m->WriteInt(p.size());
+ m->WriteData(reinterpret_cast<const char*>(p.data()), p.size());
+ }
+ static bool Read(const Message* m, void** iter, Message* r) {
+ int size;
+ if (!m->ReadInt(iter, &size))
+ return false;
+ const char* data;
+ if (!m->ReadData(iter, &data, &size))
+ return false;
+ *r = Message(data, size);
+ return true;
+ }
+ static void Log(const Message& p, std::wstring* l) {
+ l->append(L"<IPC::Message>");
+ }
+};
+
+template <>
+struct ParamTraits<Tuple0> {
+ typedef Tuple0 param_type;
+ static void Write(Message* m, const param_type& p) {
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ }
+};
+
+template <class A>
+struct ParamTraits< Tuple1<A> > {
+ typedef Tuple1<A> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return ReadParam(m, iter, &r->a);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ }
+};
+
+template <class A, class B>
+struct ParamTraits< Tuple2<A, B> > {
+ typedef Tuple2<A, B> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ WriteParam(m, p.b);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->a) &&
+ ReadParam(m, iter, &r->b));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ }
+};
+
+template <class A, class B, class C>
+struct ParamTraits< Tuple3<A, B, C> > {
+ typedef Tuple3<A, B, C> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ WriteParam(m, p.b);
+ WriteParam(m, p.c);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->a) &&
+ ReadParam(m, iter, &r->b) &&
+ ReadParam(m, iter, &r->c));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ l->append(L", ");
+ LogParam(p.c, l);
+ }
+};
+
+template <class A, class B, class C, class D>
+struct ParamTraits< Tuple4<A, B, C, D> > {
+ typedef Tuple4<A, B, C, D> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ WriteParam(m, p.b);
+ WriteParam(m, p.c);
+ WriteParam(m, p.d);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->a) &&
+ ReadParam(m, iter, &r->b) &&
+ ReadParam(m, iter, &r->c) &&
+ ReadParam(m, iter, &r->d));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ l->append(L", ");
+ LogParam(p.c, l);
+ l->append(L", ");
+ LogParam(p.d, l);
+ }
+};
+
+template <class A, class B, class C, class D, class E>
+struct ParamTraits< Tuple5<A, B, C, D, E> > {
+ typedef Tuple5<A, B, C, D, E> param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.a);
+ WriteParam(m, p.b);
+ WriteParam(m, p.c);
+ WriteParam(m, p.d);
+ WriteParam(m, p.e);
+ }
+ static bool Read(const Message* m, void** iter, param_type* r) {
+ return (ReadParam(m, iter, &r->a) &&
+ ReadParam(m, iter, &r->b) &&
+ ReadParam(m, iter, &r->c) &&
+ ReadParam(m, iter, &r->d) &&
+ ReadParam(m, iter, &r->e));
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ l->append(L", ");
+ LogParam(p.c, l);
+ l->append(L", ");
+ LogParam(p.d, l);
+ l->append(L", ");
+ LogParam(p.e, l);
+ }
+};
+
+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::wstring* l) {
+ LogParam(p.a, l);
+ l->append(L", ");
+ LogParam(p.b, l);
+ l->append(L", ");
+ LogParam(p.c, l);
+ l->append(L", ");
+ LogParam(p.d, l);
+ l->append(L", ");
+ LogParam(p.e, l);
+ l->append(L", ");
+ LogParam(p.f, l);
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Generic message subclasses
+
+// Used for asynchronous messages.
+template <class ParamType>
+class MessageWithTuple : public Message {
+ public:
+ typedef ParamType Param;
+
+ MessageWithTuple(int32 routing_id, uint16 type, const Param& p)
+ : Message(routing_id, type, PRIORITY_NORMAL) {
+ WriteParam(this, p);
+ }
+
+ static bool Read(const Message* msg, Param* p) {
+ void* iter = NULL;
+ bool rv = ReadParam(msg, &iter, p);
+ DCHECK(rv) << "Error deserializing message " << msg->type();
+ return rv;
+ }
+
+ // Generic dispatcher. Should cover most cases.
+ template<class T, class Method>
+ static bool Dispatch(const Message* msg, T* obj, Method func) {
+ Param p;
+ if (Read(msg, &p)) {
+ DispatchToMethod(obj, func, p);
+ return true;
+ }
+ return false;
+ }
+
+ // The following dispatchers exist for the case where the callback function
+ // needs the message as well. They assume that "Param" is a type of Tuple
+ // (except the one arg case, as there is no Tuple1).
+ template<class T, typename TA>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, typename TA, typename TB>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA, TB)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a, p.b);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, typename TA, typename TB, typename TC>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA, TB, TC)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a, p.b, p.c);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, typename TA, typename TB, typename TC, typename TD>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA, TB, TC, TD)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a, p.b, p.c, p.d);
+ return true;
+ }
+ return false;
+ }
+
+ template<class T, typename TA, typename TB, typename TC, typename TD,
+ typename TE>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&, TA, TB, TC, TD, TE)) {
+ Param p;
+ if (Read(msg, &p)) {
+ (obj->*func)(*msg, p.a, p.b, p.c, p.d, p.e);
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const Message* msg, std::wstring* l) {
+ Param p;
+ if (Read(msg, &p))
+ LogParam(p, l);
+ }
+
+ // Functions used to do manual unpacking. Only used by the automation code,
+ // these should go away once that code uses SyncChannel.
+ template<typename TA, typename TB>
+ static bool Read(const IPC::Message* msg, TA* a, TB* b) {
+ ParamType params;
+ if (!Read(msg, &params))
+ return false;
+ *a = params.a;
+ *b = params.b;
+ return true;
+ }
+
+ template<typename TA, typename TB, typename TC>
+ static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c) {
+ ParamType params;
+ if (!Read(msg, &params))
+ return false;
+ *a = params.a;
+ *b = params.b;
+ *c = params.c;
+ return true;
+ }
+
+ template<typename TA, typename TB, typename TC, typename TD>
+ static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d) {
+ ParamType params;
+ if (!Read(msg, &params))
+ return false;
+ *a = params.a;
+ *b = params.b;
+ *c = params.c;
+ *d = params.d;
+ return true;
+ }
+
+ template<typename TA, typename TB, typename TC, typename TD, typename TE>
+ static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d, TE* e) {
+ ParamType params;
+ if (!Read(msg, &params))
+ return false;
+ *a = params.a;
+ *b = params.b;
+ *c = params.c;
+ *d = params.d;
+ *e = params.e;
+ return true;
+ }
+};
+
+// This class assumes that its template argument is a RefTuple (a Tuple with
+// reference elements).
+template <class RefTuple>
+class ParamDeserializer : public MessageReplyDeserializer {
+ public:
+ explicit ParamDeserializer(const RefTuple& out) : out_(out) { }
+
+ bool SerializeOutputParameters(const IPC::Message& msg, void* iter) {
+ return ReadParam(&msg, &iter, &out_);
+ }
+
+ RefTuple out_;
+};
+
+// defined in ipc_logging.cc
+void GenerateLogData(const std::wstring& channel, const Message& message,
+ LogData* data);
+
+// Used for synchronous messages.
+template <class SendParamType, class ReplyParamType>
+class MessageWithReply : public SyncMessage {
+ public:
+ typedef SendParamType SendParam;
+ typedef ReplyParamType ReplyParam;
+
+ MessageWithReply(int32 routing_id, uint16 type,
+ const SendParam& send, const ReplyParam& reply)
+ : SyncMessage(routing_id, type, PRIORITY_NORMAL,
+ new ParamDeserializer<ReplyParam>(reply)) {
+ WriteParam(this, send);
+ }
+
+ static void Log(const Message* msg, std::wstring* l) {
+ if (msg->is_sync()) {
+ SendParam p;
+ void* iter = SyncMessage::GetDataIterator(msg);
+ ReadParam(msg, &iter, &p);
+ LogParam(p, l);
+
+#if defined(IPC_MESSAGE_LOG_ENABLED)
+ const std::wstring& output_params = msg->output_params();
+ if (!l->empty() && !output_params.empty())
+ l->append(L", ");
+
+ l->append(output_params);
+#endif
+ } else {
+ // This is an outgoing reply. Now that we have the output parameters, we
+ // can finally log the message.
+ typename ReplyParam::ValueTuple p;
+ void* iter = SyncMessage::GetDataIterator(msg);
+ ReadParam(msg, &iter, &p);
+ LogParam(p, l);
+ }
+ }
+
+ template<class T, class Method>
+ static bool Dispatch(const Message* msg, T* obj, Method func) {
+ SendParam send_params;
+ void* iter = GetDataIterator(msg);
+ Message* reply = GenerateReply(msg);
+ bool error;
+ if (ReadParam(msg, &iter, &send_params)) {
+ typename ReplyParam::ValueTuple reply_params;
+ DispatchToMethod(obj, func, send_params, &reply_params);
+ WriteParam(reply, reply_params);
+ error = false;
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (msg->received_time() != 0) {
+ std::wstring output_params;
+ LogParam(reply_params, &output_params);
+ msg->set_output_params(output_params);
+ }
+#endif
+ } else {
+ NOTREACHED() << "Error deserializing message " << msg->type();
+ reply->set_reply_error();
+ error = true;
+ }
+
+ obj->Send(reply);
+ return !error;
+ }
+
+ template<class T, class Method>
+ static bool DispatchDelayReply(const Message* msg, T* obj, Method func) {
+ SendParam send_params;
+ void* iter = GetDataIterator(msg);
+ Message* reply = GenerateReply(msg);
+ bool error;
+ if (ReadParam(msg, &iter, &send_params)) {
+ Tuple1<Message&> t = MakeRefTuple(*reply);
+
+#ifdef IPC_MESSAGE_LOG_ENABLED
+ if (msg->sent_time()) {
+ // Don't log the sync message after dispatch, as we don't have the
+ // output parameters at that point. Instead, save its data and log it
+ // with the outgoing reply message when it's sent.
+ LogData* data = new LogData;
+ GenerateLogData(L"", *msg, data);
+ msg->set_dont_log();
+ reply->set_sync_log_data(data);
+ }
+#endif
+ DispatchToMethod(obj, func, send_params, &t);
+ error = false;
+ } else {
+ NOTREACHED() << "Error deserializing message " << msg->type();
+ reply->set_reply_error();
+ obj->Send(reply);
+ error = true;
+ }
+ return !error;
+ }
+
+ template<typename TA>
+ static void WriteReplyParams(Message* reply, TA a) {
+ ReplyParam p(a);
+ WriteParam(reply, p);
+ }
+
+ template<typename TA, typename TB>
+ static void WriteReplyParams(Message* reply, TA a, TB b) {
+ ReplyParam p(a, b);
+ WriteParam(reply, p);
+ }
+
+ template<typename TA, typename TB, typename TC>
+ static void WriteReplyParams(Message* reply, TA a, TB b, TC c) {
+ ReplyParam p(a, b, c);
+ WriteParam(reply, p);
+ }
+
+ template<typename TA, typename TB, typename TC, typename TD>
+ static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d) {
+ ReplyParam p(a, b, c, d);
+ WriteParam(reply, p);
+ }
+
+ template<typename TA, typename TB, typename TC, typename TD, typename TE>
+ static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d, TE e) {
+ ReplyParam p(a, b, c, d, e);
+ WriteParam(reply, p);
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_MESSAGE_UTILS_H_
diff --git a/chrome/common/ipc_send_fds_test.cc b/chrome/common/ipc_send_fds_test.cc
new file mode 100644
index 0000000..747892d
--- /dev/null
+++ b/chrome/common/ipc_send_fds_test.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#include "chrome/common/ipc_tests.h"
+
+#if defined(OS_MACOSX)
+extern "C" {
+#include <sandbox.h>
+}
+#endif
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "base/message_loop.h"
+#include "chrome/common/ipc_channel.h"
+#include "chrome/common/ipc_message_utils.h"
+
+#if defined(OS_POSIX)
+
+namespace {
+
+const unsigned kNumFDsToSend = 20;
+const char* kDevZeroPath = "/dev/zero";
+
+static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) {
+ // Check that we can read from the FD.
+ char buf;
+ ssize_t amt_read = read(fd, &buf, 1);
+ ASSERT_EQ(amt_read, 1);
+ ASSERT_EQ(buf, 0); // /dev/zero always reads NUL bytes.
+
+ struct stat st;
+ ASSERT_EQ(fstat(fd, &st), 0);
+
+ ASSERT_EQ(close(fd), 0);
+
+ // We compare iNode numbers to check that the file sent over the wire
+ // was actually the same physical file as the one we were expecting.
+ ASSERT_EQ(inode_num, st.st_ino);
+}
+
+class MyChannelDescriptorListener : public IPC::Channel::Listener {
+ public:
+ MyChannelDescriptorListener(ino_t expected_inode_num)
+ : expected_inode_num_(expected_inode_num),
+ num_fds_received_(0) {}
+
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ void* iter = NULL;
+
+ ++num_fds_received_;
+ base::FileDescriptor descriptor;
+
+ ASSERT_TRUE(
+ IPC::ParamTraits<base::FileDescriptor>::Read(
+ &message, &iter, &descriptor));
+
+ VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_);
+ if (num_fds_received_ == kNumFDsToSend) {
+ MessageLoop::current()->Quit();
+ }
+ }
+
+ virtual void OnChannelError() {
+ MessageLoop::current()->Quit();
+ }
+ private:
+ ino_t expected_inode_num_;
+ unsigned num_fds_received_;
+};
+
+void TestDescriptorServer(IPC::Channel &chan,
+ base::ProcessHandle process_handle) {
+ ASSERT_TRUE(process_handle);
+
+ for (unsigned i = 0; i < kNumFDsToSend; ++i) {
+ base::FileDescriptor descriptor;
+ const int fd = open(kDevZeroPath, O_RDONLY);
+ ASSERT_GE(fd, 0);
+ descriptor.auto_close = true;
+ descriptor.fd = fd;
+
+ IPC::Message* message = new IPC::Message(0, // routing_id
+ 3, // message type
+ IPC::Message::PRIORITY_NORMAL);
+ IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
+ chan.Send(message);
+ }
+
+ // Run message loop.
+ MessageLoop::current()->Run();
+
+ // Close Channel so client gets its OnChannelError() callback fired.
+ chan.Close();
+
+ // Cleanup child process.
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
+}
+
+int TestDescriptorClient(ino_t expected_inode_num) {
+ MessageLoopForIO main_message_loop;
+ MyChannelDescriptorListener listener(expected_inode_num);
+
+ // Setup IPC channel.
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT,
+ &listener);
+ chan.Connect();
+ MessageLoop::current()->Run();
+
+ return 0;
+}
+
+} // namespace
+
+// ---------------------------------------------------------------------------
+#if defined(OS_MACOSX)
+// TODO(port): Make this test cross-platform.
+MULTIPROCESS_TEST_MAIN(RunTestDescriptorClientSandboxed) {
+ struct stat st;
+ const int fd = open(kDevZeroPath, O_RDONLY);
+ fstat(fd, &st);
+ close(fd);
+
+ // Enable the Sandbox.
+ char* error_buff = NULL;
+ int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
+ &error_buff);
+ bool success = (error == 0 && error_buff == NULL);
+ if (!success) {
+ return -1;
+ }
+
+ sandbox_free_error(error_buff);
+
+ // Make sure Sandbox is really enabled.
+ if (open(kDevZeroPath, O_RDONLY) != -1) {
+ LOG(ERROR) << "Sandbox wasn't properly enabled";
+ return -1;
+ }
+
+ // See if we can receive a file descriptor.
+ return TestDescriptorClient(st.st_ino);
+}
+
+// Test that FDs are correctly sent to a sandboxed process.
+TEST_F(IPCChannelTest, DescriptorTestSandboxed) {
+ // Setup IPC channel.
+ MyChannelDescriptorListener listener(-1);
+
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ chan.Connect();
+
+ base::ProcessHandle process_handle = SpawnChild(
+ TEST_DESCRIPTOR_CLIENT_SANDBOXED,
+ &chan);
+ TestDescriptorServer(chan, process_handle);
+}
+#endif // defined(OS_MACOSX)
+
+MULTIPROCESS_TEST_MAIN(RunTestDescriptorClient) {
+ struct stat st;
+ const int fd = open(kDevZeroPath, O_RDONLY);
+ fstat(fd, &st);
+ close(fd);
+
+ return TestDescriptorClient(st.st_ino);
+}
+
+TEST_F(IPCChannelTest, DescriptorTest) {
+ // Setup IPC channel.
+ MyChannelDescriptorListener listener(-1);
+
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &listener);
+ chan.Connect();
+
+ base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT,
+ &chan);
+ TestDescriptorServer(chan, process_handle);
+}
+
+#endif // defined(OS_POSIX)
diff --git a/chrome/common/ipc_sync_channel.cc b/chrome/common/ipc_sync_channel.cc
new file mode 100644
index 0000000..842678c
--- /dev/null
+++ b/chrome/common/ipc_sync_channel.cc
@@ -0,0 +1,453 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/ipc_sync_channel.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/thread_local.h"
+#include "base/message_loop.h"
+#include "base/waitable_event.h"
+#include "base/waitable_event_watcher.h"
+#include "chrome/common/ipc_sync_message.h"
+
+using base::TimeDelta;
+using base::TimeTicks;
+using base::WaitableEvent;
+
+namespace IPC {
+// When we're blocked in a Send(), we need to process incoming synchronous
+// messages right away because it could be blocking our reply (either
+// directly from the same object we're calling, or indirectly through one or
+// more other channels). That means that in SyncContext's OnMessageReceived,
+// we need to process sync message right away if we're blocked. However a
+// simple check isn't sufficient, because the listener thread can be in the
+// process of calling Send.
+// To work around this, when SyncChannel filters a sync message, it sets
+// an event that the listener thread waits on during its Send() call. This
+// allows us to dispatch incoming sync messages when blocked. The race
+// condition is handled because if Send is in the process of being called, it
+// will check the event. In case the listener thread isn't sending a message,
+// we queue a task on the listener thread to dispatch the received messages.
+// The messages are stored in this queue object that's shared among all
+// SyncChannel objects on the same thread (since one object can receive a
+// sync message while another one is blocked).
+
+class SyncChannel::ReceivedSyncMsgQueue :
+ public base::RefCountedThreadSafe<ReceivedSyncMsgQueue> {
+ public:
+ // Returns the ReceivedSyncMsgQueue instance for this thread, creating one
+ // if necessary. Call RemoveContext on the same thread when done.
+ static ReceivedSyncMsgQueue* AddContext() {
+ // We want one ReceivedSyncMsgQueue per listener thread (i.e. since multiple
+ // SyncChannel objects can block the same thread).
+ ReceivedSyncMsgQueue* rv = lazy_tls_ptr_.Pointer()->Get();
+ if (!rv) {
+ rv = new ReceivedSyncMsgQueue();
+ ReceivedSyncMsgQueue::lazy_tls_ptr_.Pointer()->Set(rv);
+ }
+ rv->listener_count_++;
+ return rv;
+ }
+
+ ~ReceivedSyncMsgQueue() {
+ }
+
+ // Called on IPC thread when a synchronous message or reply arrives.
+ void QueueMessage(const Message& msg, SyncChannel::SyncContext* context) {
+ bool was_task_pending;
+ {
+ AutoLock auto_lock(message_lock_);
+
+ was_task_pending = task_pending_;
+ task_pending_ = true;
+
+ // We set the event in case the listener thread is blocked (or is about
+ // to). In case it's not, the PostTask dispatches the messages.
+ message_queue_.push_back(QueuedMessage(new Message(msg), context));
+ }
+
+ dispatch_event_.Signal();
+ if (!was_task_pending) {
+ listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &ReceivedSyncMsgQueue::DispatchMessagesTask));
+ }
+ }
+
+ void QueueReply(const Message &msg, SyncChannel::SyncContext* context) {
+ received_replies_.push_back(QueuedMessage(new Message(msg), context));
+ }
+
+ // Called on the listener's thread to process any queues synchronous
+ // messages.
+ void DispatchMessagesTask() {
+ {
+ AutoLock auto_lock(message_lock_);
+ task_pending_ = false;
+ }
+ DispatchMessages();
+ }
+
+ void DispatchMessages() {
+ while (true) {
+ Message* message;
+ scoped_refptr<SyncChannel::SyncContext> context;
+ {
+ AutoLock auto_lock(message_lock_);
+ if (message_queue_.empty())
+ break;
+
+ message = message_queue_.front().message;
+ context = message_queue_.front().context;
+ message_queue_.pop_front();
+ }
+
+ context->OnDispatchMessage(*message);
+ delete message;
+ }
+ }
+
+ // SyncChannel calls this in its destructor.
+ void RemoveContext(SyncContext* context) {
+ AutoLock auto_lock(message_lock_);
+
+ SyncMessageQueue::iterator iter = message_queue_.begin();
+ while (iter != message_queue_.end()) {
+ if (iter->context == context) {
+ delete iter->message;
+ iter = message_queue_.erase(iter);
+ } else {
+ iter++;
+ }
+ }
+
+ if (--listener_count_ == 0) {
+ DCHECK(lazy_tls_ptr_.Pointer()->Get());
+ lazy_tls_ptr_.Pointer()->Set(NULL);
+ }
+ }
+
+ WaitableEvent* dispatch_event() { return &dispatch_event_; }
+ MessageLoop* listener_message_loop() { return listener_message_loop_; }
+
+ // Holds a pointer to the per-thread ReceivedSyncMsgQueue object.
+ static base::LazyInstance<base::ThreadLocalPointer<ReceivedSyncMsgQueue> >
+ lazy_tls_ptr_;
+
+ // Called on the ipc thread to check if we can unblock any current Send()
+ // calls based on a queued reply.
+ void DispatchReplies() {
+ for (size_t i = 0; i < received_replies_.size(); ++i) {
+ Message* message = received_replies_[i].message;
+ if (received_replies_[i].context->TryToUnblockListener(message)) {
+ delete message;
+ received_replies_.erase(received_replies_.begin() + i);
+ return;
+ }
+ }
+ }
+
+ private:
+ // See the comment in SyncChannel::SyncChannel for why this event is created
+ // as manual reset.
+ ReceivedSyncMsgQueue() :
+ dispatch_event_(true, false),
+ listener_message_loop_(MessageLoop::current()),
+ task_pending_(false),
+ listener_count_(0) {
+ }
+
+ // Holds information about a queued synchronous message or reply.
+ struct QueuedMessage {
+ QueuedMessage(Message* m, SyncContext* c) : message(m), context(c) { }
+ Message* message;
+ scoped_refptr<SyncChannel::SyncContext> context;
+ };
+
+ typedef std::deque<QueuedMessage> SyncMessageQueue;
+ SyncMessageQueue message_queue_;
+
+ std::vector<QueuedMessage> received_replies_;
+
+ // Set when we got a synchronous message that we must respond to as the
+ // sender needs its reply before it can reply to our original synchronous
+ // message.
+ WaitableEvent dispatch_event_;
+ MessageLoop* listener_message_loop_;
+ Lock message_lock_;
+ bool task_pending_;
+ int listener_count_;
+};
+
+base::LazyInstance<base::ThreadLocalPointer<SyncChannel::ReceivedSyncMsgQueue> >
+ SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_(base::LINKER_INITIALIZED);
+
+SyncChannel::SyncContext::SyncContext(
+ Channel::Listener* listener,
+ MessageFilter* filter,
+ MessageLoop* ipc_thread,
+ WaitableEvent* shutdown_event)
+ : ChannelProxy::Context(listener, filter, ipc_thread),
+ received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()),
+ shutdown_event_(shutdown_event) {
+}
+
+SyncChannel::SyncContext::~SyncContext() {
+ while (!deserializers_.empty())
+ Pop();
+}
+
+// Adds information about an outgoing sync message to the context so that
+// we know how to deserialize the reply. Returns a handle that's set when
+// the reply has arrived.
+void SyncChannel::SyncContext::Push(SyncMessage* sync_msg) {
+ // The event is created as manual reset because in between Signal and
+ // OnObjectSignalled, another Send can happen which would stop the watcher
+ // from being called. The event would get watched later, when the nested
+ // Send completes, so the event will need to remain set.
+ PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg),
+ sync_msg->GetReplyDeserializer(),
+ new WaitableEvent(true, false));
+ AutoLock auto_lock(deserializers_lock_);
+ deserializers_.push_back(pending);
+}
+
+bool SyncChannel::SyncContext::Pop() {
+ bool result;
+ {
+ AutoLock auto_lock(deserializers_lock_);
+ PendingSyncMsg msg = deserializers_.back();
+ delete msg.deserializer;
+ delete msg.done_event;
+ msg.done_event = NULL;
+ deserializers_.pop_back();
+ result = msg.send_result;
+ }
+
+ // We got a reply to a synchronous Send() call that's blocking the listener
+ // thread. However, further down the call stack there could be another
+ // blocking Send() call, whose reply we received after we made this last
+ // Send() call. So check if we have any queued replies available that
+ // can now unblock the listener thread.
+ ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ received_sync_msgs_.get(), &ReceivedSyncMsgQueue::DispatchReplies));
+
+ return result;
+}
+
+WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() {
+ AutoLock auto_lock(deserializers_lock_);
+ return deserializers_.back().done_event;
+}
+
+WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() {
+ return received_sync_msgs_->dispatch_event();
+}
+
+void SyncChannel::SyncContext::DispatchMessages() {
+ received_sync_msgs_->DispatchMessages();
+}
+
+bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) {
+ AutoLock auto_lock(deserializers_lock_);
+ if (deserializers_.empty() ||
+ !SyncMessage::IsMessageReplyTo(*msg, deserializers_.back().id)) {
+ return false;
+ }
+
+ if (!msg->is_reply_error()) {
+ deserializers_.back().send_result = deserializers_.back().deserializer->
+ SerializeOutputParameters(*msg);
+ }
+ deserializers_.back().done_event->Signal();
+
+ return true;
+}
+
+void SyncChannel::SyncContext::Clear() {
+ CancelPendingSends();
+ received_sync_msgs_->RemoveContext(this);
+
+ Context::Clear();
+}
+
+void SyncChannel::SyncContext::OnMessageReceived(const Message& msg) {
+ // Give the filters a chance at processing this message.
+ if (TryFilters(msg))
+ return;
+
+ if (TryToUnblockListener(&msg))
+ return;
+
+ if (msg.should_unblock()) {
+ received_sync_msgs_->QueueMessage(msg, this);
+ return;
+ }
+
+ if (msg.is_reply()) {
+ received_sync_msgs_->QueueReply(msg, this);
+ return;
+ }
+
+ return Context::OnMessageReceivedNoFilter(msg);
+}
+
+void SyncChannel::SyncContext::OnChannelError() {
+ CancelPendingSends();
+ shutdown_watcher_.StopWatching();
+ Context::OnChannelError();
+}
+
+void SyncChannel::SyncContext::OnChannelOpened() {
+ shutdown_watcher_.StartWatching(shutdown_event_, this);
+ Context::OnChannelOpened();
+}
+
+void SyncChannel::SyncContext::OnChannelClosed() {
+ shutdown_watcher_.StopWatching();
+ Context::OnChannelClosed();
+}
+
+void SyncChannel::SyncContext::OnSendTimeout(int message_id) {
+ AutoLock auto_lock(deserializers_lock_);
+ PendingSyncMessageQueue::iterator iter;
+ for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) {
+ if (iter->id == message_id) {
+ iter->done_event->Signal();
+ break;
+ }
+ }
+}
+
+void SyncChannel::SyncContext::CancelPendingSends() {
+ AutoLock auto_lock(deserializers_lock_);
+ PendingSyncMessageQueue::iterator iter;
+ for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++)
+ iter->done_event->Signal();
+}
+
+void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) {
+ DCHECK(event == shutdown_event_);
+ // Process shut down before we can get a reply to a synchronous message.
+ // Cancel pending Send calls, which will end up setting the send done event.
+ CancelPendingSends();
+}
+
+
+SyncChannel::SyncChannel(
+ const std::wstring& channel_id, Channel::Mode mode,
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_message_loop, bool create_pipe_now,
+ WaitableEvent* shutdown_event)
+ : ChannelProxy(
+ channel_id, mode, ipc_message_loop,
+ new SyncContext(listener, filter, ipc_message_loop, shutdown_event),
+ create_pipe_now),
+ sync_messages_with_no_timeout_allowed_(true) {
+ // Ideally we only want to watch this object when running a nested message
+ // loop. However, we don't know when it exits if there's another nested
+ // message loop running under it or not, so we wouldn't know whether to
+ // stop or keep watching. So we always watch it, and create the event as
+ // manual reset since the object watcher might otherwise reset the event
+ // when we're doing a WaitMany.
+ dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(), this);
+}
+
+SyncChannel::~SyncChannel() {
+}
+
+bool SyncChannel::Send(Message* message) {
+ return SendWithTimeout(message, base::kNoTimeout);
+}
+
+bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) {
+ if (!message->is_sync()) {
+ ChannelProxy::Send(message);
+ return true;
+ }
+
+ // *this* might get deleted in WaitForReply.
+ scoped_refptr<SyncContext> context(sync_context());
+ if (context->shutdown_event()->IsSignaled()) {
+ delete message;
+ return false;
+ }
+
+ DCHECK(sync_messages_with_no_timeout_allowed_ ||
+ timeout_ms != base::kNoTimeout);
+ SyncMessage* sync_msg = static_cast<SyncMessage*>(message);
+ context->Push(sync_msg);
+ int message_id = SyncMessage::GetMessageId(*sync_msg);
+ WaitableEvent* pump_messages_event = sync_msg->pump_messages_event();
+
+ ChannelProxy::Send(message);
+
+ if (timeout_ms != base::kNoTimeout) {
+ // We use the sync message id so that when a message times out, we don't
+ // confuse it with another send that is either above/below this Send in
+ // the call stack.
+ context->ipc_message_loop()->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(context.get(),
+ &SyncContext::OnSendTimeout, message_id), timeout_ms);
+ }
+
+ // Wait for reply, or for any other incoming synchronous messages.
+ WaitForReply(pump_messages_event);
+
+ return context->Pop();
+}
+
+void SyncChannel::WaitForReply(WaitableEvent* pump_messages_event) {
+ while (true) {
+ WaitableEvent* objects[] = {
+ sync_context()->GetDispatchEvent(),
+ sync_context()->GetSendDoneEvent(),
+ pump_messages_event
+ };
+
+ unsigned count = pump_messages_event ? 3: 2;
+ unsigned result = WaitableEvent::WaitMany(objects, count);
+ if (result == 0 /* dispatch event */) {
+ // We're waiting for a reply, but we received a blocking synchronous
+ // call. We must process it or otherwise a deadlock might occur.
+ sync_context()->GetDispatchEvent()->Reset();
+ sync_context()->DispatchMessages();
+ continue;
+ }
+
+ if (result == 2 /* pump_messages_event */)
+ WaitForReplyWithNestedMessageLoop(); // Start a nested message loop.
+
+ break;
+ }
+}
+
+void SyncChannel::WaitForReplyWithNestedMessageLoop() {
+ WaitableEvent* old_done_event = send_done_watcher_.GetWatchedEvent();
+ send_done_watcher_.StopWatching();
+ send_done_watcher_.StartWatching(sync_context()->GetSendDoneEvent(), this);
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+ if (old_done_event)
+ send_done_watcher_.StartWatching(old_done_event, this);
+}
+
+void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) {
+ WaitableEvent* dispatch_event = sync_context()->GetDispatchEvent();
+ if (event == dispatch_event) {
+ // The call to DispatchMessages might delete this object, so reregister
+ // the object watcher first.
+ dispatch_event->Reset();
+ dispatch_watcher_.StartWatching(dispatch_event, this);
+ sync_context()->DispatchMessages();
+ } else {
+ // We got the reply, timed out or the process shutdown.
+ DCHECK(event == sync_context()->GetSendDoneEvent());
+ MessageLoop::current()->Quit();
+ }
+}
+
+} // namespace IPC
diff --git a/chrome/common/ipc_sync_channel.h b/chrome/common/ipc_sync_channel.h
new file mode 100644
index 0000000..1b192f6
--- /dev/null
+++ b/chrome/common/ipc_sync_channel.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_SYNC_SENDER_H__
+#define CHROME_COMMON_IPC_SYNC_SENDER_H__
+
+#include <string>
+#include <deque>
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "base/scoped_handle.h"
+#include "base/waitable_event.h"
+#include "base/waitable_event_watcher.h"
+#include "chrome/common/ipc_channel_proxy.h"
+
+namespace IPC {
+
+class SyncMessage;
+class MessageReplyDeserializer;
+
+// This is similar to IPC::ChannelProxy, with the added feature of supporting
+// sending synchronous messages.
+// Note that care must be taken that the lifetime of the ipc_thread argument
+// is more than this object. If the message loop goes away while this object
+// is running and it's used to send a message, then it will use the invalid
+// message loop pointer to proxy it to the ipc thread.
+class SyncChannel : public ChannelProxy,
+ public base::WaitableEventWatcher::Delegate {
+ public:
+ SyncChannel(const std::wstring& channel_id, Channel::Mode mode,
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_message_loop, bool create_pipe_now,
+ base::WaitableEvent* shutdown_event);
+ ~SyncChannel();
+
+ virtual bool Send(Message* message);
+ virtual bool SendWithTimeout(Message* message, int timeout_ms);
+
+ // Whether we allow sending messages with no time-out.
+ void set_sync_messages_with_no_timeout_allowed(bool value) {
+ sync_messages_with_no_timeout_allowed_ = value;
+ }
+
+ protected:
+ class ReceivedSyncMsgQueue;
+ friend class ReceivedSyncMsgQueue;
+
+ // SyncContext holds the per object data for SyncChannel, so that SyncChannel
+ // can be deleted while it's being used in a different thread. See
+ // ChannelProxy::Context for more information.
+ class SyncContext : public Context,
+ public base::WaitableEventWatcher::Delegate {
+ public:
+ SyncContext(Channel::Listener* listener,
+ MessageFilter* filter,
+ MessageLoop* ipc_thread,
+ base::WaitableEvent* shutdown_event);
+
+ ~SyncContext();
+
+ // Adds information about an outgoing sync message to the context so that
+ // we know how to deserialize the reply.
+ void Push(IPC::SyncMessage* sync_msg);
+
+ // Cleanly remove the top deserializer (and throw it away). Returns the
+ // result of the Send call for that message.
+ bool Pop();
+
+ // Returns an event that's set when the send is complete, timed out or the
+ // process shut down.
+ base::WaitableEvent* GetSendDoneEvent();
+
+ // Returns an event that's set when an incoming message that's not the reply
+ // needs to get dispatched (by calling SyncContext::DispatchMessages).
+ base::WaitableEvent* GetDispatchEvent();
+
+ void DispatchMessages();
+
+ // Checks if the given message is blocking the listener thread because of a
+ // synchronous send. If it is, the thread is unblocked and true is
+ // returned. Otherwise the function returns false.
+ bool TryToUnblockListener(const Message* msg);
+
+ // Called on the IPC thread when a sync send that runs a nested message loop
+ // times out.
+ void OnSendTimeout(int message_id);
+
+ base::WaitableEvent* shutdown_event() { return shutdown_event_; }
+
+ private:
+ // IPC::ChannelProxy methods that we override.
+
+ // Called on the listener thread.
+ virtual void Clear();
+
+ // Called on the IPC thread.
+ virtual void OnMessageReceived(const Message& msg);
+ virtual void OnChannelError();
+ virtual void OnChannelOpened();
+ virtual void OnChannelClosed();
+
+ // Cancels all pending Send calls.
+ void CancelPendingSends();
+
+ // WaitableEventWatcher::Delegate implementation.
+ virtual void OnWaitableEventSignaled(base::WaitableEvent* arg);
+
+ // When sending a synchronous message, this structure contains an object
+ // that knows how to deserialize the response.
+ struct PendingSyncMsg {
+ PendingSyncMsg(int id, IPC::MessageReplyDeserializer* d,
+ base::WaitableEvent* e) :
+ id(id), deserializer(d), done_event(e), send_result(false) { }
+ int id;
+ IPC::MessageReplyDeserializer* deserializer;
+ base::WaitableEvent* done_event;
+ bool send_result;
+ };
+
+ typedef std::deque<PendingSyncMsg> PendingSyncMessageQueue;
+ PendingSyncMessageQueue deserializers_;
+ Lock deserializers_lock_;
+
+ scoped_refptr<ReceivedSyncMsgQueue> received_sync_msgs_;
+
+ base::WaitableEvent* shutdown_event_;
+ base::WaitableEventWatcher shutdown_watcher_;
+ };
+
+ private:
+ // WaitableEventWatcher::Delegate implementation.
+ virtual void OnWaitableEventSignaled(base::WaitableEvent* arg);
+
+ SyncContext* sync_context() {
+ return reinterpret_cast<SyncContext*>(context());
+ }
+
+ // Both these functions wait for a reply, timeout or process shutdown. The
+ // latter one also runs a nested message loop in the meantime.
+ void WaitForReply(base::WaitableEvent* pump_messages_event);
+
+ // Runs a nested message loop until a reply arrives, times out, or the process
+ // shuts down.
+ void WaitForReplyWithNestedMessageLoop();
+
+ bool sync_messages_with_no_timeout_allowed_;
+
+ // Used to signal events between the IPC and listener threads.
+ base::WaitableEventWatcher send_done_watcher_;
+ base::WaitableEventWatcher dispatch_watcher_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SyncChannel);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_SYNC_SENDER_H__
diff --git a/chrome/common/ipc_sync_channel_unittest.cc b/chrome/common/ipc_sync_channel_unittest.cc
new file mode 100644
index 0000000..6e00fca
--- /dev/null
+++ b/chrome/common/ipc_sync_channel_unittest.cc
@@ -0,0 +1,1009 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit test for SyncChannel.
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/platform_thread.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/waitable_event.h"
+#include "chrome/common/ipc_message.h"
+#include "chrome/common/ipc_sync_channel.h"
+#include "chrome/common/stl_util-inl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+#define MESSAGES_INTERNAL_FILE "chrome/common/ipc_sync_message_unittest.h"
+#include "chrome/common/ipc_message_macros.h"
+
+using namespace IPC;
+using base::WaitableEvent;
+
+namespace {
+
+// Base class for a "process" with listener and IPC threads.
+class Worker : public Channel::Listener, public Message::Sender {
+ public:
+ // Will create a channel without a name.
+ Worker(Channel::Mode mode, const std::string& thread_name)
+ : done_(new WaitableEvent(false, false)),
+ channel_created_(new WaitableEvent(false, false)),
+ mode_(mode),
+ ipc_thread_((thread_name + "_ipc").c_str()),
+ listener_thread_((thread_name + "_listener").c_str()),
+ overrided_thread_(NULL),
+ shutdown_event_(true, false) { }
+
+ // Will create a named channel and use this name for the threads' name.
+ Worker(const std::wstring& channel_name, Channel::Mode mode)
+ : done_(new WaitableEvent(false, false)),
+ channel_created_(new WaitableEvent(false, false)),
+ channel_name_(channel_name),
+ mode_(mode),
+ ipc_thread_((WideToUTF8(channel_name) + "_ipc").c_str()),
+ listener_thread_((WideToUTF8(channel_name) + "_listener").c_str()),
+ overrided_thread_(NULL),
+ shutdown_event_(true, false) { }
+
+ // The IPC thread needs to outlive SyncChannel, so force the correct order of
+ // destruction.
+ virtual ~Worker() {
+ WaitableEvent listener_done(false, false), ipc_done(false, false);
+ ListenerThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Worker::OnListenerThreadShutdown1, &listener_done,
+ &ipc_done));
+ listener_done.Wait();
+ ipc_done.Wait();
+ ipc_thread_.Stop();
+ listener_thread_.Stop();
+ }
+ void AddRef() { }
+ void Release() { }
+ bool Send(Message* msg) { return channel_->Send(msg); }
+ bool SendWithTimeout(Message* msg, int timeout_ms) {
+ return channel_->SendWithTimeout(msg, timeout_ms);
+ }
+ void WaitForChannelCreation() { channel_created_->Wait(); }
+ void CloseChannel() {
+ DCHECK(MessageLoop::current() == ListenerThread()->message_loop());
+ channel_->Close();
+ }
+ void Start() {
+ StartThread(&listener_thread_, MessageLoop::TYPE_DEFAULT);
+ ListenerThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Worker::OnStart));
+ }
+ void OverrideThread(base::Thread* overrided_thread) {
+ DCHECK(overrided_thread_ == NULL);
+ overrided_thread_ = overrided_thread;
+ }
+ bool SendAnswerToLife(bool pump, int timeout, bool succeed) {
+ int answer = 0;
+ SyncMessage* msg = new SyncChannelTestMsg_AnswerToLife(&answer);
+ if (pump)
+ msg->EnableMessagePumping();
+ bool result = SendWithTimeout(msg, timeout);
+ DCHECK(result == succeed);
+ DCHECK(answer == (succeed ? 42 : 0));
+ return result;
+ }
+ bool SendDouble(bool pump, bool succeed) {
+ int answer = 0;
+ SyncMessage* msg = new SyncChannelTestMsg_Double(5, &answer);
+ if (pump)
+ msg->EnableMessagePumping();
+ bool result = Send(msg);
+ DCHECK(result == succeed);
+ DCHECK(answer == (succeed ? 10 : 0));
+ return result;
+ }
+ Channel::Mode mode() { return mode_; }
+ WaitableEvent* done_event() { return done_.get(); }
+
+ protected:
+ // Derived classes need to call this when they've completed their part of
+ // the test.
+ void Done() { done_->Signal(); }
+ // Functions for dervied classes to implement if they wish.
+ virtual void Run() { }
+ virtual void OnAnswer(int* answer) { NOTREACHED(); }
+ virtual void OnAnswerDelay(Message* reply_msg) {
+ // The message handler map below can only take one entry for
+ // SyncChannelTestMsg_AnswerToLife, so since some classes want
+ // the normal version while other want the delayed reply, we
+ // call the normal version if the derived class didn't override
+ // this function.
+ int answer;
+ OnAnswer(&answer);
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, answer);
+ Send(reply_msg);
+ }
+ virtual void OnDouble(int in, int* out) { NOTREACHED(); }
+ virtual void OnDoubleDelay(int in, Message* reply_msg) {
+ int result;
+ OnDouble(in, &result);
+ SyncChannelTestMsg_Double::WriteReplyParams(reply_msg, result);
+ Send(reply_msg);
+ }
+
+ private:
+ base::Thread* ListenerThread() {
+ return overrided_thread_ ? overrided_thread_ : &listener_thread_;
+ }
+ // Called on the listener thread to create the sync channel.
+ void OnStart() {
+ // Link ipc_thread_, listener_thread_ and channel_ altogether.
+ StartThread(&ipc_thread_, MessageLoop::TYPE_IO);
+ channel_.reset(new SyncChannel(
+ channel_name_, mode_, this, NULL, ipc_thread_.message_loop(), true,
+ &shutdown_event_));
+ channel_created_->Signal();
+ Run();
+ }
+
+ void OnListenerThreadShutdown1(WaitableEvent* listener_event,
+ WaitableEvent* ipc_event) {
+ // SyncChannel needs to be destructed on the thread that it was created on.
+ channel_.reset();
+
+ MessageLoop::current()->RunAllPending();
+
+ ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Worker::OnIPCThreadShutdown, listener_event, ipc_event));
+ }
+
+ void OnIPCThreadShutdown(WaitableEvent* listener_event,
+ WaitableEvent* ipc_event) {
+ MessageLoop::current()->RunAllPending();
+ ipc_event->Signal();
+
+ listener_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &Worker::OnListenerThreadShutdown2, listener_event));
+ }
+
+ void OnListenerThreadShutdown2(WaitableEvent* listener_event) {
+ MessageLoop::current()->RunAllPending();
+ listener_event->Signal();
+ }
+
+ void OnMessageReceived(const Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(Worker, message)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_Double, OnDoubleDelay)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_AnswerToLife,
+ OnAnswerDelay)
+ IPC_END_MESSAGE_MAP()
+ }
+
+ void StartThread(base::Thread* thread, MessageLoop::Type type) {
+ base::Thread::Options options;
+ options.message_loop_type = type;
+ thread->StartWithOptions(options);
+ }
+
+ scoped_ptr<WaitableEvent> done_;
+ scoped_ptr<WaitableEvent> channel_created_;
+ std::wstring channel_name_;
+ Channel::Mode mode_;
+ scoped_ptr<SyncChannel> channel_;
+ base::Thread ipc_thread_;
+ base::Thread listener_thread_;
+ base::Thread* overrided_thread_;
+
+ base::WaitableEvent shutdown_event_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Worker);
+};
+
+
+// Starts the test with the given workers. This function deletes the workers
+// when it's done.
+void RunTest(std::vector<Worker*> workers) {
+ // First we create the workers that are channel servers, or else the other
+ // workers' channel initialization might fail because the pipe isn't created..
+ for (size_t i = 0; i < workers.size(); ++i) {
+ if (workers[i]->mode() == Channel::MODE_SERVER) {
+ workers[i]->Start();
+ workers[i]->WaitForChannelCreation();
+ }
+ }
+
+ // now create the clients
+ for (size_t i = 0; i < workers.size(); ++i) {
+ if (workers[i]->mode() == Channel::MODE_CLIENT)
+ workers[i]->Start();
+ }
+
+ // wait for all the workers to finish
+ for (size_t i = 0; i < workers.size(); ++i)
+ workers[i]->done_event()->Wait();
+
+ STLDeleteContainerPointers(workers.begin(), workers.end());
+}
+
+} // namespace
+
+class IPCSyncChannelTest : public testing::Test {
+ private:
+ MessageLoop message_loop_;
+};
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class SimpleServer : public Worker {
+ public:
+ SimpleServer(bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "simpler_server"),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+class SimpleClient : public Worker {
+ public:
+ SimpleClient() : Worker(Channel::MODE_CLIENT, "simple_client") { }
+
+ void OnAnswer(int* answer) {
+ *answer = 42;
+ Done();
+ }
+};
+
+void Simple(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ workers.push_back(new SimpleServer(pump_during_send));
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests basic synchronous call
+TEST_F(IPCSyncChannelTest, Simple) {
+ Simple(false);
+ Simple(true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class DelayClient : public Worker {
+ public:
+ DelayClient() : Worker(Channel::MODE_CLIENT, "delay_client") { }
+
+ void OnAnswerDelay(Message* reply_msg) {
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ Done();
+ }
+};
+
+void DelayReply(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ workers.push_back(new SimpleServer(pump_during_send));
+ workers.push_back(new DelayClient());
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that asynchronous replies work
+TEST_F(IPCSyncChannelTest, DelayReply) {
+ DelayReply(false);
+ DelayReply(true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class NoHangServer : public Worker {
+ public:
+ explicit NoHangServer(WaitableEvent* got_first_reply, bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "no_hang_server"),
+ got_first_reply_(got_first_reply),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ got_first_reply_->Signal();
+
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, false);
+ Done();
+ }
+
+ WaitableEvent* got_first_reply_;
+ bool pump_during_send_;
+};
+
+class NoHangClient : public Worker {
+ public:
+ explicit NoHangClient(WaitableEvent* got_first_reply)
+ : Worker(Channel::MODE_CLIENT, "no_hang_client"),
+ got_first_reply_(got_first_reply) { }
+
+ virtual void OnAnswerDelay(Message* reply_msg) {
+ // Use the DELAY_REPLY macro so that we can force the reply to be sent
+ // before this function returns (when the channel will be reset).
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ got_first_reply_->Wait();
+ CloseChannel();
+ Done();
+ }
+
+ WaitableEvent* got_first_reply_;
+};
+
+void NoHang(bool pump_during_send) {
+ WaitableEvent got_first_reply(false, false);
+ std::vector<Worker*> workers;
+ workers.push_back(new NoHangServer(&got_first_reply, pump_during_send));
+ workers.push_back(new NoHangClient(&got_first_reply));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that caller doesn't hang if receiver dies
+TEST_F(IPCSyncChannelTest, NoHang) {
+ NoHang(false);
+ NoHang(true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class UnblockServer : public Worker {
+ public:
+ UnblockServer(bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "unblock_server"),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ Done();
+ }
+
+ void OnDouble(int in, int* out) {
+ *out = in * 2;
+ }
+
+ bool pump_during_send_;
+};
+
+class UnblockClient : public Worker {
+ public:
+ UnblockClient(bool pump_during_send)
+ : Worker(Channel::MODE_CLIENT, "unblock_client"),
+ pump_during_send_(pump_during_send) { }
+
+ void OnAnswer(int* answer) {
+ SendDouble(pump_during_send_, true);
+ *answer = 42;
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+void Unblock(bool server_pump, bool client_pump) {
+ std::vector<Worker*> workers;
+ workers.push_back(new UnblockServer(server_pump));
+ workers.push_back(new UnblockClient(client_pump));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that the caller unblocks to answer a sync message from the receiver.
+TEST_F(IPCSyncChannelTest, Unblock) {
+ Unblock(false, false);
+ Unblock(false, true);
+ Unblock(true, false);
+ Unblock(true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class RecursiveServer : public Worker {
+ public:
+ explicit RecursiveServer(
+ bool expected_send_result, bool pump_first, bool pump_second)
+ : Worker(Channel::MODE_SERVER, "recursive_server"),
+ expected_send_result_(expected_send_result),
+ pump_first_(pump_first), pump_second_(pump_second) { }
+ void Run() {
+ SendDouble(pump_first_, expected_send_result_);
+ Done();
+ }
+
+ void OnDouble(int in, int* out) {
+ *out = in * 2;
+ SendAnswerToLife(pump_second_, base::kNoTimeout, expected_send_result_);
+ }
+
+ bool expected_send_result_, pump_first_, pump_second_;
+};
+
+class RecursiveClient : public Worker {
+ public:
+ explicit RecursiveClient(bool pump_during_send, bool close_channel)
+ : Worker(Channel::MODE_CLIENT, "recursive_client"),
+ pump_during_send_(pump_during_send), close_channel_(close_channel) { }
+
+ void OnDoubleDelay(int in, Message* reply_msg) {
+ SendDouble(pump_during_send_, !close_channel_);
+ if (close_channel_) {
+ delete reply_msg;
+ } else {
+ SyncChannelTestMsg_Double::WriteReplyParams(reply_msg, in * 2);
+ Send(reply_msg);
+ }
+ Done();
+ }
+
+ void OnAnswerDelay(Message* reply_msg) {
+ if (close_channel_) {
+ delete reply_msg;
+ CloseChannel();
+ } else {
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ }
+ }
+
+ bool pump_during_send_, close_channel_;
+};
+
+void Recursive(
+ bool server_pump_first, bool server_pump_second, bool client_pump) {
+ std::vector<Worker*> workers;
+ workers.push_back(
+ new RecursiveServer(true, server_pump_first, server_pump_second));
+ workers.push_back(new RecursiveClient(client_pump, false));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests a server calling Send while another Send is pending.
+TEST_F(IPCSyncChannelTest, Recursive) {
+ Recursive(false, false, false);
+ Recursive(false, false, true);
+ Recursive(false, true, false);
+ Recursive(false, true, true);
+ Recursive(true, false, false);
+ Recursive(true, false, true);
+ Recursive(true, true, false);
+ Recursive(true, true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+void RecursiveNoHang(
+ bool server_pump_first, bool server_pump_second, bool client_pump) {
+ std::vector<Worker*> workers;
+ workers.push_back(
+ new RecursiveServer(false, server_pump_first, server_pump_second));
+ workers.push_back(new RecursiveClient(client_pump, true));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that if a caller makes a sync call during an existing sync call and
+// the receiver dies, neither of the Send() calls hang.
+TEST_F(IPCSyncChannelTest, RecursiveNoHang) {
+ RecursiveNoHang(false, false, false);
+ RecursiveNoHang(false, false, true);
+ RecursiveNoHang(false, true, false);
+ RecursiveNoHang(false, true, true);
+ RecursiveNoHang(true, false, false);
+ RecursiveNoHang(true, false, true);
+ RecursiveNoHang(true, true, false);
+ RecursiveNoHang(true, true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class MultipleServer1 : public Worker {
+ public:
+ MultipleServer1(bool pump_during_send)
+ : Worker(L"test_channel1", Channel::MODE_SERVER),
+ pump_during_send_(pump_during_send) { }
+
+ void Run() {
+ SendDouble(pump_during_send_, true);
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+class MultipleClient1 : public Worker {
+ public:
+ MultipleClient1(WaitableEvent* client1_msg_received,
+ WaitableEvent* client1_can_reply) :
+ Worker(L"test_channel1", Channel::MODE_CLIENT),
+ client1_msg_received_(client1_msg_received),
+ client1_can_reply_(client1_can_reply) { }
+
+ void OnDouble(int in, int* out) {
+ client1_msg_received_->Signal();
+ *out = in * 2;
+ client1_can_reply_->Wait();
+ Done();
+ }
+
+ private:
+ WaitableEvent *client1_msg_received_, *client1_can_reply_;
+};
+
+class MultipleServer2 : public Worker {
+ public:
+ MultipleServer2() : Worker(L"test_channel2", Channel::MODE_SERVER) { }
+
+ void OnAnswer(int* result) {
+ *result = 42;
+ Done();
+ }
+};
+
+class MultipleClient2 : public Worker {
+ public:
+ MultipleClient2(
+ WaitableEvent* client1_msg_received, WaitableEvent* client1_can_reply,
+ bool pump_during_send)
+ : Worker(L"test_channel2", Channel::MODE_CLIENT),
+ client1_msg_received_(client1_msg_received),
+ client1_can_reply_(client1_can_reply),
+ pump_during_send_(pump_during_send) { }
+
+ void Run() {
+ client1_msg_received_->Wait();
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ client1_can_reply_->Signal();
+ Done();
+ }
+
+ private:
+ WaitableEvent *client1_msg_received_, *client1_can_reply_;
+ bool pump_during_send_;
+};
+
+void Multiple(bool server_pump, bool client_pump) {
+ std::vector<Worker*> workers;
+
+ // A shared worker thread so that server1 and server2 run on one thread.
+ base::Thread worker_thread("Multiple");
+ worker_thread.Start();
+
+ // Server1 sends a sync msg to client1, which blocks the reply until
+ // server2 (which runs on the same worker thread as server1) responds
+ // to a sync msg from client2.
+ WaitableEvent client1_msg_received(false, false);
+ WaitableEvent client1_can_reply(false, false);
+
+ Worker* worker;
+
+ worker = new MultipleServer2();
+ worker->OverrideThread(&worker_thread);
+ workers.push_back(worker);
+
+ worker = new MultipleClient2(
+ &client1_msg_received, &client1_can_reply, client_pump);
+ workers.push_back(worker);
+
+ worker = new MultipleServer1(server_pump);
+ worker->OverrideThread(&worker_thread);
+ workers.push_back(worker);
+
+ worker = new MultipleClient1(
+ &client1_msg_received, &client1_can_reply);
+ workers.push_back(worker);
+
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that multiple SyncObjects on the same listener thread can unblock each
+// other.
+TEST_F(IPCSyncChannelTest, Multiple) {
+ Multiple(false, false);
+ Multiple(false, true);
+ Multiple(true, false);
+ Multiple(true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class QueuedReplyServer1 : public Worker {
+ public:
+ QueuedReplyServer1(bool pump_during_send)
+ : Worker(L"test_channel1", Channel::MODE_SERVER),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ SendDouble(pump_during_send_, true);
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+class QueuedReplyClient1 : public Worker {
+ public:
+ QueuedReplyClient1(WaitableEvent* client1_msg_received,
+ WaitableEvent* server2_can_reply) :
+ Worker(L"test_channel1", Channel::MODE_CLIENT),
+ client1_msg_received_(client1_msg_received),
+ server2_can_reply_(server2_can_reply) { }
+
+ void OnDouble(int in, int* out) {
+ client1_msg_received_->Signal();
+ *out = in * 2;
+ server2_can_reply_->Wait();
+ Done();
+ }
+
+ private:
+ WaitableEvent *client1_msg_received_, *server2_can_reply_;
+};
+
+class QueuedReplyServer2 : public Worker {
+ public:
+ explicit QueuedReplyServer2(WaitableEvent* server2_can_reply) :
+ Worker(L"test_channel2", Channel::MODE_SERVER),
+ server2_can_reply_(server2_can_reply) { }
+
+ void OnAnswer(int* result) {
+ server2_can_reply_->Signal();
+
+ // give client1's reply time to reach the server listener thread
+ PlatformThread::Sleep(200);
+
+ *result = 42;
+ Done();
+ }
+
+ WaitableEvent *server2_can_reply_;
+};
+
+class QueuedReplyClient2 : public Worker {
+ public:
+ explicit QueuedReplyClient2(
+ WaitableEvent* client1_msg_received, bool pump_during_send)
+ : Worker(L"test_channel2", Channel::MODE_CLIENT),
+ client1_msg_received_(client1_msg_received),
+ pump_during_send_(pump_during_send){ }
+
+ void Run() {
+ client1_msg_received_->Wait();
+ SendAnswerToLife(pump_during_send_, base::kNoTimeout, true);
+ Done();
+ }
+
+ WaitableEvent *client1_msg_received_;
+ bool pump_during_send_;
+};
+
+void QueuedReply(bool server_pump, bool client_pump) {
+ std::vector<Worker*> workers;
+
+ // A shared worker thread so that server1 and server2 run on one thread.
+ base::Thread worker_thread("QueuedReply");
+ worker_thread.Start();
+
+ WaitableEvent client1_msg_received(false, false);
+ WaitableEvent server2_can_reply(false, false);
+
+ Worker* worker;
+
+ worker = new QueuedReplyServer2(&server2_can_reply);
+ worker->OverrideThread(&worker_thread);
+ workers.push_back(worker);
+
+ worker = new QueuedReplyClient2(&client1_msg_received, client_pump);
+ workers.push_back(worker);
+
+ worker = new QueuedReplyServer1(server_pump);
+ worker->OverrideThread(&worker_thread);
+ workers.push_back(worker);
+
+ worker = new QueuedReplyClient1(
+ &client1_msg_received, &server2_can_reply);
+ workers.push_back(worker);
+
+ RunTest(workers);
+}
+
+} // namespace
+
+// While a blocking send is in progress, the listener thread might answer other
+// synchronous messages. This tests that if during the response to another
+// message the reply to the original messages comes, it is queued up correctly
+// and the original Send is unblocked later.
+TEST_F(IPCSyncChannelTest, QueuedReply) {
+ QueuedReply(false, false);
+ QueuedReply(false, true);
+ QueuedReply(true, false);
+ QueuedReply(true, true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class BadServer : public Worker {
+ public:
+ BadServer(bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "simpler_server"),
+ pump_during_send_(pump_during_send) { }
+ void Run() {
+ int answer = 0;
+
+ SyncMessage* msg = new SyncMessage(
+ MSG_ROUTING_CONTROL, SyncChannelTestMsg_Double::ID,
+ Message::PRIORITY_NORMAL, NULL);
+ if (pump_during_send_)
+ msg->EnableMessagePumping();
+
+ // Temporarily set the minimum logging very high so that the assertion
+ // in ipc_message_utils doesn't fire.
+ int log_level = logging::GetMinLogLevel();
+ logging::SetMinLogLevel(kint32max);
+ bool result = Send(msg);
+ logging::SetMinLogLevel(log_level);
+ DCHECK(!result);
+
+ // Need to send another message to get the client to call Done().
+ result = Send(new SyncChannelTestMsg_AnswerToLife(&answer));
+ DCHECK(result);
+ DCHECK(answer == 42);
+
+ Done();
+ }
+
+ bool pump_during_send_;
+};
+
+void BadMessage(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ workers.push_back(new BadServer(pump_during_send));
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that if a message is not serialized correctly, the Send() will fail.
+TEST_F(IPCSyncChannelTest, BadMessage) {
+ BadMessage(false);
+ BadMessage(true);
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class ChattyClient : public Worker {
+ public:
+ ChattyClient() :
+ Worker(Channel::MODE_CLIENT, "chatty_client") { }
+
+ void OnAnswer(int* answer) {
+ // The PostMessage limit is 10k. Send 20% more than that.
+ const int kMessageLimit = 10000;
+ const int kMessagesToSend = kMessageLimit * 120 / 100;
+ for (int i = 0; i < kMessagesToSend; ++i) {
+ if (!SendDouble(false, true))
+ break;
+ }
+ *answer = 42;
+ Done();
+ }
+};
+
+void ChattyServer(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ workers.push_back(new UnblockServer(pump_during_send));
+ workers.push_back(new ChattyClient());
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests http://b/1093251 - that sending lots of sync messages while
+// the receiver is waiting for a sync reply does not overflow the PostMessage
+// queue.
+TEST_F(IPCSyncChannelTest, ChattyServer) {
+ ChattyServer(false);
+ ChattyServer(true);
+}
+
+//------------------------------------------------------------------------------
+
+namespace {
+
+class TimeoutServer : public Worker {
+ public:
+ TimeoutServer(int timeout_ms,
+ std::vector<bool> timeout_seq,
+ bool pump_during_send)
+ : Worker(Channel::MODE_SERVER, "timeout_server"),
+ timeout_ms_(timeout_ms),
+ timeout_seq_(timeout_seq),
+ pump_during_send_(pump_during_send) {
+ }
+
+ void Run() {
+ for (std::vector<bool>::const_iterator iter = timeout_seq_.begin();
+ iter != timeout_seq_.end(); ++iter) {
+ SendAnswerToLife(pump_during_send_, timeout_ms_, !*iter);
+ }
+ Done();
+ }
+
+ private:
+ int timeout_ms_;
+ std::vector<bool> timeout_seq_;
+ bool pump_during_send_;
+};
+
+class UnresponsiveClient : public Worker {
+ public:
+ UnresponsiveClient(std::vector<bool> timeout_seq)
+ : Worker(Channel::MODE_CLIENT, "unresponsive_client"),
+ timeout_seq_(timeout_seq) {
+ }
+
+ void OnAnswerDelay(Message* reply_msg) {
+ DCHECK(!timeout_seq_.empty());
+ if (!timeout_seq_[0]) {
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ } else {
+ // Don't reply.
+ delete reply_msg;
+ }
+ timeout_seq_.erase(timeout_seq_.begin());
+ if (timeout_seq_.empty())
+ Done();
+ }
+
+ private:
+ // Whether we should time-out or respond to the various messages we receive.
+ std::vector<bool> timeout_seq_;
+};
+
+void SendWithTimeoutOK(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(5000, timeout_seq, pump_during_send));
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
+
+void SendWithTimeoutTimeout(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(100, timeout_seq, pump_during_send));
+ workers.push_back(new UnresponsiveClient(timeout_seq));
+ RunTest(workers);
+}
+
+void SendWithTimeoutMixedOKAndTimeout(bool pump_during_send) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(100, timeout_seq, pump_during_send));
+ workers.push_back(new UnresponsiveClient(timeout_seq));
+ RunTest(workers);
+}
+
+} // namespace
+
+// Tests that SendWithTimeout does not time-out if the response comes back fast
+// enough.
+TEST_F(IPCSyncChannelTest, SendWithTimeoutOK) {
+ SendWithTimeoutOK(false);
+ SendWithTimeoutOK(true);
+}
+
+// Tests that SendWithTimeout does time-out.
+TEST_F(IPCSyncChannelTest, SendWithTimeoutTimeout) {
+ SendWithTimeoutTimeout(false);
+ SendWithTimeoutTimeout(true);
+}
+
+// Sends some message that time-out and some that succeed.
+TEST_F(IPCSyncChannelTest, SendWithTimeoutMixedOKAndTimeout) {
+ SendWithTimeoutMixedOKAndTimeout(false);
+ SendWithTimeoutMixedOKAndTimeout(true);
+}
+
+//------------------------------------------------------------------------------
+
+namespace {
+
+class NestedTask : public Task {
+ public:
+ NestedTask(Worker* server) : server_(server) { }
+ void Run() {
+ // Sleep a bit so that we wake up after the reply has been received.
+ PlatformThread::Sleep(250);
+ server_->SendAnswerToLife(true, base::kNoTimeout, true);
+ }
+
+ Worker* server_;
+};
+
+static bool timeout_occured = false;
+
+class TimeoutTask : public Task {
+ public:
+ void Run() {
+ timeout_occured = true;
+ }
+};
+
+class DoneEventRaceServer : public Worker {
+ public:
+ DoneEventRaceServer()
+ : Worker(Channel::MODE_SERVER, "done_event_race_server") { }
+
+ void Run() {
+ MessageLoop::current()->PostTask(FROM_HERE, new NestedTask(this));
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, new TimeoutTask(), 9000);
+ // Even though we have a timeout on the Send, it will succeed since for this
+ // bug, the reply message comes back and is deserialized, however the done
+ // event wasn't set. So we indirectly use the timeout task to notice if a
+ // timeout occurred.
+ SendAnswerToLife(true, 10000, true);
+ DCHECK(!timeout_occured);
+ Done();
+ }
+};
+
+} // namespace
+
+// Tests http://b/1474092 - that if after the done_event is set but before
+// OnObjectSignaled is called another message is sent out, then after its
+// reply comes back OnObjectSignaled will be called for the first message.
+TEST_F(IPCSyncChannelTest, DoneEventRace) {
+ std::vector<Worker*> workers;
+ workers.push_back(new DoneEventRaceServer());
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
diff --git a/chrome/common/ipc_sync_message.cc b/chrome/common/ipc_sync_message.cc
new file mode 100644
index 0000000..a7407e4
--- /dev/null
+++ b/chrome/common/ipc_sync_message.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#include <stack>
+
+#include "base/logging.h"
+#include "base/waitable_event.h"
+#include "chrome/common/ipc_sync_message.h"
+
+namespace IPC {
+
+uint32 SyncMessage::next_id_ = 0;
+#define kSyncMessageHeaderSize 4
+
+base::WaitableEvent* dummy_event = new base::WaitableEvent(true, true);
+
+SyncMessage::SyncMessage(
+ int32 routing_id,
+ uint16 type,
+ PriorityValue priority,
+ MessageReplyDeserializer* deserializer)
+ : Message(routing_id, type, priority),
+ deserializer_(deserializer),
+ pump_messages_event_(NULL)
+ {
+ set_sync();
+ set_unblock(true);
+
+ // Add synchronous message data before the message payload.
+ SyncHeader header;
+ header.message_id = ++next_id_;
+ WriteSyncHeader(this, header);
+}
+
+MessageReplyDeserializer* SyncMessage::GetReplyDeserializer() {
+ MessageReplyDeserializer* rv = deserializer_;
+ DCHECK(rv);
+ deserializer_ = NULL;
+ return rv;
+}
+
+void SyncMessage::EnableMessagePumping() {
+ DCHECK(!pump_messages_event_);
+ set_pump_messages_event(dummy_event);
+}
+
+bool SyncMessage::IsMessageReplyTo(const Message& msg, int request_id) {
+ if (!msg.is_reply())
+ return false;
+
+ return GetMessageId(msg) == request_id;
+}
+
+void* SyncMessage::GetDataIterator(const Message* msg) {
+ void* iter = const_cast<char*>(msg->payload());
+ UpdateIter(&iter, kSyncMessageHeaderSize);
+ return iter;
+}
+
+int SyncMessage::GetMessageId(const Message& msg) {
+ if (!msg.is_sync() && !msg.is_reply())
+ return 0;
+
+ SyncHeader header;
+ if (!ReadSyncHeader(msg, &header))
+ return 0;
+
+ return header.message_id;
+}
+
+Message* SyncMessage::GenerateReply(const Message* msg) {
+ DCHECK(msg->is_sync());
+
+ Message* reply = new Message(msg->routing_id(), IPC_REPLY_ID,
+ msg->priority());
+ reply->set_reply();
+
+ SyncHeader header;
+
+ // use the same message id, but this time reply bit is set
+ header.message_id = GetMessageId(*msg);
+ WriteSyncHeader(reply, header);
+
+ return reply;
+}
+
+bool SyncMessage::ReadSyncHeader(const Message& msg, SyncHeader* header) {
+ DCHECK(msg.is_sync() || msg.is_reply());
+
+ void* iter = NULL;
+ bool result = msg.ReadInt(&iter, &header->message_id);
+ if (!result) {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool SyncMessage::WriteSyncHeader(Message* msg, const SyncHeader& header) {
+ DCHECK(msg->is_sync() || msg->is_reply());
+ DCHECK(msg->payload_size() == 0);
+ bool result = msg->WriteInt(header.message_id);
+ if (!result) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Note: if you add anything here, you need to update kSyncMessageHeaderSize.
+ DCHECK(kSyncMessageHeaderSize == msg->payload_size());
+
+ return true;
+}
+
+
+bool MessageReplyDeserializer::SerializeOutputParameters(const Message& msg) {
+ return SerializeOutputParameters(msg, SyncMessage::GetDataIterator(&msg));
+}
+
+} // namespace IPC
diff --git a/chrome/common/ipc_sync_message.h b/chrome/common/ipc_sync_message.h
new file mode 100644
index 0000000..b03007e
--- /dev/null
+++ b/chrome/common/ipc_sync_message.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_SYNC_MESSAGE_H__
+#define CHROME_COMMON_IPC_SYNC_MESSAGE_H__
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#include <string>
+#include "base/basictypes.h"
+#include "chrome/common/ipc_message.h"
+
+namespace base {
+class WaitableEvent;
+}
+
+namespace IPC {
+
+class MessageReplyDeserializer;
+
+class SyncMessage : public Message {
+ public:
+ SyncMessage(int32 routing_id, uint16 type, PriorityValue priority,
+ MessageReplyDeserializer* deserializer);
+
+ // Call this to get a deserializer for the output parameters.
+ // Note that this can only be called once, and the caller is responsible
+ // for deleting the deserializer when they're done.
+ MessageReplyDeserializer* GetReplyDeserializer();
+
+ // If this message can cause the receiver to block while waiting for user
+ // input (i.e. by calling MessageBox), then the caller needs to pump window
+ // messages and dispatch asynchronous messages while waiting for the reply.
+ // If this event is passed in, then window messages will start being pumped
+ // when it's set. Note that this behavior will continue even if the event is
+ // later reset. The event must be valid until after the Send call returns.
+ void set_pump_messages_event(base::WaitableEvent* event) {
+ pump_messages_event_ = event;
+ if (event) {
+ header()->flags |= PUMPING_MSGS_BIT;
+ } else {
+ header()->flags &= ~PUMPING_MSGS_BIT;
+ }
+ }
+
+ // Call this if you always want to pump messages. You can call this method
+ // or set_pump_messages_event but not both.
+ void EnableMessagePumping();
+
+ base::WaitableEvent* pump_messages_event() const {
+ return pump_messages_event_;
+ }
+
+ // Returns true if the message is a reply to the given request id.
+ static bool IsMessageReplyTo(const Message& msg, int request_id);
+
+ // Given a reply message, returns an iterator to the beginning of the data
+ // (i.e. skips over the synchronous specific data).
+ static void* GetDataIterator(const Message* msg);
+
+ // Given a synchronous message (or its reply), returns its id.
+ static int GetMessageId(const Message& msg);
+
+ // Generates a reply message to the given message.
+ static Message* GenerateReply(const Message* msg);
+
+ private:
+ struct SyncHeader {
+ // unique ID (unique per sender)
+ int message_id;
+ };
+
+ static bool ReadSyncHeader(const Message& msg, SyncHeader* header);
+ static bool WriteSyncHeader(Message* msg, const SyncHeader& header);
+
+ MessageReplyDeserializer* deserializer_;
+ base::WaitableEvent* pump_messages_event_;
+
+ static uint32 next_id_; // for generation of unique ids
+};
+
+// Used to deserialize parameters from a reply to a synchronous message
+class MessageReplyDeserializer {
+ public:
+ bool SerializeOutputParameters(const Message& msg);
+ private:
+ // Derived classes need to implement this, using the given iterator (which
+ // is skipped past the header for synchronous messages).
+ virtual bool SerializeOutputParameters(const Message& msg, void* iter) = 0;
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_SYNC_MESSAGE_H__
diff --git a/chrome/common/ipc_sync_message_unittest.cc b/chrome/common/ipc_sync_message_unittest.cc
new file mode 100644
index 0000000..b4f86a2
--- /dev/null
+++ b/chrome/common/ipc_sync_message_unittest.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit test to make sure that the serialization of synchronous IPC messages
+// works. This ensures that the macros and templates were defined correctly.
+// Doesn't test the IPC channel mechanism.
+
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "chrome/common/ipc_message.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+#define MESSAGES_INTERNAL_FILE "chrome/common/ipc_sync_message_unittest.h"
+#include "chrome/common/ipc_message_macros.h"
+
+static IPC::Message* g_reply;
+
+class TestMessageReceiver {
+ public:
+
+ void On_0_1(bool* out1) {
+ *out1 = false;
+ }
+
+ void On_0_2(bool* out1, int* out2) {
+ *out1 = true;
+ *out2 = 2;
+ }
+
+ void On_0_3(bool* out1, int* out2, std::string* out3) {
+ *out1 = false;
+ *out2 = 3;
+ *out3 = "0_3";
+ }
+
+ void On_1_1(int in1, bool* out1) {
+ DCHECK(in1 == 1);
+ *out1 = true;
+ }
+
+ void On_1_2(bool in1, bool* out1, int* out2) {
+ DCHECK(!in1);
+ *out1 = true;
+ *out2 = 12;
+ }
+
+ void On_1_3(int in1, std::string* out1, int* out2, bool* out3) {
+ DCHECK(in1 == 3);
+ *out1 = "1_3";
+ *out2 = 13;
+ *out3 = false;
+ }
+
+ void On_2_1(int in1, bool in2, bool* out1) {
+ DCHECK(in1 == 1 && !in2);
+ *out1 = true;
+ }
+
+ void On_2_2(bool in1, int in2, bool* out1, int* out2) {
+ DCHECK(!in1 && in2 == 2);
+ *out1 = true;
+ *out2 = 22;
+ }
+
+ void On_2_3(int in1, bool in2, std::string* out1, int* out2, bool* out3) {
+ DCHECK(in1 == 3 && in2);
+ *out1 = "2_3";
+ *out2 = 23;
+ *out3 = false;
+ }
+
+ void On_3_1(int in1, bool in2, std::string in3, bool* out1) {
+ DCHECK(in1 == 1 && !in2 && in3 == "3_1");
+ *out1 = true;
+ }
+
+ void On_3_2(std::string in1, bool in2, int in3, bool* out1, int* out2) {
+ DCHECK(in1 == "3_2" && !in2 && in3 == 2);
+ *out1 = true;
+ *out2 = 32;
+ }
+
+ void On_3_3(int in1, std::string in2, bool in3, std::string* out1, int* out2,
+ bool* out3) {
+ DCHECK(in1 == 3 && in2 == "3_3" && in3);
+ *out1 = "3_3";
+ *out2 = 33;
+ *out3 = false;
+ }
+
+ bool Send(IPC::Message* message) {
+ // gets the reply message, stash in global
+ DCHECK(g_reply == NULL);
+ g_reply = message;
+ return true;
+ }
+
+ void OnMessageReceived(const IPC::Message& msg) {
+ IPC_BEGIN_MESSAGE_MAP(TestMessageReceiver, msg)
+ IPC_MESSAGE_HANDLER(Msg_C_0_1, On_0_1)
+ IPC_MESSAGE_HANDLER(Msg_C_0_2, On_0_2)
+ IPC_MESSAGE_HANDLER(Msg_C_0_3, On_0_3)
+ IPC_MESSAGE_HANDLER(Msg_C_1_1, On_1_1)
+ IPC_MESSAGE_HANDLER(Msg_C_1_2, On_1_2)
+ IPC_MESSAGE_HANDLER(Msg_C_1_3, On_1_3)
+ IPC_MESSAGE_HANDLER(Msg_C_2_1, On_2_1)
+ IPC_MESSAGE_HANDLER(Msg_C_2_2, On_2_2)
+ IPC_MESSAGE_HANDLER(Msg_C_2_3, On_2_3)
+ IPC_MESSAGE_HANDLER(Msg_C_3_1, On_3_1)
+ IPC_MESSAGE_HANDLER(Msg_C_3_2, On_3_2)
+ IPC_MESSAGE_HANDLER(Msg_C_3_3, On_3_3)
+ IPC_MESSAGE_HANDLER(Msg_R_0_1, On_0_1)
+ IPC_MESSAGE_HANDLER(Msg_R_0_2, On_0_2)
+ IPC_MESSAGE_HANDLER(Msg_R_0_3, On_0_3)
+ IPC_MESSAGE_HANDLER(Msg_R_1_1, On_1_1)
+ IPC_MESSAGE_HANDLER(Msg_R_1_2, On_1_2)
+ IPC_MESSAGE_HANDLER(Msg_R_1_3, On_1_3)
+ IPC_MESSAGE_HANDLER(Msg_R_2_1, On_2_1)
+ IPC_MESSAGE_HANDLER(Msg_R_2_2, On_2_2)
+ IPC_MESSAGE_HANDLER(Msg_R_2_3, On_2_3)
+ IPC_MESSAGE_HANDLER(Msg_R_3_1, On_3_1)
+ IPC_MESSAGE_HANDLER(Msg_R_3_2, On_3_2)
+ IPC_MESSAGE_HANDLER(Msg_R_3_3, On_3_3)
+ IPC_END_MESSAGE_MAP()
+ }
+
+};
+
+void Send(IPC::SyncMessage* msg) {
+ static TestMessageReceiver receiver;
+
+ IPC::MessageReplyDeserializer* reply_serializer = msg->GetReplyDeserializer();
+ DCHECK(reply_serializer != NULL);
+
+ // "send" the message
+ receiver.OnMessageReceived(*msg);
+ delete msg;
+
+ // get the reply message from the global, and deserialize the output
+ // parameters into the output pointers.
+ DCHECK(g_reply != NULL);
+ bool result = reply_serializer->SerializeOutputParameters(*g_reply);
+ DCHECK(result);
+ delete g_reply;
+ g_reply = NULL;
+ delete reply_serializer;
+}
+
+TEST(IPCSyncMessageTest, Main) {
+ bool bool1 = true;
+ int int1 = 0;
+ std::string string1;
+
+ Send(new Msg_C_0_1(&bool1));
+ DCHECK(!bool1);
+
+ Send(new Msg_C_0_2(&bool1, &int1));
+ DCHECK(bool1 && int1 == 2);
+
+ Send(new Msg_C_0_3(&bool1, &int1, &string1));
+ DCHECK(!bool1 && int1 == 3 && string1 == "0_3");
+
+ bool1 = false;
+ Send(new Msg_C_1_1(1, &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_C_1_2(false, &bool1, &int1));
+ DCHECK(bool1 && int1 == 12);
+
+ bool1 = true;
+ Send(new Msg_C_1_3(3, &string1, &int1, &bool1));
+ DCHECK(string1 == "1_3" && int1 == 13 && !bool1);
+
+ bool1 = false;
+ Send(new Msg_C_2_1(1, false, &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_C_2_2(false, 2, &bool1, &int1));
+ DCHECK(bool1 && int1 == 22);
+
+ bool1 = true;
+ Send(new Msg_C_2_3(3, true, &string1, &int1, &bool1));
+ DCHECK(string1 == "2_3" && int1 == 23 && !bool1);
+
+ bool1 = false;
+ Send(new Msg_C_3_1(1, false, "3_1", &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_C_3_2("3_2", false, 2, &bool1, &int1));
+ DCHECK(bool1 && int1 == 32);
+
+ bool1 = true;
+ Send(new Msg_C_3_3(3, "3_3", true, &string1, &int1, &bool1));
+ DCHECK(string1 == "3_3" && int1 == 33 && !bool1);
+
+ // Routed messages, just a copy of the above but with extra routing paramater
+ Send(new Msg_R_0_1(0, &bool1));
+ DCHECK(!bool1);
+
+ Send(new Msg_R_0_2(0, &bool1, &int1));
+ DCHECK(bool1 && int1 == 2);
+
+ Send(new Msg_R_0_3(0, &bool1, &int1, &string1));
+ DCHECK(!bool1 && int1 == 3 && string1 == "0_3");
+
+ bool1 = false;
+ Send(new Msg_R_1_1(0, 1, &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_R_1_2(0, false, &bool1, &int1));
+ DCHECK(bool1 && int1 == 12);
+
+ bool1 = true;
+ Send(new Msg_R_1_3(0, 3, &string1, &int1, &bool1));
+ DCHECK(string1 == "1_3" && int1 == 13 && !bool1);
+
+ bool1 = false;
+ Send(new Msg_R_2_1(0, 1, false, &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_R_2_2(0, false, 2, &bool1, &int1));
+ DCHECK(bool1 && int1 == 22);
+
+ bool1 = true;
+ Send(new Msg_R_2_3(0, 3, true, &string1, &int1, &bool1));
+ DCHECK(string1 == "2_3" && int1 == 23 && !bool1);
+
+ bool1 = false;
+ Send(new Msg_R_3_1(0, 1, false, "3_1", &bool1));
+ DCHECK(bool1);
+
+ bool1 = false;
+ Send(new Msg_R_3_2(0, "3_2", false, 2, &bool1, &int1));
+ DCHECK(bool1 && int1 == 32);
+
+ bool1 = true;
+ Send(new Msg_R_3_3(0, 3, "3_3", true, &string1, &int1, &bool1));
+ DCHECK(string1 == "3_3" && int1 == 33 && !bool1);
+}
diff --git a/chrome/common/ipc_sync_message_unittest.h b/chrome/common/ipc_sync_message_unittest.h
new file mode 100644
index 0000000..4ac9099
--- /dev/null
+++ b/chrome/common/ipc_sync_message_unittest.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/ipc_message_macros.h"
+
+IPC_BEGIN_MESSAGES(Test)
+ IPC_SYNC_MESSAGE_CONTROL0_0(SyncChannelTestMsg_NoArgs)
+
+ IPC_SYNC_MESSAGE_CONTROL0_1(SyncChannelTestMsg_AnswerToLife,
+ int /* answer */)
+
+ IPC_SYNC_MESSAGE_CONTROL1_1(SyncChannelTestMsg_Double,
+ int /* in */,
+ int /* out */)
+
+ // out1 is false
+ IPC_SYNC_MESSAGE_CONTROL0_1(Msg_C_0_1, bool)
+
+ // out1 is true, out2 is 2
+ IPC_SYNC_MESSAGE_CONTROL0_2(Msg_C_0_2, bool, int)
+
+ // out1 is false, out2 is 3, out3 is "0_3"
+ IPC_SYNC_MESSAGE_CONTROL0_3(Msg_C_0_3, bool, int, std::string)
+
+ // in1 must be 1, out1 is true
+ IPC_SYNC_MESSAGE_CONTROL1_1(Msg_C_1_1, int, bool)
+
+ // in1 must be false, out1 is true, out2 is 12
+ IPC_SYNC_MESSAGE_CONTROL1_2(Msg_C_1_2, bool, bool, int)
+
+ // in1 must be 3, out1 is "1_3", out2 is 13, out3 is false
+ IPC_SYNC_MESSAGE_CONTROL1_3(Msg_C_1_3, int, std::string, int, bool)
+
+ // in1 must be 1, in2 must be false, out1 is true
+ IPC_SYNC_MESSAGE_CONTROL2_1(Msg_C_2_1, int, bool, bool)
+
+ // in1 must be false, in2 must be 2, out1 is true, out2 is 22
+ IPC_SYNC_MESSAGE_CONTROL2_2(Msg_C_2_2, bool, int, bool, int)
+
+ // in1 must be 3, in2 must be true, out1 is "2_3", out2 is 23, out3 is false
+ IPC_SYNC_MESSAGE_CONTROL2_3(Msg_C_2_3, int, bool, std::string, int, bool)
+
+ // in1 must be 1, in2 must be false, in3 must be "3_1", out1 is true
+ IPC_SYNC_MESSAGE_CONTROL3_1(Msg_C_3_1, int, bool, std::string, bool)
+
+ // in1 must be "3_3", in2 must be false, in3 must be 2, out1 is true, out2 is
+ // 32
+ IPC_SYNC_MESSAGE_CONTROL3_2(Msg_C_3_2, std::string, bool, int, bool, int)
+
+ // in1 must be 3, in2 must be "3_3", in3 must be true, out1 is "3_3", out2 is
+ // 33, out3 is false
+ IPC_SYNC_MESSAGE_CONTROL3_3(Msg_C_3_3, int, std::string, bool, std::string,
+ int, bool)
+
+
+ // NOTE: routed messages are just a copy of the above...
+
+ // out1 is false
+ IPC_SYNC_MESSAGE_ROUTED0_1(Msg_R_0_1, bool)
+
+ // out1 is true, out2 is 2
+ IPC_SYNC_MESSAGE_ROUTED0_2(Msg_R_0_2, bool, int)
+
+ // out1 is false, out2 is 3, out3 is "0_3"
+ IPC_SYNC_MESSAGE_ROUTED0_3(Msg_R_0_3, bool, int, std::string)
+
+ // in1 must be 1, out1 is true
+ IPC_SYNC_MESSAGE_ROUTED1_1(Msg_R_1_1, int, bool)
+
+ // in1 must be false, out1 is true, out2 is 12
+ IPC_SYNC_MESSAGE_ROUTED1_2(Msg_R_1_2, bool, bool, int)
+
+ // in1 must be 3, out1 is "1_3", out2 is 13, out3 is false
+ IPC_SYNC_MESSAGE_ROUTED1_3(Msg_R_1_3, int, std::string, int, bool)
+
+ // in1 must be 1, in2 must be false, out1 is true
+ IPC_SYNC_MESSAGE_ROUTED2_1(Msg_R_2_1, int, bool, bool)
+
+ // in1 must be false, in2 must be 2, out1 is true, out2 is 22
+ IPC_SYNC_MESSAGE_ROUTED2_2(Msg_R_2_2, bool, int, bool, int)
+
+ // in1 must be 3, in2 must be true, out1 is "2_3", out2 is 23, out3 is false
+ IPC_SYNC_MESSAGE_ROUTED2_3(Msg_R_2_3, int, bool, std::string, int, bool)
+
+ // in1 must be 1, in2 must be false, in3 must be "3_1", out1 is true
+ IPC_SYNC_MESSAGE_ROUTED3_1(Msg_R_3_1, int, bool, std::string, bool)
+
+ // in1 must be "3_3", in2 must be false, in3 must be 2, out1 is true, out2
+ // is 32
+ IPC_SYNC_MESSAGE_ROUTED3_2(Msg_R_3_2, std::string, bool, int, bool, int)
+
+ // in1 must be 3, in2 must be "3_3", in3 must be true, out1 is "3_3", out2 is
+ // 33, out3 is false
+ IPC_SYNC_MESSAGE_ROUTED3_3(Msg_R_3_3, int, std::string, bool, std::string,
+ int, bool)
+
+IPC_END_MESSAGES(TestMsg)
diff --git a/chrome/common/ipc_test_sink.cc b/chrome/common/ipc_test_sink.cc
new file mode 100644
index 0000000..c278902
--- /dev/null
+++ b/chrome/common/ipc_test_sink.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/ipc_test_sink.h"
+
+namespace IPC {
+
+TestSink::TestSink() {
+}
+
+TestSink::~TestSink() {
+}
+
+void TestSink::OnMessageReceived(const Message& msg) {
+ messages_.push_back(Message(msg));
+}
+
+void TestSink::ClearMessages() {
+ messages_.clear();
+}
+
+const Message* TestSink::GetMessageAt(size_t index) const {
+ if (index >= messages_.size())
+ return NULL;
+ return &messages_[index];
+}
+
+const Message* TestSink::GetFirstMessageMatching(uint16 id) const {
+ for (size_t i = 0; i < messages_.size(); i++) {
+ if (messages_[i].type() == id)
+ return &messages_[i];
+ }
+ return NULL;
+}
+
+const Message* TestSink::GetUniqueMessageMatching(uint16 id) const {
+ size_t found_index = 0;
+ size_t found_count = 0;
+ for (size_t i = 0; i < messages_.size(); i++) {
+ if (messages_[i].type() == id) {
+ found_count++;
+ found_index = i;
+ }
+ }
+ if (found_count != 1)
+ return NULL; // Didn't find a unique one.
+ return &messages_[found_index];
+}
+
+} // namespace IPC
diff --git a/chrome/common/ipc_test_sink.h b/chrome/common/ipc_test_sink.h
new file mode 100644
index 0000000..fd9faee
--- /dev/null
+++ b/chrome/common/ipc_test_sink.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_TEST_SINK_H_
+#define CHROME_COMMON_IPC_TEST_SINK_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "chrome/common/ipc_message.h"
+
+namespace IPC {
+
+// This test sink provides a "sink" for IPC messages that are sent. It allows
+// the caller to query messages received in various different ways. It is
+// designed for tests for objects that use the IPC system.
+//
+// Typical usage:
+//
+// test_sink.ClearMessages();
+// do_something();
+//
+// // We should have gotten exactly one update state message.
+// EXPECT_TRUE(test_sink.GetUniqeMessageMatching(ViewHostMsg_Update::ID));
+// // ...and no start load messages.
+// EXPECT_FALSE(test_sink.GetFirstMessageMatching(ViewHostMsg_Start::ID));
+//
+// // Now inspect a message. This assumes a message that was declared like
+// // this: IPC_MESSAGE_ROUTED2(ViewMsg_Foo, bool, int)
+// IPC::Message* msg = test_sink.GetFirstMessageMatching(ViewMsg_Foo::ID));
+// ASSERT_TRUE(msg);
+// bool first_param;
+// int second_param;
+// ViewMsg_Foo::Read(msg, &first_param, &second_param);
+//
+// // Go on to the next phase of the test.
+// test_sink.ClearMessages();
+//
+// To hook up the sink, all you need to do is call OnMessageReceived when a
+// message is recieved.
+class TestSink {
+ public:
+ TestSink();
+ ~TestSink();
+
+ // Used by the source of the messages to send the message to the sink. This
+ // will make a copy of the message and store it in the list.
+ void OnMessageReceived(const Message& msg);
+
+ // Returns the number of messages in the queue.
+ size_t message_count() const { return messages_.size(); }
+
+ // Clears the message queue of saved messages.
+ void ClearMessages();
+
+ // Returns the message at the given index in the queue. The index may be out
+ // of range, in which case the return value is NULL. The returned pointer will
+ // only be valid until another message is received or the list is cleared.
+ const Message* GetMessageAt(size_t index) const;
+
+ // Returns the first message with the given ID in the queue. If there is no
+ // message with the given ID, returns NULL. The returned pointer will only be
+ // valid until another message is received or the list is cleared.
+ const Message* GetFirstMessageMatching(uint16 id) const;
+
+ // Returns the message with the given ID in the queue. If there is no such
+ // message or there is more than one of that message, this will return NULL
+ // (with the expectation that you'll do an ASSERT_TRUE() on the result).
+ // The returned pointer will only be valid until another message is received
+ // or the list is cleared.
+ const Message* GetUniqueMessageMatching(uint16 id) const;
+
+ private:
+ // The actual list of received messages.
+ std::vector<Message> messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestSink);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_TEST_SINK_H_
diff --git a/chrome/common/ipc_tests.cc b/chrome/common/ipc_tests.cc
new file mode 100644
index 0000000..ae88659
--- /dev/null
+++ b/chrome/common/ipc_tests.cc
@@ -0,0 +1,481 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <iostream>
+#include <string>
+
+#include "chrome/common/ipc_tests.h"
+
+#include "base/at_exit.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug_on_start.h"
+#include "base/perftimer.h"
+#include "base/perf_test_suite.h"
+#include "base/test_suite.h"
+#include "base/thread.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/ipc_channel.h"
+#include "chrome/common/ipc_channel_proxy.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "testing/multiprocess_func_list.h"
+
+// Define to enable IPC performance testing instead of the regular unit tests
+// #define PERFORMANCE_TEST
+
+const wchar_t kTestClientChannel[] = L"T1";
+const wchar_t kReflectorChannel[] = L"T2";
+const wchar_t kFuzzerChannel[] = L"F3";
+
+const size_t kLongMessageStringNumBytes = 50000;
+
+#ifndef PERFORMANCE_TEST
+
+void IPCChannelTest::SetUp() {
+ MultiProcessTest::SetUp();
+
+ // Construct a fresh IO Message loop for the duration of each test.
+ message_loop_ = new MessageLoopForIO();
+}
+
+void IPCChannelTest::TearDown() {
+ delete message_loop_;
+ message_loop_ = NULL;
+
+ MultiProcessTest::TearDown();
+}
+
+#if defined(OS_WIN)
+base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
+ IPC::Channel *channel) {
+ // kDebugChildren support.
+ bool debug_on_start =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
+
+ switch (child_type) {
+ case TEST_CLIENT:
+ return MultiProcessTest::SpawnChild(L"RunTestClient", debug_on_start);
+ break;
+ case TEST_REFLECTOR:
+ return MultiProcessTest::SpawnChild(L"RunReflector", debug_on_start);
+ break;
+ case FUZZER_SERVER:
+ return MultiProcessTest::SpawnChild(L"RunFuzzServer", debug_on_start);
+ break;
+ default:
+ return NULL;
+ break;
+ }
+}
+#elif defined(OS_POSIX)
+base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
+ IPC::Channel *channel) {
+ // kDebugChildren support.
+ bool debug_on_start =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
+
+ base::file_handle_mapping_vector fds_to_map;
+ int src_fd;
+ int dest_fd;
+ channel->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
+ if (src_fd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(src_fd, dest_fd));
+ }
+
+ base::ProcessHandle ret = NULL;
+ switch (child_type) {
+ case TEST_CLIENT:
+ ret = MultiProcessTest::SpawnChild(L"RunTestClient",
+ fds_to_map,
+ debug_on_start);
+ channel->OnClientConnected();
+ break;
+ case TEST_DESCRIPTOR_CLIENT:
+ ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClient",
+ fds_to_map,
+ debug_on_start);
+ channel->OnClientConnected();
+ break;
+ case TEST_DESCRIPTOR_CLIENT_SANDBOXED:
+ ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClientSandboxed",
+ fds_to_map,
+ debug_on_start);
+ channel->OnClientConnected();
+ break;
+ case TEST_REFLECTOR:
+ ret = MultiProcessTest::SpawnChild(L"RunReflector",
+ fds_to_map,
+ debug_on_start);
+ channel->OnClientConnected();
+ break;
+ case FUZZER_SERVER:
+ ret = MultiProcessTest::SpawnChild(L"RunFuzzServer",
+ fds_to_map,
+ debug_on_start);
+ channel->OnClientConnected();
+ break;
+ default:
+ return NULL;
+ break;
+ }
+ return ret;
+}
+#endif // defined(OS_POSIX)
+
+TEST_F(IPCChannelTest, BasicMessageTest) {
+ int v1 = 10;
+ std::string v2("foobar");
+ std::wstring v3(L"hello world");
+
+ IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL);
+ EXPECT_TRUE(m.WriteInt(v1));
+ EXPECT_TRUE(m.WriteString(v2));
+ EXPECT_TRUE(m.WriteWString(v3));
+
+ void* iter = NULL;
+
+ int vi;
+ std::string vs;
+ std::wstring vw;
+
+ EXPECT_TRUE(m.ReadInt(&iter, &vi));
+ EXPECT_EQ(v1, vi);
+
+ EXPECT_TRUE(m.ReadString(&iter, &vs));
+ EXPECT_EQ(v2, vs);
+
+ EXPECT_TRUE(m.ReadWString(&iter, &vw));
+ EXPECT_EQ(v3, vw);
+
+ // should fail
+ EXPECT_FALSE(m.ReadInt(&iter, &vi));
+ EXPECT_FALSE(m.ReadString(&iter, &vs));
+ EXPECT_FALSE(m.ReadWString(&iter, &vw));
+}
+
+static void Send(IPC::Message::Sender* sender, const char* text) {
+ static int message_index = 0;
+
+ IPC::Message* message = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ message->WriteInt(message_index++);
+ message->WriteString(std::string(text));
+
+ // Make sure we can handle large messages.
+ char junk[kLongMessageStringNumBytes];
+ memset(junk, 'a', sizeof(junk)-1);
+ junk[sizeof(junk)-1] = 0;
+ message->WriteString(std::string(junk));
+
+ // DEBUG: printf("[%u] sending message [%s]\n", GetCurrentProcessId(), text);
+ sender->Send(message);
+}
+
+class MyChannelListener : public IPC::Channel::Listener {
+ public:
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ IPC::MessageIterator iter(message);
+
+ iter.NextInt();
+ const std::string data = iter.NextString();
+ const std::string big_string = iter.NextString();
+ EXPECT_EQ(kLongMessageStringNumBytes - 1, big_string.length());
+
+
+ if (--messages_left_ == 0) {
+ MessageLoop::current()->Quit();
+ } else {
+ Send(sender_, "Foo");
+ }
+ }
+
+ virtual void OnChannelError() {
+ // There is a race when closing the channel so the last message may be lost.
+ EXPECT_LE(messages_left_, 1);
+ MessageLoop::current()->Quit();
+ }
+
+ void Init(IPC::Message::Sender* s) {
+ sender_ = s;
+ messages_left_ = 50;
+ }
+
+ private:
+ IPC::Message::Sender* sender_;
+ int messages_left_;
+};
+
+TEST_F(IPCChannelTest, ChannelTest) {
+ MyChannelListener channel_listener;
+ // Setup IPC channel.
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &channel_listener);
+ chan.Connect();
+
+ channel_listener.Init(&chan);
+
+ base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, &chan);
+ ASSERT_TRUE(process_handle);
+
+ Send(&chan, "hello from parent");
+
+ // Run message loop.
+ MessageLoop::current()->Run();
+
+ // Close Channel so client gets its OnChannelError() callback fired.
+ chan.Close();
+
+ // Cleanup child process.
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
+ base::CloseProcessHandle(process_handle);
+}
+
+TEST_F(IPCChannelTest, ChannelProxyTest) {
+ MyChannelListener channel_listener;
+
+ // The thread needs to out-live the ChannelProxy.
+ base::Thread thread("ChannelProxyTestServer");
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ thread.StartWithOptions(options);
+ {
+ // setup IPC channel proxy
+ IPC::ChannelProxy chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
+ &channel_listener, NULL, thread.message_loop());
+
+ channel_listener.Init(&chan);
+
+#if defined(OS_WIN)
+ base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, NULL);
+#elif defined(OS_POSIX)
+ bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDebugChildren);
+ base::file_handle_mapping_vector fds_to_map;
+ int src_fd;
+ int dest_fd;
+ chan.GetClientFileDescriptorMapping(&src_fd, &dest_fd);
+ if (src_fd > -1) {
+ fds_to_map.push_back(std::pair<int,int>(src_fd, dest_fd));
+ }
+
+ base::ProcessHandle process_handle = MultiProcessTest::SpawnChild(
+ L"RunTestClient",
+ fds_to_map,
+ debug_on_start);
+ chan.OnClientConnected();
+#endif // defined(OS_POXIX)
+
+ ASSERT_TRUE(process_handle);
+
+ Send(&chan, "hello from parent");
+
+ // run message loop
+ MessageLoop::current()->Run();
+
+ // cleanup child process
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000));
+ base::CloseProcessHandle(process_handle);
+ }
+ thread.Stop();
+}
+
+MULTIPROCESS_TEST_MAIN(RunTestClient) {
+ MessageLoopForIO main_message_loop;
+ MyChannelListener channel_listener;
+
+ // setup IPC channel
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT,
+ &channel_listener);
+ chan.Connect();
+ channel_listener.Init(&chan);
+ Send(&chan, "hello from child");
+ // run message loop
+ MessageLoop::current()->Run();
+ // return true;
+ return NULL;
+}
+
+#endif // !PERFORMANCE_TEST
+
+#ifdef PERFORMANCE_TEST
+
+//-----------------------------------------------------------------------------
+// Manually performance test
+//
+// This test times the roundtrip IPC message cycle. It is enabled with a
+// special preprocessor define to enable it instead of the standard IPC
+// unit tests. This works around some funny termination conditions in the
+// regular unit tests.
+//
+// This test is not automated. To test, you will want to vary the message
+// count and message size in TEST to get the numbers you want.
+//
+// FIXME(brettw): Automate this test and have it run by default.
+
+// This channel listener just replies to all messages with the exact same
+// message. It assumes each message has one string parameter. When the string
+// "quit" is sent, it will exit.
+class ChannelReflectorListener : public IPC::Channel::Listener {
+ public:
+ explicit ChannelReflectorListener(IPC::Channel *channel) :
+ channel_(channel),
+ count_messages_(0),
+ latency_messages_(0) {
+ std::cout << "Reflector up" << std::endl;
+ }
+
+ ~ChannelReflectorListener() {
+ std::cout << "Client Messages: " << count_messages_ << std::endl;
+ std::cout << "Client Latency: " << latency_messages_ << std::endl;
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ count_messages_++;
+ IPC::MessageIterator iter(message);
+ int time = iter.NextInt();
+ int msgid = iter.NextInt();
+ std::string payload = iter.NextString();
+ latency_messages_ += GetTickCount() - time;
+
+ // cout << "reflector msg received: " << msgid << endl;
+ if (payload == "quit")
+ MessageLoop::current()->Quit();
+
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(msgid);
+ msg->WriteString(payload);
+ channel_->Send(msg);
+ }
+ private:
+ IPC::Channel *channel_;
+ int count_messages_;
+ int latency_messages_;
+};
+
+class ChannelPerfListener : public IPC::Channel::Listener {
+ public:
+ ChannelPerfListener(IPC::Channel* channel, int msg_count, int msg_size) :
+ count_down_(msg_count),
+ channel_(channel),
+ count_messages_(0),
+ latency_messages_(0) {
+ payload_.resize(msg_size);
+ for (int i = 0; i < static_cast<int>(payload_.size()); i++)
+ payload_[i] = 'a';
+ std::cout << "perflistener up" << std::endl;
+ }
+
+ ~ChannelPerfListener() {
+ std::cout << "Server Messages: " << count_messages_ << std::endl;
+ std::cout << "Server Latency: " << latency_messages_ << std::endl;
+ }
+
+ virtual void OnMessageReceived(const IPC::Message& message) {
+ count_messages_++;
+ // decode the string so this gets counted in the total time
+ IPC::MessageIterator iter(message);
+ int time = iter.NextInt();
+ int msgid = iter.NextInt();
+ std::string cur = iter.NextString();
+ latency_messages_ += GetTickCount() - time;
+
+ // cout << "perflistener got message" << endl;
+
+ count_down_--;
+ if (count_down_ == 0) {
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(count_down_);
+ msg->WriteString("quit");
+ channel_->Send(msg);
+ SetTimer(NULL, 1, 250, (TIMERPROC) PostQuitMessage);
+ return;
+ }
+
+ IPC::Message* msg = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ msg->WriteInt(GetTickCount());
+ msg->WriteInt(count_down_);
+ msg->WriteString(payload_);
+ channel_->Send(msg);
+ }
+
+ private:
+ int count_down_;
+ std::string payload_;
+ IPC::Channel *channel_;
+ int count_messages_;
+ int latency_messages_;
+};
+
+TEST_F(IPCChannelTest, Performance) {
+ // setup IPC channel
+ IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_SERVER, NULL);
+ ChannelPerfListener perf_listener(&chan, 10000, 100000);
+ chan.set_listener(&perf_listener);
+ chan.Connect();
+
+ HANDLE process = SpawnChild(TEST_REFLECTOR, &chan);
+ ASSERT_TRUE(process);
+
+ Sleep(1000);
+
+ PerfTimeLogger logger("IPC_Perf");
+
+ // this initial message will kick-start the ping-pong of messages
+ IPC::Message* message = new IPC::Message(0,
+ 2,
+ IPC::Message::PRIORITY_NORMAL);
+ message->WriteInt(GetTickCount());
+ message->WriteInt(-1);
+ message->WriteString("Hello");
+ chan.Send(message);
+
+ // run message loop
+ MessageLoop::current()->Run();
+
+ // cleanup child process
+ WaitForSingleObject(process, 5000);
+ CloseHandle(process);
+}
+
+// This message loop bounces all messages back to the sender
+MULTIPROCESS_TEST_MAIN(RunReflector) {
+ MessageLoopForIO main_message_loop;
+ IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_CLIENT, NULL);
+ ChannelReflectorListener channel_reflector_listener(&chan);
+ chan.set_listener(&channel_reflector_listener);
+ chan.Connect();
+
+ MessageLoop::current()->Run();
+ return true;
+}
+
+#endif // PERFORMANCE_TEST
+
+int main(int argc, char** argv) {
+#ifdef PERFORMANCE_TEST
+ int retval = PerfTestSuite(argc, argv).Run();
+#else
+ int retval = TestSuite(argc, argv).Run();
+#endif
+ return retval;
+}
diff --git a/chrome/common/ipc_tests.h b/chrome/common/ipc_tests.h
new file mode 100644
index 0000000..3cefafe
--- /dev/null
+++ b/chrome/common/ipc_tests.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_IPC_TESTS_H__
+#define CHROME_COMMON_IPC_TESTS_H__
+
+#include "base/multiprocess_test.h"
+#include "base/process.h"
+
+// This unit test uses 3 types of child processes, a regular pipe client,
+// a client reflector and a IPC server used for fuzzing tests.
+enum ChildType {
+ TEST_CLIENT,
+ TEST_DESCRIPTOR_CLIENT,
+ TEST_DESCRIPTOR_CLIENT_SANDBOXED,
+ TEST_REFLECTOR,
+ FUZZER_SERVER
+};
+
+// The different channel names for the child processes.
+extern const wchar_t kTestClientChannel[];
+extern const wchar_t kReflectorChannel[];
+extern const wchar_t kFuzzerChannel[];
+
+class MessageLoopForIO;
+namespace IPC {
+class Channel;
+} // namespace IPC
+
+//Base class to facilitate Spawning IPC Client processes.
+class IPCChannelTest : public MultiProcessTest {
+ protected:
+
+ // Create a new MessageLoopForIO For each test.
+ virtual void SetUp();
+ virtual void TearDown();
+
+ // Spawns a child process of the specified type
+ base::ProcessHandle SpawnChild(ChildType child_type,
+ IPC::Channel *channel);
+
+ // Created around each test instantiation.
+ MessageLoopForIO *message_loop_;
+};
+
+#endif // CHROME_COMMON_IPC_TESTS_H__
diff --git a/chrome/common/ipc_tests.vcproj b/chrome/common/ipc_tests.vcproj
new file mode 100644
index 0000000..78fae65
--- /dev/null
+++ b/chrome/common/ipc_tests.vcproj
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="ipc_tests"
+ ProjectGUID="{B92AE829-E1CD-4781-824A-DCB1603A1672}"
+ RootNamespace="ipc_tests"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib rpcrt4.lib winmm.lib"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib rpcrt4.lib winmm.lib"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\ipc_fuzzing_tests.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_tests.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_tests.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\base\perftimer.cc"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index a68b4d6..66a49cf 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -25,23 +25,6 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/env_vars.h"
-#if defined(OS_POSIX)
-// On POSIX, this file also handles IPC logging
-
-#include "ipc/ipc_logging.h"
-#include "ipc/ipc_message.h"
-
-#ifdef IPC_MESSAGE_LOG_ENABLED
-// This will cause render_messages.h etc to define ViewMsgLog and friends.
-#define IPC_MESSAGE_MACROS_LOG_ENABLED
-// This include list should contain all _messages.h header files so that they
-// can get *MsgLog function etc. This makes ipc logs much more informative.
-#include "chrome/common/render_messages.h"
-#include "chrome/test/automation/automation_messages.h"
-#endif
-
-#endif
-
// When true, this means that error dialogs should not be shown.
static bool dialogs_are_suppressed_ = false;
@@ -89,10 +72,6 @@ void InitChromeLogging(const CommandLine& command_line,
DCHECK(!chrome_logging_initialized_) <<
"Attempted to initialize logging when it was already initialized.";
-#if defined(OS_POSIX) && defined(IPC_MESSAGE_LOG_ENABLED)
- IPC::Logging::SetLoggerFunctions(g_log_function_mapping);
-#endif
-
// only use OutputDebugString in debug mode
#ifdef NDEBUG
bool enable_logging = false;
diff --git a/chrome/common/message_router.h b/chrome/common/message_router.h
index ef18aaa..d23e2f3 100644
--- a/chrome/common/message_router.h
+++ b/chrome/common/message_router.h
@@ -6,7 +6,7 @@
#define CHROME_COMMON_MESSAGE_ROUTER_H__
#include "base/id_map.h"
-#include "ipc/ipc_channel.h"
+#include "chrome/common/ipc_channel.h"
// The MessageRouter handles all incoming messages sent to it by routing them
// to the correct listener. Routing is based on the Message's routing ID.
diff --git a/chrome/common/plugin_messages.h b/chrome/common/plugin_messages.h
index ecd012a..7b5ce73 100644
--- a/chrome/common/plugin_messages.h
+++ b/chrome/common/plugin_messages.h
@@ -16,8 +16,7 @@
#include "base/gfx/native_widget_types.h"
#include "base/gfx/rect.h"
#include "base/basictypes.h"
-#include "chrome/common/common_message_utils.h"
-#include "ipc/ipc_message_utils.h"
+#include "chrome/common/ipc_message_utils.h"
#include "googleurl/src/gurl.h"
#include "third_party/npapi/bindings/npapi.h"
#include "webkit/glue/npruntime_util.h"
@@ -448,12 +447,8 @@ struct ParamTraits<NPVariant_Param> {
} // namespace IPC
-#if 0
-// This for tools which parse #include lines, but cannot process when we
-// include via a macro name.
-#include "chrome/common/plugin_messages_internal.h"
-#endif
+
#define MESSAGES_INTERNAL_FILE "chrome/common/plugin_messages_internal.h"
-#include "ipc/ipc_message_macros.h"
+#include "chrome/common/ipc_message_macros.h"
#endif // CHROME_COMMON_PLUGIN_MESSAGES_H__
diff --git a/chrome/common/plugin_messages_internal.h b/chrome/common/plugin_messages_internal.h
index cafc4b0..11a0365 100644
--- a/chrome/common/plugin_messages_internal.h
+++ b/chrome/common/plugin_messages_internal.h
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "base/shared_memory.h"
-#include "ipc/ipc_message_macros.h"
+#include "chrome/common/ipc_message_macros.h"
#include "webkit/glue/webcursor.h"
//-----------------------------------------------------------------------------
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index b2e289f..38af203 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -15,13 +15,12 @@
#include "base/shared_memory.h"
#include "chrome/browser/renderer_host/resource_handler.h"
#include "chrome/common/filter_policy.h"
-#include "chrome/common/common_message_utils.h"
+#include "chrome/common/ipc_message_utils.h"
#include "chrome/common/modal_dialog_event.h"
#include "chrome/common/page_transition_types.h"
#include "chrome/common/transport_dib.h"
#include "chrome/common/webkit_param_traits.h"
#include "googleurl/src/gurl.h"
-#include "ipc/ipc_message_utils.h"
#include "media/audio/audio_output.h"
#include "net/base/upload_data.h"
#include "net/http/http_response_headers.h"
@@ -1772,12 +1771,7 @@ struct ParamTraits<AudioOutputStream::State> {
} // namespace IPC
-#if 0
-// This for tools which parse #include lines, but cannot process when we
-// include via a macro name.
-#include "chrome/common/render_messages_internal.h"
-#endif
#define MESSAGES_INTERNAL_FILE "chrome/common/render_messages_internal.h"
-#include "ipc/ipc_message_macros.h"
+#include "chrome/common/ipc_message_macros.h"
#endif // CHROME_COMMON_RENDER_MESSAGES_H_
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index 8a81d74..ece0cd1 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -15,8 +15,8 @@
#include "base/gfx/rect.h"
#include "base/gfx/native_widget_types.h"
#include "base/shared_memory.h"
+#include "chrome/common/ipc_message_macros.h"
#include "chrome/common/transport_dib.h"
-#include "ipc/ipc_message_macros.h"
#include "skia/include/SkBitmap.h"
#include "webkit/glue/dom_operations.h"
#include "webkit/glue/webcursor.h"
diff --git a/chrome/common/resource_dispatcher.h b/chrome/common/resource_dispatcher.h
index f494aff..73d0a8b3 100644
--- a/chrome/common/resource_dispatcher.h
+++ b/chrome/common/resource_dispatcher.h
@@ -15,7 +15,7 @@
#include "base/shared_memory.h"
#include "base/task.h"
#include "chrome/common/filter_policy.h"
-#include "ipc/ipc_channel.h"
+#include "chrome/common/ipc_channel.h"
#include "webkit/glue/resource_loader_bridge.h"
struct ResourceResponseHead;
diff --git a/chrome/common/webkit_param_traits.h b/chrome/common/webkit_param_traits.h
index 0f48ef3..d41bccc 100644
--- a/chrome/common/webkit_param_traits.h
+++ b/chrome/common/webkit_param_traits.h
@@ -8,7 +8,7 @@
#ifndef CHROME_COMMON_WEBKIT_PARAM_TRAITS_H_
#define CHROME_COMMON_WEBKIT_PARAM_TRAITS_H_
-#include "chrome/common/common_message_utils.h"
+#include "chrome/common/ipc_message_utils.h"
#include "third_party/WebKit/WebKit/chromium/public/WebCache.h"
#include "third_party/WebKit/WebKit/chromium/public/WebConsoleMessage.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFindInPageRequest.h"
diff --git a/chrome/common/worker_messages.h b/chrome/common/worker_messages.h
index 50bc69b..c1fde97 100644
--- a/chrome/common/worker_messages.h
+++ b/chrome/common/worker_messages.h
@@ -11,15 +11,10 @@
#include <string>
#include "base/basictypes.h"
-#include "ipc/ipc_message_utils.h"
-#include "chrome/common/common_message_utils.h"
+#include "chrome/common/ipc_message_utils.h"
+
-#if 0
-// This for tools which parse #include lines, but cannot process when we
-// include via a macro name.
-#include "chrome/common/worker_messages_internal.h"
-#endif
#define MESSAGES_INTERNAL_FILE "chrome/common/worker_messages_internal.h"
-#include "ipc/ipc_message_macros.h"
+#include "chrome/common/ipc_message_macros.h"
#endif // CHROME_COMMON_WORKER_MESSAGES_H_
diff --git a/chrome/common/worker_messages_internal.h b/chrome/common/worker_messages_internal.h
index ba9bb4c..7691c79 100644
--- a/chrome/common/worker_messages_internal.h
+++ b/chrome/common/worker_messages_internal.h
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "base/string16.h"
-#include "ipc/ipc_message_macros.h"
+#include "chrome/common/ipc_message_macros.h"
#include "googleurl/src/gurl.h"