1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_FRAME_SYNC_MSG_REPLY_DISPATCHER_H_
6#define CHROME_FRAME_SYNC_MSG_REPLY_DISPATCHER_H_
7
8#include <deque>
9
10#include "base/synchronization/lock.h"
11#include "ipc/ipc_channel_proxy.h"
12
13// Base class used to allow synchronous IPC messages to be sent and
14// received in an asynchronous manner. To use this class add it as a filter to
15// your IPC channel using ChannelProxy::AddFilter(). From then on, before
16// sending a synchronous message, call SyncMessageReplyDispatcher::Push() with
17// a callback and a key. This class will then handle the message response and
18// will call the callback when it is received.
19//
20// This class is intended to be extended by classes implementing
21// HandleMessageType with delegation for the messages they expect to receive in
22// cases where you care about the return values of synchronous messages.
23//
24// Sample usage pattern:
25// Define a class which inherits from SyncMessageCallContext which specifies
26// the output_type tuple and has a Completed member function.
27// class SampleContext
28//     : public SyncMessageReplyDispatcher::SyncMessageCallContext {
29// public:
30//  typedef Tuple1<int> output_type;
31//  void Completed(int arg) {}
32// };
33//
34// // Add handling for desired message types.
35// class SyncMessageReplyDispatcherImpl : public SyncMessageReplyDispatcher {
36//   virtual bool HandleMessageType(const IPC::Message& msg,
37//                                  SyncMessageReplyDispatcher* context) {
38//    switch (context->message_type()) {
39//      case AutomationMsg_CreateExternalTab::ID:
40//        InvokeCallback<CreateExternalTabContext>(msg, context);
41//        break;
42//     [HANDLING FOR OTHER EXPECTED MESSAGE TYPES]
43//   }
44// }
45//
46// // Add the filter
47// IPC::SyncChannel channel_;
48// channel_.AddFilter(new SyncMessageReplyDispatcherImpl());
49//
50// sync_->Push(msg, new SampleContext, this);
51// channel_->ChannelProxy::Send(msg);
52//
53class SyncMessageReplyDispatcher : public IPC::ChannelProxy::MessageFilter {
54 public:
55  class SyncMessageCallContext {
56   public:
57    SyncMessageCallContext()
58        : id_(0),
59          message_type_(0),
60          key_(NULL) {}
61
62    virtual ~SyncMessageCallContext() {}
63
64    uint32 message_type() const {
65      return message_type_;
66    }
67
68   private:
69    int id_;
70    uint32 message_type_;
71    void* key_;
72
73    friend class SyncMessageReplyDispatcher;
74  };
75
76  SyncMessageReplyDispatcher() {}
77  void Push(IPC::SyncMessage* msg, SyncMessageCallContext* context,
78            void* key);
79  void Cancel(void* key);
80
81 protected:
82  typedef std::deque<SyncMessageCallContext*> PendingSyncMessageQueue;
83
84  SyncMessageCallContext* GetContext(const IPC::Message& msg);
85
86  virtual bool OnMessageReceived(const IPC::Message& msg);
87
88  // Child classes must implement a handler for the message types they are
89  // interested in handling responses for. If you don't care about the replies
90  // to any of the sync messages you are handling, then you don't have to
91  // implement this.
92  virtual bool HandleMessageType(const IPC::Message& msg,
93                                 SyncMessageCallContext* context);
94
95  template <typename T>
96  void InvokeCallback(const IPC::Message& msg,
97                      SyncMessageCallContext* call_context) {
98    if (!call_context || !call_context->key_) {
99      NOTREACHED() << "Invalid context parameter";
100      return;
101    }
102
103    T* context = static_cast<T*>(call_context);
104    T::output_type tmp;  // Acts as "initializer" for output parameters.
105    IPC::ParamDeserializer<T::output_type> deserializer(tmp);
106    if (deserializer.MessageReplyDeserializer::SerializeOutputParameters(msg)) {
107      DispatchToMethod(context, &T::Completed, deserializer.out_);
108      delete context;
109    } else {
110      // TODO(stoyan): How to handle errors?
111    }
112  }
113
114  PendingSyncMessageQueue message_queue_;
115  base::Lock message_queue_lock_;
116};
117
118#endif  // CHROME_FRAME_SYNC_MSG_REPLY_DISPATCHER_H_
119