1 // Copyright (c) 2011 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"
19 typedef base::hash_map
<std::string
, scoped_refptr
<NPChannelBase
> > ChannelMap
;
20 static base::LazyInstance
<ChannelMap
,
21 base::LeakyLazyInstanceTraits
<ChannelMap
> >
22 g_channels
= LAZY_INSTANCE_INITIALIZER
;
24 typedef std::stack
<scoped_refptr
<NPChannelBase
> > NPChannelRefStack
;
25 static base::LazyInstance
<NPChannelRefStack
,
26 base::LeakyLazyInstanceTraits
<NPChannelRefStack
> >
27 g_lazy_channel_stack
= LAZY_INSTANCE_INITIALIZER
;
29 static int next_pipe_id
= 0;
31 NPChannelBase
* NPChannelBase::GetChannel(
32 const IPC::ChannelHandle
& channel_handle
, IPC::Channel::Mode mode
,
33 ChannelFactory factory
, base::MessageLoopProxy
* ipc_message_loop
,
34 bool create_pipe_now
, base::WaitableEvent
* shutdown_event
) {
35 scoped_refptr
<NPChannelBase
> channel
;
36 std::string channel_key
= channel_handle
.name
;
37 ChannelMap::const_iterator iter
= g_channels
.Get().find(channel_key
);
38 if (iter
== g_channels
.Get().end()) {
41 channel
= iter
->second
;
44 DCHECK(channel
!= NULL
);
46 if (!channel
->channel_valid()) {
47 channel
->channel_handle_
= channel_handle
;
48 if (mode
& IPC::Channel::MODE_SERVER_FLAG
) {
49 channel
->channel_handle_
.name
.append(".");
50 channel
->channel_handle_
.name
.append(base::IntToString(next_pipe_id
++));
52 channel
->mode_
= mode
;
53 if (channel
->Init(ipc_message_loop
, create_pipe_now
, shutdown_event
)) {
54 g_channels
.Get()[channel_key
] = channel
;
63 void NPChannelBase::Broadcast(IPC::Message
* message
) {
64 for (ChannelMap::iterator iter
= g_channels
.Get().begin();
65 iter
!= g_channels
.Get().end();
67 iter
->second
->Send(new IPC::Message(*message
));
72 NPChannelBase::NPChannelBase()
73 : mode_(IPC::Channel::MODE_NONE
),
74 non_npobject_count_(0),
76 in_remove_route_(false),
77 channel_valid_(false),
78 in_unblock_dispatch_(0),
79 send_unblocking_only_during_unblock_dispatch_(false) {
82 NPChannelBase::~NPChannelBase() {
85 NPChannelBase
* NPChannelBase::GetCurrentChannel() {
86 return g_lazy_channel_stack
.Pointer()->top();
89 void NPChannelBase::CleanupChannels() {
90 // Make a copy of the references as we can't iterate the map since items will
91 // be removed from it as we clean them up.
92 std::vector
<scoped_refptr
<NPChannelBase
> > channels
;
93 for (ChannelMap::const_iterator iter
= g_channels
.Get().begin();
94 iter
!= g_channels
.Get().end();
96 channels
.push_back(iter
->second
);
99 for (size_t i
= 0; i
< channels
.size(); ++i
)
100 channels
[i
]->CleanUp();
102 // This will clean up channels added to the map for which subsequent
103 // AddRoute wasn't called
104 g_channels
.Get().clear();
107 NPObjectBase
* NPChannelBase::GetNPObjectListenerForRoute(int route_id
) {
108 ListenerMap::iterator iter
= npobject_listeners_
.find(route_id
);
109 if (iter
== npobject_listeners_
.end()) {
110 DLOG(WARNING
) << "Invalid route id passed in:" << route_id
;
116 base::WaitableEvent
* NPChannelBase::GetModalDialogEvent(
117 gfx::NativeViewId containing_window
) {
121 bool NPChannelBase::Init(base::MessageLoopProxy
* ipc_message_loop
,
122 bool create_pipe_now
,
123 base::WaitableEvent
* shutdown_event
) {
124 #if defined(OS_POSIX)
125 // Check the validity of fd for bug investigation. Remove after fixed.
126 // See for details: crbug.com/95129, crbug.com/97285.
127 if (mode_
== IPC::Channel::MODE_CLIENT
)
128 CHECK_NE(-1, channel_handle_
.socket
.fd
);
131 channel_
.reset(new IPC::SyncChannel(
132 channel_handle_
, mode_
, this, ipc_message_loop
, create_pipe_now
,
135 #if defined(OS_POSIX)
136 // Check the validity of fd for bug investigation. Remove after fixed.
137 // See for details: crbug.com/95129, crbug.com/97285.
138 if (mode_
== IPC::Channel::MODE_SERVER
)
139 CHECK_NE(-1, channel_
->GetClientFileDescriptor());
142 channel_valid_
= true;
146 bool NPChannelBase::Send(IPC::Message
* message
) {
147 if (!channel_
.get()) {
152 if (send_unblocking_only_during_unblock_dispatch_
&&
153 in_unblock_dispatch_
== 0 &&
154 message
->is_sync()) {
155 message
->set_unblock(false);
158 return channel_
->Send(message
);
161 int NPChannelBase::Count() {
162 return static_cast<int>(g_channels
.Get().size());
165 bool NPChannelBase::OnMessageReceived(const IPC::Message
& message
) {
166 // This call might cause us to be deleted, so keep an extra reference to
167 // ourself so that we can send the reply and decrement back in_dispatch_.
168 g_lazy_channel_stack
.Pointer()->push(
169 scoped_refptr
<NPChannelBase
>(this));
172 if (message
.should_unblock())
173 in_unblock_dispatch_
++;
174 if (message
.routing_id() == MSG_ROUTING_CONTROL
) {
175 handled
= OnControlMessageReceived(message
);
177 handled
= router_
.RouteMessage(message
);
178 if (!handled
&& message
.is_sync()) {
179 // The listener has gone away, so we must respond or else the caller will
180 // hang waiting for a reply.
181 IPC::Message
* reply
= IPC::SyncMessage::GenerateReply(&message
);
182 reply
->set_reply_error();
186 if (message
.should_unblock())
187 in_unblock_dispatch_
--;
189 g_lazy_channel_stack
.Pointer()->pop();
193 void NPChannelBase::OnChannelConnected(int32 peer_pid
) {
194 peer_pid_
= peer_pid
;
197 void NPChannelBase::AddRoute(int route_id
,
198 IPC::Channel::Listener
* listener
,
199 NPObjectBase
* npobject
) {
201 npobject_listeners_
[route_id
] = npobject
;
203 non_npobject_count_
++;
206 router_
.AddRoute(route_id
, listener
);
209 void NPChannelBase::RemoveRoute(int route_id
) {
210 router_
.RemoveRoute(route_id
);
212 ListenerMap::iterator iter
= npobject_listeners_
.find(route_id
);
213 if (iter
!= npobject_listeners_
.end()) {
214 // This was an NPObject proxy or stub, it's not involved in the refcounting.
216 // If this RemoveRoute call from the NPObject is a result of us calling
217 // OnChannelError below, don't call erase() here because that'll corrupt
218 // the iterator below.
219 if (in_remove_route_
) {
222 npobject_listeners_
.erase(iter
);
228 non_npobject_count_
--;
229 DCHECK(non_npobject_count_
>= 0);
231 if (!non_npobject_count_
) {
232 AutoReset
<bool> auto_reset_in_remove_route(&in_remove_route_
, true);
233 for (ListenerMap::iterator npobj_iter
= npobject_listeners_
.begin();
234 npobj_iter
!= npobject_listeners_
.end(); ++npobj_iter
) {
235 if (npobj_iter
->second
) {
236 IPC::Channel::Listener
* channel_listener
=
237 npobj_iter
->second
->GetChannelListener();
238 DCHECK(channel_listener
!= NULL
);
239 channel_listener
->OnChannelError();
243 for (ChannelMap::iterator iter
= g_channels
.Get().begin();
244 iter
!= g_channels
.Get().end(); ++iter
) {
245 if (iter
->second
== this) {
246 g_channels
.Get().erase(iter
);
255 bool NPChannelBase::OnControlMessageReceived(const IPC::Message
& msg
) {
257 "should override in subclass if you care about control messages";
261 void NPChannelBase::OnChannelError() {
262 channel_valid_
= false;
265 NPObject
* NPChannelBase::GetExistingNPObjectProxy(int route_id
) {
266 ProxyMap::iterator iter
= proxy_map_
.find(route_id
);
267 return iter
!= proxy_map_
.end() ? iter
->second
: NULL
;
270 int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject
* npobject
) {
271 StubMap::iterator iter
= stub_map_
.find(npobject
);
272 return iter
!= stub_map_
.end() ? iter
->second
: MSG_ROUTING_NONE
;
275 void NPChannelBase::AddMappingForNPObjectProxy(int route_id
,
277 proxy_map_
[route_id
] = object
;
280 void NPChannelBase::AddMappingForNPObjectStub(int route_id
,
282 DCHECK(object
!= NULL
);
283 stub_map_
[object
] = route_id
;
286 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id
,
288 DCHECK(object
!= NULL
);
289 stub_map_
.erase(object
);
292 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id
) {
293 proxy_map_
.erase(route_id
);