1 // Copyright 2013 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 #include "content/child/npapi/np_channel_base.h" 6 7 #include <stack> 8 9 #include "base/auto_reset.h" 10 #include "base/containers/hash_tables.h" 11 #include "base/lazy_instance.h" 12 #include "base/threading/thread_local.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "ipc/ipc_sync_message.h" 15 16 #if defined(OS_POSIX) 17 #include "ipc/ipc_channel_posix.h" 18 #endif 19 20 namespace content { 21 22 typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap; 23 static base::LazyInstance<base::ThreadLocalPointer<ChannelMap> >::Leaky 24 g_channels_tls_ptr = LAZY_INSTANCE_INITIALIZER; 25 26 typedef std::stack<scoped_refptr<NPChannelBase> > NPChannelRefStack; 27 static base::LazyInstance<base::ThreadLocalPointer<NPChannelRefStack> >::Leaky 28 g_lazy_channel_stack_tls_ptr = LAZY_INSTANCE_INITIALIZER; 29 30 static ChannelMap* GetChannelMap() { 31 ChannelMap* channel_map = g_channels_tls_ptr.Get().Get(); 32 if (!channel_map) { 33 channel_map = new ChannelMap(); 34 g_channels_tls_ptr.Get().Set(channel_map); 35 } 36 return channel_map; 37 } 38 39 static NPChannelRefStack* GetChannelRefStack() { 40 NPChannelRefStack* ref_stack = g_lazy_channel_stack_tls_ptr.Get().Get(); 41 if (!ref_stack) { 42 ref_stack = new NPChannelRefStack(); 43 g_lazy_channel_stack_tls_ptr.Get().Set(ref_stack); 44 } 45 return ref_stack; 46 } 47 48 NPChannelBase* NPChannelBase::GetChannel( 49 const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode, 50 ChannelFactory factory, base::MessageLoopProxy* ipc_message_loop, 51 bool create_pipe_now, base::WaitableEvent* shutdown_event) { 52 scoped_refptr<NPChannelBase> channel; 53 std::string channel_key = channel_handle.name; 54 ChannelMap::const_iterator iter = GetChannelMap()->find(channel_key); 55 if (iter == GetChannelMap()->end()) { 56 channel = factory(); 57 } else { 58 channel = iter->second; 59 } 60 61 DCHECK(channel.get() != NULL); 62 63 if (!channel->channel_valid()) { 64 channel->channel_handle_ = channel_handle; 65 if (mode & IPC::Channel::MODE_SERVER_FLAG) { 66 channel->channel_handle_.name = 67 IPC::Channel::GenerateVerifiedChannelID(channel_key); 68 } 69 channel->mode_ = mode; 70 if (channel->Init(ipc_message_loop, create_pipe_now, shutdown_event)) { 71 (*GetChannelMap())[channel_key] = channel; 72 } else { 73 channel = NULL; 74 } 75 } 76 77 return channel.get(); 78 } 79 80 void NPChannelBase::Broadcast(IPC::Message* message) { 81 for (ChannelMap::iterator iter = GetChannelMap()->begin(); 82 iter != GetChannelMap()->end(); 83 ++iter) { 84 iter->second->Send(new IPC::Message(*message)); 85 } 86 delete message; 87 } 88 89 NPChannelBase::NPChannelBase() 90 : mode_(IPC::Channel::MODE_NONE), 91 non_npobject_count_(0), 92 peer_pid_(0), 93 in_remove_route_(false), 94 default_owner_(NULL), 95 channel_valid_(false), 96 in_unblock_dispatch_(0), 97 send_unblocking_only_during_unblock_dispatch_(false) { 98 } 99 100 NPChannelBase::~NPChannelBase() { 101 // TODO(wez): Establish why these would ever be non-empty at teardown. 102 //DCHECK(npobject_listeners_.empty()); 103 //DCHECK(proxy_map_.empty()); 104 //DCHECK(stub_map_.empty()); 105 DCHECK(owner_to_route_.empty()); 106 DCHECK(route_to_owner_.empty()); 107 } 108 109 NPChannelBase* NPChannelBase::GetCurrentChannel() { 110 return GetChannelRefStack()->top().get(); 111 } 112 113 void NPChannelBase::CleanupChannels() { 114 // Make a copy of the references as we can't iterate the map since items will 115 // be removed from it as we clean them up. 116 std::vector<scoped_refptr<NPChannelBase> > channels; 117 for (ChannelMap::const_iterator iter = GetChannelMap()->begin(); 118 iter != GetChannelMap()->end(); 119 ++iter) { 120 channels.push_back(iter->second); 121 } 122 123 for (size_t i = 0; i < channels.size(); ++i) 124 channels[i]->CleanUp(); 125 126 // This will clean up channels added to the map for which subsequent 127 // AddRoute wasn't called 128 GetChannelMap()->clear(); 129 } 130 131 NPObjectBase* NPChannelBase::GetNPObjectListenerForRoute(int route_id) { 132 ListenerMap::iterator iter = npobject_listeners_.find(route_id); 133 if (iter == npobject_listeners_.end()) { 134 DLOG(WARNING) << "Invalid route id passed in:" << route_id; 135 return NULL; 136 } 137 return iter->second; 138 } 139 140 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) { 141 return NULL; 142 } 143 144 bool NPChannelBase::Init(base::MessageLoopProxy* ipc_message_loop, 145 bool create_pipe_now, 146 base::WaitableEvent* shutdown_event) { 147 #if defined(OS_POSIX) 148 // Attempting to initialize with an invalid channel handle. 149 // See http://crbug.com/97285 for details. 150 if (mode_ == IPC::Channel::MODE_CLIENT && -1 == channel_handle_.socket.fd) 151 return false; 152 #endif 153 154 channel_.reset(new IPC::SyncChannel( 155 channel_handle_, mode_, this, ipc_message_loop, create_pipe_now, 156 shutdown_event)); 157 158 #if defined(OS_POSIX) 159 // Check the validity of fd for bug investigation. Remove after fixed. 160 // See crbug.com/97285 for details. 161 if (mode_ == IPC::Channel::MODE_SERVER) 162 CHECK_NE(-1, channel_->GetClientFileDescriptor()); 163 #endif 164 165 channel_valid_ = true; 166 return true; 167 } 168 169 bool NPChannelBase::Send(IPC::Message* message) { 170 if (!channel_) { 171 VLOG(1) << "Channel is NULL; dropping message"; 172 delete message; 173 return false; 174 } 175 176 if (send_unblocking_only_during_unblock_dispatch_ && 177 in_unblock_dispatch_ == 0 && 178 message->is_sync()) { 179 message->set_unblock(false); 180 } 181 182 return channel_->Send(message); 183 } 184 185 int NPChannelBase::Count() { 186 return static_cast<int>(GetChannelMap()->size()); 187 } 188 189 bool NPChannelBase::OnMessageReceived(const IPC::Message& message) { 190 // This call might cause us to be deleted, so keep an extra reference to 191 // ourself so that we can send the reply and decrement back in_dispatch_. 192 GetChannelRefStack()->push( 193 scoped_refptr<NPChannelBase>(this)); 194 195 bool handled; 196 if (message.should_unblock()) 197 in_unblock_dispatch_++; 198 if (message.routing_id() == MSG_ROUTING_CONTROL) { 199 handled = OnControlMessageReceived(message); 200 } else { 201 handled = router_.RouteMessage(message); 202 if (!handled && message.is_sync()) { 203 // The listener has gone away, so we must respond or else the caller will 204 // hang waiting for a reply. 205 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); 206 reply->set_reply_error(); 207 Send(reply); 208 } 209 } 210 if (message.should_unblock()) 211 in_unblock_dispatch_--; 212 213 GetChannelRefStack()->pop(); 214 return handled; 215 } 216 217 void NPChannelBase::OnChannelConnected(int32 peer_pid) { 218 peer_pid_ = peer_pid; 219 } 220 221 void NPChannelBase::AddRoute(int route_id, 222 IPC::Listener* listener, 223 NPObjectBase* npobject) { 224 if (npobject) { 225 npobject_listeners_[route_id] = npobject; 226 } else { 227 non_npobject_count_++; 228 } 229 230 router_.AddRoute(route_id, listener); 231 } 232 233 void NPChannelBase::RemoveRoute(int route_id) { 234 router_.RemoveRoute(route_id); 235 236 ListenerMap::iterator iter = npobject_listeners_.find(route_id); 237 if (iter != npobject_listeners_.end()) { 238 // This was an NPObject proxy or stub, it's not involved in the refcounting. 239 240 // If this RemoveRoute call from the NPObject is a result of us calling 241 // OnChannelError below, don't call erase() here because that'll corrupt 242 // the iterator below. 243 if (in_remove_route_) { 244 iter->second = NULL; 245 } else { 246 npobject_listeners_.erase(iter); 247 } 248 249 return; 250 } 251 252 non_npobject_count_--; 253 DCHECK(non_npobject_count_ >= 0); 254 255 if (!non_npobject_count_) { 256 base::AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true); 257 for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin(); 258 npobj_iter != npobject_listeners_.end(); ++npobj_iter) { 259 if (npobj_iter->second) { 260 npobj_iter->second->GetChannelListener()->OnChannelError(); 261 } 262 } 263 264 for (ChannelMap::iterator iter = GetChannelMap()->begin(); 265 iter != GetChannelMap()->end(); ++iter) { 266 if (iter->second.get() == this) { 267 GetChannelMap()->erase(iter); 268 return; 269 } 270 } 271 272 NOTREACHED(); 273 } 274 } 275 276 bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) { 277 NOTREACHED() << 278 "should override in subclass if you care about control messages"; 279 return false; 280 } 281 282 void NPChannelBase::OnChannelError() { 283 channel_valid_ = false; 284 285 // TODO(shess): http://crbug.com/97285 286 // Once an error is seen on a channel, remap the channel to prevent 287 // it from being vended again. Keep the channel in the map so 288 // RemoveRoute() can clean things up correctly. 289 for (ChannelMap::iterator iter = GetChannelMap()->begin(); 290 iter != GetChannelMap()->end(); ++iter) { 291 if (iter->second.get() == this) { 292 // Insert new element before invalidating |iter|. 293 (*GetChannelMap())[iter->first + "-error"] = iter->second; 294 GetChannelMap()->erase(iter); 295 break; 296 } 297 } 298 } 299 300 void NPChannelBase::AddMappingForNPObjectProxy(int route_id, 301 NPObject* object) { 302 proxy_map_[route_id] = object; 303 } 304 305 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) { 306 proxy_map_.erase(route_id); 307 } 308 309 void NPChannelBase::AddMappingForNPObjectStub(int route_id, 310 NPObject* object) { 311 DCHECK(object != NULL); 312 stub_map_[object] = route_id; 313 } 314 315 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id, 316 NPObject* object) { 317 DCHECK(object != NULL); 318 stub_map_.erase(object); 319 } 320 321 void NPChannelBase::AddMappingForNPObjectOwner(int route_id, 322 struct _NPP* owner) { 323 DCHECK(owner != NULL); 324 route_to_owner_[route_id] = owner; 325 owner_to_route_[owner] = route_id; 326 } 327 328 void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) { 329 DCHECK(owner != NULL); 330 default_owner_ = owner; 331 } 332 333 void NPChannelBase::RemoveMappingForNPObjectOwner(int route_id) { 334 DCHECK(route_to_owner_.find(route_id) != route_to_owner_.end()); 335 owner_to_route_.erase(route_to_owner_[route_id]); 336 route_to_owner_.erase(route_id); 337 } 338 339 NPObject* NPChannelBase::GetExistingNPObjectProxy(int route_id) { 340 ProxyMap::iterator iter = proxy_map_.find(route_id); 341 return iter != proxy_map_.end() ? iter->second : NULL; 342 } 343 344 int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject* npobject) { 345 StubMap::iterator iter = stub_map_.find(npobject); 346 return iter != stub_map_.end() ? iter->second : MSG_ROUTING_NONE; 347 } 348 349 NPP NPChannelBase::GetExistingNPObjectOwner(int route_id) { 350 RouteToOwnerMap::iterator iter = route_to_owner_.find(route_id); 351 return iter != route_to_owner_.end() ? iter->second : default_owner_; 352 } 353 354 int NPChannelBase::GetExistingRouteForNPObjectOwner(NPP owner) { 355 OwnerToRouteMap::iterator iter = owner_to_route_.find(owner); 356 return iter != owner_to_route_.end() ? iter->second : MSG_ROUTING_NONE; 357 } 358 359 } // namespace content 360