Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / child / npapi / np_channel_base.cc
blobc39ff4cdc2a657185498289dbd01f14ae7ffb392
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"
15 #if defined(OS_POSIX)
16 #include "base/files/file_util.h"
17 #include "ipc/ipc_channel_posix.h"
18 #endif
20 namespace content {
22 namespace {
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();
42 if (!globals) {
43 globals = new ChannelGlobals;
44 g_channels_tls_ptr.Get().Set(globals);
46 return globals;
49 #else
51 base::LazyInstance<ChannelGlobals>::Leaky g_channels_globals =
52 LAZY_INSTANCE_INITIALIZER;
54 ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); }
56 #endif // OS_ANDROID
58 ChannelMap* GetChannelMap() {
59 return &GetChannelGlobals()->channel_map;
62 } // namespace
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) {
68 #if defined(OS_POSIX)
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);
74 #endif
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()) {
80 channel = factory();
81 } else {
82 channel = iter->second;
85 DCHECK(channel.get() != NULL);
87 if (!channel->channel_valid()) {
88 channel->channel_handle_ = channel_handle;
89 #if defined(OS_POSIX)
90 ignore_result(fd.release());
91 #endif
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;
99 } else {
100 channel = NULL;
104 return channel.get();
107 void NPChannelBase::Broadcast(IPC::Message* message) {
108 for (ChannelMap::iterator iter = GetChannelMap()->begin();
109 iter != GetChannelMap()->end();
110 ++iter) {
111 iter->second->Send(new IPC::Message(*message));
113 delete message;
116 NPChannelBase::NPChannelBase()
117 : mode_(IPC::Channel::MODE_NONE),
118 non_npobject_count_(0),
119 peer_pid_(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();
146 ++iter) {
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;
162 return NULL;
164 return iter->second;
167 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
168 return NULL;
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)
178 return false;
179 #endif
181 channel_ = IPC::SyncChannel::Create(
182 channel_handle_, mode_, this, ipc_message_loop, create_pipe_now,
183 shutdown_event);
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());
190 #endif
192 channel_valid_ = true;
193 return true;
196 bool NPChannelBase::Send(IPC::Message* message) {
197 if (!channel_) {
198 VLOG(1) << "Channel is NULL; dropping message";
199 delete message;
200 return false;
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);
223 bool handled;
224 if (message.should_unblock())
225 in_unblock_dispatch_++;
226 if (message.routing_id() == MSG_ROUTING_CONTROL) {
227 handled = OnControlMessageReceived(message);
228 } else {
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();
235 Send(reply);
238 if (message.should_unblock())
239 in_unblock_dispatch_--;
241 return handled;
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) {
251 if (npobject) {
252 npobject_listeners_[route_id] = npobject;
253 } else {
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_) {
271 iter->second = NULL;
272 } else {
273 npobject_listeners_.erase(iter);
276 return;
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);
295 return;
299 NOTREACHED();
303 bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
304 NOTREACHED() <<
305 "should override in subclass if you care about control messages";
306 return false;
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);
322 break;
327 void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
328 NPObject* object) {
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,
337 NPObject* object) {
338 DCHECK(object != NULL);
339 stub_map_[object] = route_id;
342 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
343 NPObject* object) {
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