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