diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-03 02:29:45 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-03 02:29:45 +0000 |
commit | 82e5ee816a5f05d230c5356205e93d803650d0b9 (patch) | |
tree | 6b6e9d1bef407feef8ef24eedc9f343e2d536609 /chrome/common | |
parent | 8cf3f1dae79db8cfbf9dc6b75c140eabad918a6e (diff) | |
download | chromium_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')
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, ¶ms); + + 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, ¶ms); + + 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, ¶ms)) + 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, ¶ms)) + 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, ¶ms)) + 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, ¶ms)) + 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" |