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.
5 #include "content/common/np_channel_base.h"
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"
16 #include "ipc/ipc_channel_posix.h"
21 typedef base::hash_map
<std::string
, scoped_refptr
<NPChannelBase
> > ChannelMap
;
22 static base::LazyInstance
<ChannelMap
>::Leaky
23 g_channels
= LAZY_INSTANCE_INITIALIZER
;
25 typedef std::stack
<scoped_refptr
<NPChannelBase
> > NPChannelRefStack
;
26 static base::LazyInstance
<NPChannelRefStack
>::Leaky
27 g_lazy_channel_stack
= LAZY_INSTANCE_INITIALIZER
;
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()) {
39 channel
= iter
->second
;
42 DCHECK(channel
!= NULL
);
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
);
50 channel
->mode_
= mode
;
51 if (channel
->Init(ipc_message_loop
, create_pipe_now
, shutdown_event
)) {
52 g_channels
.Get()[channel_key
] = channel
;
61 void NPChannelBase::Broadcast(IPC::Message
* message
) {
62 for (ChannelMap::iterator iter
= g_channels
.Get().begin();
63 iter
!= g_channels
.Get().end();
65 iter
->second
->Send(new IPC::Message(*message
));
70 NPChannelBase::NPChannelBase()
71 : mode_(IPC::Channel::MODE_NONE
),
72 non_npobject_count_(0),
74 in_remove_route_(false),
75 channel_valid_(false),
76 in_unblock_dispatch_(0),
77 send_unblocking_only_during_unblock_dispatch_(false) {
80 NPChannelBase::~NPChannelBase() {
83 NPChannelBase
* NPChannelBase::GetCurrentChannel() {
84 return g_lazy_channel_stack
.Pointer()->top();
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();
94 channels
.push_back(iter
->second
);
97 for (size_t i
= 0; i
< channels
.size(); ++i
)
98 channels
[i
]->CleanUp();
100 // This will clean up channels added to the map for which subsequent
101 // AddRoute wasn't called
102 g_channels
.Get().clear();
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
;
114 base::WaitableEvent
* NPChannelBase::GetModalDialogEvent(int render_view_id
) {
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
)
128 channel_
.reset(new IPC::SyncChannel(
129 channel_handle_
, mode_
, this, ipc_message_loop
, create_pipe_now
,
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());
139 channel_valid_
= true;
143 bool NPChannelBase::Send(IPC::Message
* message
) {
144 if (!channel_
.get()) {
145 VLOG(1) << "Channel is NULL; dropping message";
150 if (send_unblocking_only_during_unblock_dispatch_
&&
151 in_unblock_dispatch_
== 0 &&
152 message
->is_sync()) {
153 message
->set_unblock(false);
156 return channel_
->Send(message
);
159 int NPChannelBase::Count() {
160 return static_cast<int>(g_channels
.Get().size());
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));
170 if (message
.should_unblock())
171 in_unblock_dispatch_
++;
172 if (message
.routing_id() == MSG_ROUTING_CONTROL
) {
173 handled
= OnControlMessageReceived(message
);
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();
184 if (message
.should_unblock())
185 in_unblock_dispatch_
--;
187 g_lazy_channel_stack
.Pointer()->pop();
191 void NPChannelBase::OnChannelConnected(int32 peer_pid
) {
192 peer_pid_
= peer_pid
;
195 void NPChannelBase::AddRoute(int route_id
,
196 IPC::Listener
* listener
,
197 NPObjectBase
* npobject
) {
199 npobject_listeners_
[route_id
] = npobject
;
201 non_npobject_count_
++;
204 router_
.AddRoute(route_id
, listener
);
207 void NPChannelBase::RemoveRoute(int route_id
) {
208 router_
.RemoveRoute(route_id
);
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.
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_
) {
220 npobject_listeners_
.erase(iter
);
226 non_npobject_count_
--;
227 DCHECK(non_npobject_count_
>= 0);
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();
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
);
250 bool NPChannelBase::OnControlMessageReceived(const IPC::Message
& msg
) {
252 "should override in subclass if you care about control messages";
256 void NPChannelBase::OnChannelError() {
257 channel_valid_
= false;
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
);
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
;
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
;
284 void NPChannelBase::AddMappingForNPObjectProxy(int route_id
,
286 proxy_map_
[route_id
] = object
;
289 void NPChannelBase::AddMappingForNPObjectStub(int route_id
,
291 DCHECK(object
!= NULL
);
292 stub_map_
[object
] = route_id
;
295 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id
,
297 DCHECK(object
!= NULL
);
298 stub_map_
.erase(object
);
301 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id
) {
302 proxy_map_
.erase(route_id
);
305 } // namespace content