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