Home | History | Annotate | Download | only in chrome_frame
      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 //
     53 class 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