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 |