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