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/single_thread_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/threading/thread_local.h"
14 #include "ipc/ipc_sync_message.h"
17 #include "base/files/file_util.h"
18 #include "ipc/ipc_channel_posix.h"
25 typedef base::hash_map
<std::string
, scoped_refptr
<NPChannelBase
> > ChannelMap
;
27 struct ChannelGlobals
{
28 ChannelMap channel_map
;
29 scoped_refptr
<NPChannelBase
> current_channel
;
32 #if defined(OS_ANDROID)
33 // Workaround for http://crbug.com/298179 - NPChannelBase is only intended
34 // for use on one thread per process. Using TLS to store the globals removes the
35 // worst thread hostility in this class, especially needed for webview which
36 // runs in single-process mode. TODO(joth): Make a complete fix, most likely
37 // as part of addressing http://crbug.com/258510.
38 base::LazyInstance
<base::ThreadLocalPointer
<ChannelGlobals
> >::Leaky
39 g_channels_tls_ptr
= LAZY_INSTANCE_INITIALIZER
;
41 ChannelGlobals
* GetChannelGlobals() {
42 ChannelGlobals
* globals
= g_channels_tls_ptr
.Get().Get();
44 globals
= new ChannelGlobals
;
45 g_channels_tls_ptr
.Get().Set(globals
);
52 base::LazyInstance
<ChannelGlobals
>::Leaky g_channels_globals
=
53 LAZY_INSTANCE_INITIALIZER
;
55 ChannelGlobals
* GetChannelGlobals() { return g_channels_globals
.Pointer(); }
59 ChannelMap
* GetChannelMap() {
60 return &GetChannelGlobals()->channel_map
;
65 NPChannelBase
* NPChannelBase::GetChannel(
66 const IPC::ChannelHandle
& channel_handle
,
67 IPC::Channel::Mode mode
,
68 ChannelFactory factory
,
69 base::SingleThreadTaskRunner
* ipc_task_runner
,
71 base::WaitableEvent
* shutdown_event
,
72 IPC::AttachmentBroker
* broker
) {
74 // On POSIX the channel_handle conveys an FD (socket) which is duped by the
75 // kernel during the IPC message exchange (via the SCM_RIGHTS mechanism).
76 // Ensure we do not leak this FD.
77 base::ScopedFD
fd(channel_handle
.socket
.auto_close
?
78 channel_handle
.socket
.fd
: -1);
81 scoped_refptr
<NPChannelBase
> channel
;
82 std::string channel_key
= channel_handle
.name
;
83 ChannelMap::const_iterator iter
= GetChannelMap()->find(channel_key
);
84 if (iter
== GetChannelMap()->end()) {
87 channel
= iter
->second
;
90 DCHECK(channel
.get() != NULL
);
92 if (!channel
->channel_valid()) {
93 channel
->channel_handle_
= channel_handle
;
95 ignore_result(fd
.release());
97 if (mode
& IPC::Channel::MODE_SERVER_FLAG
) {
98 channel
->channel_handle_
.name
=
99 IPC::Channel::GenerateVerifiedChannelID(channel_key
);
101 channel
->mode_
= mode
;
102 if (channel
->Init(ipc_task_runner
, create_pipe_now
, shutdown_event
,
104 (*GetChannelMap())[channel_key
] = channel
;
110 return channel
.get();
113 void NPChannelBase::Broadcast(IPC::Message
* message
) {
114 for (ChannelMap::iterator iter
= GetChannelMap()->begin();
115 iter
!= GetChannelMap()->end();
117 iter
->second
->Send(new IPC::Message(*message
));
122 NPChannelBase::NPChannelBase()
123 : mode_(IPC::Channel::MODE_NONE
),
124 non_npobject_count_(0),
126 in_remove_route_(false),
127 default_owner_(NULL
),
128 channel_valid_(false),
129 in_unblock_dispatch_(0),
130 send_unblocking_only_during_unblock_dispatch_(false) {
133 NPChannelBase::~NPChannelBase() {
134 // TODO(wez): Establish why these would ever be non-empty at teardown.
135 //DCHECK(npobject_listeners_.empty());
136 //DCHECK(proxy_map_.empty());
137 //DCHECK(stub_map_.empty());
138 DCHECK(owner_to_route_
.empty());
139 DCHECK(route_to_owner_
.empty());
142 NPChannelBase
* NPChannelBase::GetCurrentChannel() {
143 return GetChannelGlobals()->current_channel
.get();
146 void NPChannelBase::CleanupChannels() {
147 // Make a copy of the references as we can't iterate the map since items will
148 // be removed from it as we clean them up.
149 std::vector
<scoped_refptr
<NPChannelBase
> > channels
;
150 for (ChannelMap::const_iterator iter
= GetChannelMap()->begin();
151 iter
!= GetChannelMap()->end();
153 channels
.push_back(iter
->second
);
156 for (size_t i
= 0; i
< channels
.size(); ++i
)
157 channels
[i
]->CleanUp();
159 // This will clean up channels added to the map for which subsequent
160 // AddRoute wasn't called
161 GetChannelMap()->clear();
164 NPObjectBase
* NPChannelBase::GetNPObjectListenerForRoute(int route_id
) {
165 ListenerMap::iterator iter
= npobject_listeners_
.find(route_id
);
166 if (iter
== npobject_listeners_
.end()) {
167 DLOG(WARNING
) << "Invalid route id passed in:" << route_id
;
173 base::WaitableEvent
* NPChannelBase::GetModalDialogEvent(int render_view_id
) {
177 bool NPChannelBase::Init(base::SingleThreadTaskRunner
* ipc_task_runner
,
178 bool create_pipe_now
,
179 base::WaitableEvent
* shutdown_event
,
180 IPC::AttachmentBroker
* broker
) {
181 #if defined(OS_POSIX)
182 // Attempting to initialize with an invalid channel handle.
183 // See http://crbug.com/97285 for details.
184 if (mode_
== IPC::Channel::MODE_CLIENT
&& -1 == channel_handle_
.socket
.fd
)
189 IPC::SyncChannel::Create(channel_handle_
, mode_
, this, ipc_task_runner
,
190 create_pipe_now
, shutdown_event
, broker
);
192 #if defined(OS_POSIX)
193 // Check the validity of fd for bug investigation. Remove after fixed.
194 // See crbug.com/97285 for details.
195 if (mode_
== IPC::Channel::MODE_SERVER
)
196 CHECK_NE(-1, channel_
->GetClientFileDescriptor());
199 channel_valid_
= true;
203 bool NPChannelBase::Send(IPC::Message
* message
) {
205 VLOG(1) << "Channel is NULL; dropping message";
210 if (send_unblocking_only_during_unblock_dispatch_
&&
211 in_unblock_dispatch_
== 0 &&
212 message
->is_sync()) {
213 message
->set_unblock(false);
216 return channel_
->Send(message
);
219 int NPChannelBase::Count() {
220 return static_cast<int>(GetChannelMap()->size());
223 bool NPChannelBase::OnMessageReceived(const IPC::Message
& message
) {
224 // Push this channel as the current channel being processed. This also forms
225 // a stack of scoped_refptr avoiding ourselves (or any instance higher
226 // up the callstack) from being deleted while processing a message.
227 base::AutoReset
<scoped_refptr
<NPChannelBase
> > keep_alive(
228 &GetChannelGlobals()->current_channel
, this);
231 if (message
.should_unblock())
232 in_unblock_dispatch_
++;
233 if (message
.routing_id() == MSG_ROUTING_CONTROL
) {
234 handled
= OnControlMessageReceived(message
);
236 handled
= router_
.RouteMessage(message
);
237 if (!handled
&& message
.is_sync()) {
238 // The listener has gone away, so we must respond or else the caller will
239 // hang waiting for a reply.
240 IPC::Message
* reply
= IPC::SyncMessage::GenerateReply(&message
);
241 reply
->set_reply_error();
245 if (message
.should_unblock())
246 in_unblock_dispatch_
--;
251 void NPChannelBase::OnChannelConnected(int32 peer_pid
) {
252 peer_pid_
= peer_pid
;
255 void NPChannelBase::AddRoute(int route_id
,
256 IPC::Listener
* listener
,
257 NPObjectBase
* npobject
) {
259 npobject_listeners_
[route_id
] = npobject
;
261 non_npobject_count_
++;
264 router_
.AddRoute(route_id
, listener
);
267 void NPChannelBase::RemoveRoute(int route_id
) {
268 router_
.RemoveRoute(route_id
);
270 ListenerMap::iterator iter
= npobject_listeners_
.find(route_id
);
271 if (iter
!= npobject_listeners_
.end()) {
272 // This was an NPObject proxy or stub, it's not involved in the refcounting.
274 // If this RemoveRoute call from the NPObject is a result of us calling
275 // OnChannelError below, don't call erase() here because that'll corrupt
276 // the iterator below.
277 if (in_remove_route_
) {
280 npobject_listeners_
.erase(iter
);
286 non_npobject_count_
--;
287 DCHECK(non_npobject_count_
>= 0);
289 if (!non_npobject_count_
) {
290 base::AutoReset
<bool> auto_reset_in_remove_route(&in_remove_route_
, true);
291 for (ListenerMap::iterator npobj_iter
= npobject_listeners_
.begin();
292 npobj_iter
!= npobject_listeners_
.end(); ++npobj_iter
) {
293 if (npobj_iter
->second
) {
294 npobj_iter
->second
->GetChannelListener()->OnChannelError();
298 for (ChannelMap::iterator iter
= GetChannelMap()->begin();
299 iter
!= GetChannelMap()->end(); ++iter
) {
300 if (iter
->second
.get() == this) {
301 GetChannelMap()->erase(iter
);
310 bool NPChannelBase::OnControlMessageReceived(const IPC::Message
& msg
) {
312 "should override in subclass if you care about control messages";
316 void NPChannelBase::OnChannelError() {
317 channel_valid_
= false;
319 // TODO(shess): http://crbug.com/97285
320 // Once an error is seen on a channel, remap the channel to prevent
321 // it from being vended again. Keep the channel in the map so
322 // RemoveRoute() can clean things up correctly.
323 for (ChannelMap::iterator iter
= GetChannelMap()->begin();
324 iter
!= GetChannelMap()->end(); ++iter
) {
325 if (iter
->second
.get() == this) {
326 // Insert new element before invalidating |iter|.
327 (*GetChannelMap())[iter
->first
+ "-error"] = iter
->second
;
328 GetChannelMap()->erase(iter
);
334 void NPChannelBase::AddMappingForNPObjectProxy(int route_id
,
336 proxy_map_
[route_id
] = object
;
339 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id
) {
340 proxy_map_
.erase(route_id
);
343 void NPChannelBase::AddMappingForNPObjectStub(int route_id
,
345 DCHECK(object
!= NULL
);
346 stub_map_
[object
] = route_id
;
349 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id
,
351 DCHECK(object
!= NULL
);
352 stub_map_
.erase(object
);
355 void NPChannelBase::AddMappingForNPObjectOwner(int route_id
,
356 struct _NPP
* owner
) {
357 DCHECK(owner
!= NULL
);
358 route_to_owner_
[route_id
] = owner
;
359 owner_to_route_
[owner
] = route_id
;
362 void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP
* owner
) {
363 DCHECK(owner
!= NULL
);
364 default_owner_
= owner
;
367 void NPChannelBase::RemoveMappingForNPObjectOwner(int route_id
) {
368 DCHECK(route_to_owner_
.find(route_id
) != route_to_owner_
.end());
369 owner_to_route_
.erase(route_to_owner_
[route_id
]);
370 route_to_owner_
.erase(route_id
);
373 NPObject
* NPChannelBase::GetExistingNPObjectProxy(int route_id
) {
374 ProxyMap::iterator iter
= proxy_map_
.find(route_id
);
375 return iter
!= proxy_map_
.end() ? iter
->second
: NULL
;
378 int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject
* npobject
) {
379 StubMap::iterator iter
= stub_map_
.find(npobject
);
380 return iter
!= stub_map_
.end() ? iter
->second
: MSG_ROUTING_NONE
;
383 NPP
NPChannelBase::GetExistingNPObjectOwner(int route_id
) {
384 RouteToOwnerMap::iterator iter
= route_to_owner_
.find(route_id
);
385 return iter
!= route_to_owner_
.end() ? iter
->second
: default_owner_
;
388 int NPChannelBase::GetExistingRouteForNPObjectOwner(NPP owner
) {
389 OwnerToRouteMap::iterator iter
= owner_to_route_
.find(owner
);
390 return iter
!= owner_to_route_
.end() ? iter
->second
: MSG_ROUTING_NONE
;
393 } // namespace content