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