1 // Copyright 2014 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 MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_ 6 #define MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_ 7 8 #include <string> 9 #include <utility> 10 11 #include "base/bind.h" 12 #include "base/callback.h" 13 #include "base/macros.h" 14 #include "base/memory/ptr_util.h" 15 #include "mojo/public/cpp/bindings/binding.h" 16 #include "mojo/public/cpp/bindings/connection_error_callback.h" 17 #include "mojo/public/cpp/bindings/interface_ptr.h" 18 #include "mojo/public/cpp/bindings/interface_request.h" 19 #include "mojo/public/cpp/bindings/message.h" 20 21 namespace mojo { 22 23 template <typename BindingType> 24 struct BindingSetTraits; 25 26 template <typename Interface, typename ImplRefTraits> 27 struct BindingSetTraits<Binding<Interface, ImplRefTraits>> { 28 using ProxyType = InterfacePtr<Interface>; 29 using RequestType = InterfaceRequest<Interface>; 30 using BindingType = Binding<Interface, ImplRefTraits>; 31 using ImplPointerType = typename BindingType::ImplPointerType; 32 33 static RequestType MakeRequest(ProxyType* proxy) { 34 return mojo::MakeRequest(proxy); 35 } 36 }; 37 38 using BindingId = size_t; 39 40 template <typename ContextType> 41 struct BindingSetContextTraits { 42 using Type = ContextType; 43 44 static constexpr bool SupportsContext() { return true; } 45 }; 46 47 template <> 48 struct BindingSetContextTraits<void> { 49 // NOTE: This choice of Type only matters insofar as it affects the size of 50 // the |context_| field of a BindingSetBase::Entry with void context. The 51 // context value is never used in this case. 52 using Type = bool; 53 54 static constexpr bool SupportsContext() { return false; } 55 }; 56 57 // Generic definition used for BindingSet and AssociatedBindingSet to own a 58 // collection of bindings which point to the same implementation. 59 // 60 // If |ContextType| is non-void, then every added binding must include a context 61 // value of that type, and |dispatch_context()| will return that value during 62 // the extent of any message dispatch targeting that specific binding. 63 template <typename Interface, typename BindingType, typename ContextType> 64 class BindingSetBase { 65 public: 66 using ContextTraits = BindingSetContextTraits<ContextType>; 67 using Context = typename ContextTraits::Type; 68 using PreDispatchCallback = base::Callback<void(const Context&)>; 69 using Traits = BindingSetTraits<BindingType>; 70 using ProxyType = typename Traits::ProxyType; 71 using RequestType = typename Traits::RequestType; 72 using ImplPointerType = typename Traits::ImplPointerType; 73 74 BindingSetBase() {} 75 76 void set_connection_error_handler(const base::Closure& error_handler) { 77 error_handler_ = error_handler; 78 error_with_reason_handler_.Reset(); 79 } 80 81 void set_connection_error_with_reason_handler( 82 const ConnectionErrorWithReasonCallback& error_handler) { 83 error_with_reason_handler_ = error_handler; 84 error_handler_.Reset(); 85 } 86 87 // Sets a callback to be invoked immediately before dispatching any message or 88 // error received by any of the bindings in the set. This may only be used 89 // with a non-void |ContextType|. 90 void set_pre_dispatch_handler(const PreDispatchCallback& handler) { 91 static_assert(ContextTraits::SupportsContext(), 92 "Pre-dispatch handler usage requires non-void context type."); 93 pre_dispatch_handler_ = handler; 94 } 95 96 // Adds a new binding to the set which binds |request| to |impl| with no 97 // additional context. 98 BindingId AddBinding(ImplPointerType impl, RequestType request) { 99 static_assert(!ContextTraits::SupportsContext(), 100 "Context value required for non-void context type."); 101 return AddBindingImpl(std::move(impl), std::move(request), false); 102 } 103 104 // Adds a new binding associated with |context|. 105 BindingId AddBinding(ImplPointerType impl, 106 RequestType request, 107 Context context) { 108 static_assert(ContextTraits::SupportsContext(), 109 "Context value unsupported for void context type."); 110 return AddBindingImpl(std::move(impl), std::move(request), 111 std::move(context)); 112 } 113 114 // Removes a binding from the set. Note that this is safe to call even if the 115 // binding corresponding to |id| has already been removed. 116 // 117 // Returns |true| if the binding was removed and |false| if it didn't exist. 118 bool RemoveBinding(BindingId id) { 119 auto it = bindings_.find(id); 120 if (it == bindings_.end()) 121 return false; 122 bindings_.erase(it); 123 return true; 124 } 125 126 // Returns a proxy bound to one end of a pipe whose other end is bound to 127 // |this|. If |id_storage| is not null, |*id_storage| will be set to the ID 128 // of the added binding. 129 ProxyType CreateInterfacePtrAndBind(ImplPointerType impl, 130 BindingId* id_storage = nullptr) { 131 ProxyType proxy; 132 BindingId id = AddBinding(std::move(impl), Traits::MakeRequest(&proxy)); 133 if (id_storage) 134 *id_storage = id; 135 return proxy; 136 } 137 138 void CloseAllBindings() { bindings_.clear(); } 139 140 bool empty() const { return bindings_.empty(); } 141 142 // Implementations may call this when processing a dispatched message or 143 // error. During the extent of message or error dispatch, this will return the 144 // context associated with the specific binding which received the message or 145 // error. Use AddBinding() to associated a context with a specific binding. 146 const Context& dispatch_context() const { 147 static_assert(ContextTraits::SupportsContext(), 148 "dispatch_context() requires non-void context type."); 149 DCHECK(dispatch_context_); 150 return *dispatch_context_; 151 } 152 153 void FlushForTesting() { 154 for (auto& binding : bindings_) 155 binding.second->FlushForTesting(); 156 } 157 158 private: 159 friend class Entry; 160 161 class Entry { 162 public: 163 Entry(ImplPointerType impl, 164 RequestType request, 165 BindingSetBase* binding_set, 166 BindingId binding_id, 167 Context context) 168 : binding_(std::move(impl), std::move(request)), 169 binding_set_(binding_set), 170 binding_id_(binding_id), 171 context_(std::move(context)) { 172 if (ContextTraits::SupportsContext()) 173 binding_.AddFilter(base::MakeUnique<DispatchFilter>(this)); 174 binding_.set_connection_error_with_reason_handler( 175 base::Bind(&Entry::OnConnectionError, base::Unretained(this))); 176 } 177 178 void FlushForTesting() { binding_.FlushForTesting(); } 179 180 private: 181 class DispatchFilter : public MessageReceiver { 182 public: 183 explicit DispatchFilter(Entry* entry) : entry_(entry) {} 184 ~DispatchFilter() override {} 185 186 private: 187 // MessageReceiver: 188 bool Accept(Message* message) override { 189 entry_->WillDispatch(); 190 return true; 191 } 192 193 Entry* entry_; 194 195 DISALLOW_COPY_AND_ASSIGN(DispatchFilter); 196 }; 197 198 void WillDispatch() { 199 DCHECK(ContextTraits::SupportsContext()); 200 binding_set_->SetDispatchContext(&context_); 201 } 202 203 void OnConnectionError(uint32_t custom_reason, 204 const std::string& description) { 205 if (ContextTraits::SupportsContext()) 206 WillDispatch(); 207 binding_set_->OnConnectionError(binding_id_, custom_reason, description); 208 } 209 210 BindingType binding_; 211 BindingSetBase* const binding_set_; 212 const BindingId binding_id_; 213 Context const context_; 214 215 DISALLOW_COPY_AND_ASSIGN(Entry); 216 }; 217 218 void SetDispatchContext(const Context* context) { 219 DCHECK(ContextTraits::SupportsContext()); 220 dispatch_context_ = context; 221 if (!pre_dispatch_handler_.is_null()) 222 pre_dispatch_handler_.Run(*context); 223 } 224 225 BindingId AddBindingImpl(ImplPointerType impl, 226 RequestType request, 227 Context context) { 228 BindingId id = next_binding_id_++; 229 DCHECK_GE(next_binding_id_, 0u); 230 auto entry = base::MakeUnique<Entry>(std::move(impl), std::move(request), 231 this, id, std::move(context)); 232 bindings_.insert(std::make_pair(id, std::move(entry))); 233 return id; 234 } 235 236 void OnConnectionError(BindingId id, 237 uint32_t custom_reason, 238 const std::string& description) { 239 auto it = bindings_.find(id); 240 DCHECK(it != bindings_.end()); 241 242 // We keep the Entry alive throughout error dispatch. 243 std::unique_ptr<Entry> entry = std::move(it->second); 244 bindings_.erase(it); 245 246 if (!error_handler_.is_null()) 247 error_handler_.Run(); 248 else if (!error_with_reason_handler_.is_null()) 249 error_with_reason_handler_.Run(custom_reason, description); 250 } 251 252 base::Closure error_handler_; 253 ConnectionErrorWithReasonCallback error_with_reason_handler_; 254 PreDispatchCallback pre_dispatch_handler_; 255 BindingId next_binding_id_ = 0; 256 std::map<BindingId, std::unique_ptr<Entry>> bindings_; 257 const Context* dispatch_context_ = nullptr; 258 259 DISALLOW_COPY_AND_ASSIGN(BindingSetBase); 260 }; 261 262 template <typename Interface, typename ContextType = void> 263 using BindingSet = BindingSetBase<Interface, Binding<Interface>, ContextType>; 264 265 } // namespace mojo 266 267 #endif // MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_ 268