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.
5 #include "content/child/npapi/np_channel_base.h"
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"
16 #include "base/file_util.h"
17 #include "ipc/ipc_channel_posix.h"
24 typedef base::hash_map
<std::string
, scoped_refptr
<NPChannelBase
> > ChannelMap
;
26 struct ChannelGlobals
{
27 ChannelMap channel_map
;
28 scoped_refptr
<NPChannelBase
> current_channel
;
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
;
40 ChannelGlobals
* GetChannelGlobals() {
41 ChannelGlobals
* globals
= g_channels_tls_ptr
.Get().Get();
43 globals
= new ChannelGlobals
;
44 g_channels_tls_ptr
.Get().Set(globals
);
51 base::LazyInstance
<ChannelGlobals
>::Leaky g_channels_globals
=
52 LAZY_INSTANCE_INITIALIZER
;
54 ChannelGlobals
* GetChannelGlobals() { return g_channels_globals
.Pointer(); }
58 ChannelMap
* GetChannelMap() {
59 return &GetChannelGlobals()->channel_map
;
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
) {
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);
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()) {
82 channel
= iter
->second
;
85 DCHECK(channel
.get() != NULL
);
87 if (!channel
->channel_valid()) {
88 channel
->channel_handle_
= channel_handle
;
90 ignore_result(fd
.release());
92 if (mode
& IPC::Channel::MODE_SERVER_FLAG
) {
93 channel
->channel_handle_
.name
=
94 IPC::Channel::GenerateVerifiedChannelID(channel_key
);
96 channel
->mode_
= mode
;
97 if (channel
->Init(ipc_message_loop
, create_pipe_now
, shutdown_event
)) {
98 (*GetChannelMap())[channel_key
] = channel
;
104 return channel
.get();
107 void NPChannelBase::Broadcast(IPC::Message
* message
) {
108 for (ChannelMap::iterator iter
= GetChannelMap()->begin();
109 iter
!= GetChannelMap()->end();
111 iter
->second
->Send(new IPC::Message(*message
));
116 NPChannelBase::NPChannelBase()
117 : mode_(IPC::Channel::MODE_NONE
),
118 non_npobject_count_(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) {
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());
136 NPChannelBase
* NPChannelBase::GetCurrentChannel() {
137 return GetChannelGlobals()->current_channel
.get();
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();
147 channels
.push_back(iter
->second
);
150 for (size_t i
= 0; i
< channels
.size(); ++i
)
151 channels
[i
]->CleanUp();
153 // This will clean up channels added to the map for which subsequent
154 // AddRoute wasn't called
155 GetChannelMap()->clear();
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
;
167 base::WaitableEvent
* NPChannelBase::GetModalDialogEvent(int render_view_id
) {
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
)
181 channel_
= IPC::SyncChannel::Create(
182 channel_handle_
, mode_
, this, ipc_message_loop
, create_pipe_now
,
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());
192 channel_valid_
= true;
196 bool NPChannelBase::Send(IPC::Message
* message
) {
198 VLOG(1) << "Channel is NULL; dropping message";
203 if (send_unblocking_only_during_unblock_dispatch_
&&
204 in_unblock_dispatch_
== 0 &&
205 message
->is_sync()) {
206 message
->set_unblock(false);
209 return channel_
->Send(message
);
212 int NPChannelBase::Count() {
213 return static_cast<int>(GetChannelMap()->size());
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);
224 if (message
.should_unblock())
225 in_unblock_dispatch_
++;
226 if (message
.routing_id() == MSG_ROUTING_CONTROL
) {
227 handled
= OnControlMessageReceived(message
);
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();
238 if (message
.should_unblock())
239 in_unblock_dispatch_
--;
244 void NPChannelBase::OnChannelConnected(int32 peer_pid
) {
245 peer_pid_
= peer_pid
;
248 void NPChannelBase::AddRoute(int route_id
,
249 IPC::Listener
* listener
,
250 NPObjectBase
* npobject
) {
252 npobject_listeners_
[route_id
] = npobject
;
254 non_npobject_count_
++;
257 router_
.AddRoute(route_id
, listener
);
260 void NPChannelBase::RemoveRoute(int route_id
) {
261 router_
.RemoveRoute(route_id
);
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.
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_
) {
273 npobject_listeners_
.erase(iter
);
279 non_npobject_count_
--;
280 DCHECK(non_npobject_count_
>= 0);
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();
291 for (ChannelMap::iterator iter
= GetChannelMap()->begin();
292 iter
!= GetChannelMap()->end(); ++iter
) {
293 if (iter
->second
.get() == this) {
294 GetChannelMap()->erase(iter
);
303 bool NPChannelBase::OnControlMessageReceived(const IPC::Message
& msg
) {
305 "should override in subclass if you care about control messages";
309 void NPChannelBase::OnChannelError() {
310 channel_valid_
= false;
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
);
327 void NPChannelBase::AddMappingForNPObjectProxy(int route_id
,
329 proxy_map_
[route_id
] = object
;
332 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id
) {
333 proxy_map_
.erase(route_id
);
336 void NPChannelBase::AddMappingForNPObjectStub(int route_id
,
338 DCHECK(object
!= NULL
);
339 stub_map_
[object
] = route_id
;
342 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id
,
344 DCHECK(object
!= NULL
);
345 stub_map_
.erase(object
);
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
;
355 void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP
* owner
) {
356 DCHECK(owner
!= NULL
);
357 default_owner_
= owner
;
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
);
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
;
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
;
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_
;
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
;
386 } // namespace content