np_channel_base.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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.
4
5#include "content/child/npapi/np_channel_base.h"
6
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"
14
15#if defined(OS_POSIX)
16#include "base/file_util.h"
17#include "ipc/ipc_channel_posix.h"
18#endif
19
20namespace content {
21
22namespace {
23
24typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap;
25
26struct ChannelGlobals {
27  ChannelMap channel_map;
28  scoped_refptr<NPChannelBase> current_channel;
29};
30
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.
37base::LazyInstance<base::ThreadLocalPointer<ChannelGlobals> >::Leaky
38    g_channels_tls_ptr = LAZY_INSTANCE_INITIALIZER;
39
40ChannelGlobals* 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);
45  }
46  return globals;
47}
48
49#else
50
51base::LazyInstance<ChannelGlobals>::Leaky g_channels_globals =
52    LAZY_INSTANCE_INITIALIZER;
53
54ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); }
55
56#endif  // OS_ANDROID
57
58ChannelMap* GetChannelMap() {
59  return &GetChannelGlobals()->channel_map;
60}
61
62}  // namespace
63
64NPChannelBase* 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
75
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;
83  }
84
85  DCHECK(channel.get() != NULL);
86
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);
95    }
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;
101    }
102  }
103
104  return channel.get();
105}
106
107void 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));
112  }
113  delete message;
114}
115
116NPChannelBase::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) {
125}
126
127NPChannelBase::~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());
134}
135
136NPChannelBase* NPChannelBase::GetCurrentChannel() {
137  return GetChannelGlobals()->current_channel.get();
138}
139
140void 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);
148  }
149
150  for (size_t i = 0; i < channels.size(); ++i)
151    channels[i]->CleanUp();
152
153  // This will clean up channels added to the map for which subsequent
154  // AddRoute wasn't called
155  GetChannelMap()->clear();
156}
157
158NPObjectBase* 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;
163  }
164  return iter->second;
165}
166
167base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
168  return NULL;
169}
170
171bool 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
180
181  channel_ = IPC::SyncChannel::Create(
182      channel_handle_, mode_, this, ipc_message_loop, create_pipe_now,
183      shutdown_event);
184
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
191
192  channel_valid_ = true;
193  return true;
194}
195
196bool NPChannelBase::Send(IPC::Message* message) {
197  if (!channel_) {
198    VLOG(1) << "Channel is NULL; dropping message";
199    delete message;
200    return false;
201  }
202
203  if (send_unblocking_only_during_unblock_dispatch_ &&
204      in_unblock_dispatch_ == 0 &&
205      message->is_sync()) {
206    message->set_unblock(false);
207  }
208
209  return channel_->Send(message);
210}
211
212int NPChannelBase::Count() {
213  return static_cast<int>(GetChannelMap()->size());
214}
215
216bool 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);
222
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);
236    }
237  }
238  if (message.should_unblock())
239    in_unblock_dispatch_--;
240
241  return handled;
242}
243
244void NPChannelBase::OnChannelConnected(int32 peer_pid) {
245  peer_pid_ = peer_pid;
246}
247
248void 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_++;
255  }
256
257  router_.AddRoute(route_id, listener);
258}
259
260void NPChannelBase::RemoveRoute(int route_id) {
261  router_.RemoveRoute(route_id);
262
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.
266
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);
274    }
275
276    return;
277  }
278
279  non_npobject_count_--;
280  DCHECK(non_npobject_count_ >= 0);
281
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();
288      }
289    }
290
291    for (ChannelMap::iterator iter = GetChannelMap()->begin();
292         iter != GetChannelMap()->end(); ++iter) {
293      if (iter->second.get() == this) {
294        GetChannelMap()->erase(iter);
295        return;
296      }
297    }
298
299    NOTREACHED();
300  }
301}
302
303bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
304  NOTREACHED() <<
305      "should override in subclass if you care about control messages";
306  return false;
307}
308
309void NPChannelBase::OnChannelError() {
310  channel_valid_ = false;
311
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;
323    }
324  }
325}
326
327void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
328                                               NPObject* object) {
329  proxy_map_[route_id] = object;
330}
331
332void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) {
333  proxy_map_.erase(route_id);
334}
335
336void NPChannelBase::AddMappingForNPObjectStub(int route_id,
337                                              NPObject* object) {
338  DCHECK(object != NULL);
339  stub_map_[object] = route_id;
340}
341
342void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
343                                                 NPObject* object) {
344  DCHECK(object != NULL);
345  stub_map_.erase(object);
346}
347
348void 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;
353}
354
355void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) {
356  DCHECK(owner != NULL);
357  default_owner_ = owner;
358}
359
360void 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);
364}
365
366NPObject* NPChannelBase::GetExistingNPObjectProxy(int route_id) {
367  ProxyMap::iterator iter = proxy_map_.find(route_id);
368  return iter != proxy_map_.end() ? iter->second : NULL;
369}
370
371int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject* npobject) {
372  StubMap::iterator iter = stub_map_.find(npobject);
373  return iter != stub_map_.end() ? iter->second : MSG_ROUTING_NONE;
374}
375
376NPP 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_;
379}
380
381int 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;
384}
385
386}  // namespace content
387