| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/common/np_channel_base.h" | |
| 6 | |
| 7 #include <stack> | |
| 8 | |
| 9 #include "base/auto_reset.h" | |
| 10 #include "base/hash_tables.h" | |
| 11 #include "base/lazy_instance.h" | |
| 12 #include "base/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 != 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; | |
| 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 channel_valid_(false), | |
| 76 in_unblock_dispatch_(0), | |
| 77 send_unblocking_only_during_unblock_dispatch_(false) { | |
| 78 } | |
| 79 | |
| 80 NPChannelBase::~NPChannelBase() { | |
| 81 } | |
| 82 | |
| 83 NPChannelBase* NPChannelBase::GetCurrentChannel() { | |
| 84 return g_lazy_channel_stack.Pointer()->top(); | |
| 85 } | |
| 86 | |
| 87 void NPChannelBase::CleanupChannels() { | |
| 88 // Make a copy of the references as we can't iterate the map since items will | |
| 89 // be removed from it as we clean them up. | |
| 90 std::vector<scoped_refptr<NPChannelBase> > channels; | |
| 91 for (ChannelMap::const_iterator iter = g_channels.Get().begin(); | |
| 92 iter != g_channels.Get().end(); | |
| 93 ++iter) { | |
| 94 channels.push_back(iter->second); | |
| 95 } | |
| 96 | |
| 97 for (size_t i = 0; i < channels.size(); ++i) | |
| 98 channels[i]->CleanUp(); | |
| 99 | |
| 100 // This will clean up channels added to the map for which subsequent | |
| 101 // AddRoute wasn't called | |
| 102 g_channels.Get().clear(); | |
| 103 } | |
| 104 | |
| 105 NPObjectBase* NPChannelBase::GetNPObjectListenerForRoute(int route_id) { | |
| 106 ListenerMap::iterator iter = npobject_listeners_.find(route_id); | |
| 107 if (iter == npobject_listeners_.end()) { | |
| 108 DLOG(WARNING) << "Invalid route id passed in:" << route_id; | |
| 109 return NULL; | |
| 110 } | |
| 111 return iter->second; | |
| 112 } | |
| 113 | |
| 114 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) { | |
| 115 return NULL; | |
| 116 } | |
| 117 | |
| 118 bool NPChannelBase::Init(base::MessageLoopProxy* ipc_message_loop, | |
| 119 bool create_pipe_now, | |
| 120 base::WaitableEvent* shutdown_event) { | |
| 121 #if defined(OS_POSIX) | |
| 122 // Attempting to initialize with an invalid channel handle. | |
| 123 // See http://crbug.com/97285 for details. | |
| 124 if (mode_ == IPC::Channel::MODE_CLIENT && -1 == channel_handle_.socket.fd) | |
| 125 return false; | |
| 126 #endif | |
| 127 | |
| 128 channel_.reset(new IPC::SyncChannel( | |
| 129 channel_handle_, mode_, this, ipc_message_loop, create_pipe_now, | |
| 130 shutdown_event)); | |
| 131 | |
| 132 #if defined(OS_POSIX) | |
| 133 // Check the validity of fd for bug investigation. Remove after fixed. | |
| 134 // See crbug.com/97285 for details. | |
| 135 if (mode_ == IPC::Channel::MODE_SERVER) | |
| 136 CHECK_NE(-1, channel_->GetClientFileDescriptor()); | |
| 137 #endif | |
| 138 | |
| 139 channel_valid_ = true; | |
| 140 return true; | |
| 141 } | |
| 142 | |
| 143 bool NPChannelBase::Send(IPC::Message* message) { | |
| 144 if (!channel_) { | |
| 145 VLOG(1) << "Channel is NULL; dropping message"; | |
| 146 delete message; | |
| 147 return false; | |
| 148 } | |
| 149 | |
| 150 if (send_unblocking_only_during_unblock_dispatch_ && | |
| 151 in_unblock_dispatch_ == 0 && | |
| 152 message->is_sync()) { | |
| 153 message->set_unblock(false); | |
| 154 } | |
| 155 | |
| 156 return channel_->Send(message); | |
| 157 } | |
| 158 | |
| 159 int NPChannelBase::Count() { | |
| 160 return static_cast<int>(g_channels.Get().size()); | |
| 161 } | |
| 162 | |
| 163 bool NPChannelBase::OnMessageReceived(const IPC::Message& message) { | |
| 164 // This call might cause us to be deleted, so keep an extra reference to | |
| 165 // ourself so that we can send the reply and decrement back in_dispatch_. | |
| 166 g_lazy_channel_stack.Pointer()->push( | |
| 167 scoped_refptr<NPChannelBase>(this)); | |
| 168 | |
| 169 bool handled; | |
| 170 if (message.should_unblock()) | |
| 171 in_unblock_dispatch_++; | |
| 172 if (message.routing_id() == MSG_ROUTING_CONTROL) { | |
| 173 handled = OnControlMessageReceived(message); | |
| 174 } else { | |
| 175 handled = router_.RouteMessage(message); | |
| 176 if (!handled && message.is_sync()) { | |
| 177 // The listener has gone away, so we must respond or else the caller will | |
| 178 // hang waiting for a reply. | |
| 179 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); | |
| 180 reply->set_reply_error(); | |
| 181 Send(reply); | |
| 182 } | |
| 183 } | |
| 184 if (message.should_unblock()) | |
| 185 in_unblock_dispatch_--; | |
| 186 | |
| 187 g_lazy_channel_stack.Pointer()->pop(); | |
| 188 return handled; | |
| 189 } | |
| 190 | |
| 191 void NPChannelBase::OnChannelConnected(int32 peer_pid) { | |
| 192 peer_pid_ = peer_pid; | |
| 193 } | |
| 194 | |
| 195 void NPChannelBase::AddRoute(int route_id, | |
| 196 IPC::Listener* listener, | |
| 197 NPObjectBase* npobject) { | |
| 198 if (npobject) { | |
| 199 npobject_listeners_[route_id] = npobject; | |
| 200 } else { | |
| 201 non_npobject_count_++; | |
| 202 } | |
| 203 | |
| 204 router_.AddRoute(route_id, listener); | |
| 205 } | |
| 206 | |
| 207 void NPChannelBase::RemoveRoute(int route_id) { | |
| 208 router_.RemoveRoute(route_id); | |
| 209 | |
| 210 ListenerMap::iterator iter = npobject_listeners_.find(route_id); | |
| 211 if (iter != npobject_listeners_.end()) { | |
| 212 // This was an NPObject proxy or stub, it's not involved in the refcounting. | |
| 213 | |
| 214 // If this RemoveRoute call from the NPObject is a result of us calling | |
| 215 // OnChannelError below, don't call erase() here because that'll corrupt | |
| 216 // the iterator below. | |
| 217 if (in_remove_route_) { | |
| 218 iter->second = NULL; | |
| 219 } else { | |
| 220 npobject_listeners_.erase(iter); | |
| 221 } | |
| 222 | |
| 223 return; | |
| 224 } | |
| 225 | |
| 226 non_npobject_count_--; | |
| 227 DCHECK(non_npobject_count_ >= 0); | |
| 228 | |
| 229 if (!non_npobject_count_) { | |
| 230 base::AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true); | |
| 231 for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin(); | |
| 232 npobj_iter != npobject_listeners_.end(); ++npobj_iter) { | |
| 233 if (npobj_iter->second) { | |
| 234 npobj_iter->second->GetChannelListener()->OnChannelError(); | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 for (ChannelMap::iterator iter = g_channels.Get().begin(); | |
| 239 iter != g_channels.Get().end(); ++iter) { | |
| 240 if (iter->second == this) { | |
| 241 g_channels.Get().erase(iter); | |
| 242 return; | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 NOTREACHED(); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) { | |
| 251 NOTREACHED() << | |
| 252 "should override in subclass if you care about control messages"; | |
| 253 return false; | |
| 254 } | |
| 255 | |
| 256 void NPChannelBase::OnChannelError() { | |
| 257 channel_valid_ = false; | |
| 258 | |
| 259 // TODO(shess): http://crbug.com/97285 | |
| 260 // Once an error is seen on a channel, remap the channel to prevent | |
| 261 // it from being vended again. Keep the channel in the map so | |
| 262 // RemoveRoute() can clean things up correctly. | |
| 263 for (ChannelMap::iterator iter = g_channels.Get().begin(); | |
| 264 iter != g_channels.Get().end(); ++iter) { | |
| 265 if (iter->second == this) { | |
| 266 // Insert new element before invalidating |iter|. | |
| 267 g_channels.Get()[iter->first + "-error"] = iter->second; | |
| 268 g_channels.Get().erase(iter); | |
| 269 break; | |
| 270 } | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 NPObject* NPChannelBase::GetExistingNPObjectProxy(int route_id) { | |
| 275 ProxyMap::iterator iter = proxy_map_.find(route_id); | |
| 276 return iter != proxy_map_.end() ? iter->second : NULL; | |
| 277 } | |
| 278 | |
| 279 int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject* npobject) { | |
| 280 StubMap::iterator iter = stub_map_.find(npobject); | |
| 281 return iter != stub_map_.end() ? iter->second : MSG_ROUTING_NONE; | |
| 282 } | |
| 283 | |
| 284 void NPChannelBase::AddMappingForNPObjectProxy(int route_id, | |
| 285 NPObject* object) { | |
| 286 proxy_map_[route_id] = object; | |
| 287 } | |
| 288 | |
| 289 void NPChannelBase::AddMappingForNPObjectStub(int route_id, | |
| 290 NPObject* object) { | |
| 291 DCHECK(object != NULL); | |
| 292 stub_map_[object] = route_id; | |
| 293 } | |
| 294 | |
| 295 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id, | |
| 296 NPObject* object) { | |
| 297 DCHECK(object != NULL); | |
| 298 stub_map_.erase(object); | |
| 299 } | |
| 300 | |
| 301 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) { | |
| 302 proxy_map_.erase(route_id); | |
| 303 } | |
| 304 | |
| 305 } // namespace content | |
| OLD | NEW |