np_channel_base.cc revision f2477e01787aa58f445919b809d89e252beef54f
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/lazy_instance.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/threading/thread_local.h"
12#include "ipc/ipc_sync_message.h"
13
14#if defined(OS_POSIX)
15#include "base/file_util.h"
16#include "ipc/ipc_channel_posix.h"
17#endif
18
19namespace content {
20
21namespace {
22
23typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap;
24
25struct ChannelGlobals {
26  ChannelMap channel_map;
27  scoped_refptr<NPChannelBase> current_channel;
28};
29
30#if defined(OS_ANDROID)
31// Workaround for http://crbug.com/298179 - NPChannelBase is only intended
32// for use on one thread per process. Using TLS to store the globals removes the
33// worst thread hostility in this class, especially needed for webview which
34// runs in single-process mode. TODO(joth): Make a complete fix, most likely
35// as part of addressing http://crbug.com/258510.
36base::LazyInstance<base::ThreadLocalPointer<ChannelGlobals> >::Leaky
37    g_channels_tls_ptr = LAZY_INSTANCE_INITIALIZER;
38
39ChannelGlobals* GetChannelGlobals() {
40  ChannelGlobals* globals = g_channels_tls_ptr.Get().Get();
41  if (!globals) {
42    globals = new ChannelGlobals;
43    g_channels_tls_ptr.Get().Set(globals);
44  }
45  return globals;
46}
47
48#else
49
50base::LazyInstance<ChannelGlobals>::Leaky g_channels_globals =
51    LAZY_INSTANCE_INITIALIZER;
52
53ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); }
54
55#endif  // OS_ANDROID
56
57ChannelMap* GetChannelMap() {
58  return &GetChannelGlobals()->channel_map;
59}
60
61}  // namespace
62
63NPChannelBase* NPChannelBase::GetChannel(
64    const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode,
65    ChannelFactory factory, base::MessageLoopProxy* ipc_message_loop,
66    bool create_pipe_now, base::WaitableEvent* shutdown_event) {
67#if defined(OS_POSIX)
68  // On POSIX the channel_handle conveys an FD (socket) which is duped by the
69  // kernel during the IPC message exchange (via the SCM_RIGHTS mechanism).
70  // Ensure we do not leak this FD.
71  int fd = channel_handle.socket.auto_close ? channel_handle.socket.fd : -1;
72  file_util::ScopedFD auto_close_fd(&fd);
73#endif
74
75  scoped_refptr<NPChannelBase> channel;
76  std::string channel_key = channel_handle.name;
77  ChannelMap::const_iterator iter = GetChannelMap()->find(channel_key);
78  if (iter == GetChannelMap()->end()) {
79    channel = factory();
80  } else {
81    channel = iter->second;
82  }
83
84  DCHECK(channel.get() != NULL);
85
86  if (!channel->channel_valid()) {
87    channel->channel_handle_ = channel_handle;
88#if defined(OS_POSIX)
89    ignore_result(auto_close_fd.release());
90#endif
91    if (mode & IPC::Channel::MODE_SERVER_FLAG) {
92      channel->channel_handle_.name =
93          IPC::Channel::GenerateVerifiedChannelID(channel_key);
94    }
95    channel->mode_ = mode;
96    if (channel->Init(ipc_message_loop, create_pipe_now, shutdown_event)) {
97      (*GetChannelMap())[channel_key] = channel;
98    } else {
99      channel = NULL;
100    }
101  }
102
103  return channel.get();
104}
105
106void NPChannelBase::Broadcast(IPC::Message* message) {
107  for (ChannelMap::iterator iter = GetChannelMap()->begin();
108       iter != GetChannelMap()->end();
109       ++iter) {
110    iter->second->Send(new IPC::Message(*message));
111  }
112  delete message;
113}
114
115NPChannelBase::NPChannelBase()
116    : mode_(IPC::Channel::MODE_NONE),
117      non_npobject_count_(0),
118      peer_pid_(0),
119      in_remove_route_(false),
120      default_owner_(NULL),
121      channel_valid_(false),
122      in_unblock_dispatch_(0),
123      send_unblocking_only_during_unblock_dispatch_(false) {
124}
125
126NPChannelBase::~NPChannelBase() {
127  // TODO(wez): Establish why these would ever be non-empty at teardown.
128  //DCHECK(npobject_listeners_.empty());
129  //DCHECK(proxy_map_.empty());
130  //DCHECK(stub_map_.empty());
131  DCHECK(owner_to_route_.empty());
132  DCHECK(route_to_owner_.empty());
133}
134
135NPChannelBase* NPChannelBase::GetCurrentChannel() {
136  return GetChannelGlobals()->current_channel.get();
137}
138
139void NPChannelBase::CleanupChannels() {
140  // Make a copy of the references as we can't iterate the map since items will
141  // be removed from it as we clean them up.
142  std::vector<scoped_refptr<NPChannelBase> > channels;
143  for (ChannelMap::const_iterator iter = GetChannelMap()->begin();
144       iter != GetChannelMap()->end();
145       ++iter) {
146    channels.push_back(iter->second);
147  }
148
149  for (size_t i = 0; i < channels.size(); ++i)
150    channels[i]->CleanUp();
151
152  // This will clean up channels added to the map for which subsequent
153  // AddRoute wasn't called
154  GetChannelMap()->clear();
155}
156
157NPObjectBase* NPChannelBase::GetNPObjectListenerForRoute(int route_id) {
158  ListenerMap::iterator iter = npobject_listeners_.find(route_id);
159  if (iter == npobject_listeners_.end()) {
160    DLOG(WARNING) << "Invalid route id passed in:" << route_id;
161    return NULL;
162  }
163  return iter->second;
164}
165
166base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
167  return NULL;
168}
169
170bool NPChannelBase::Init(base::MessageLoopProxy* ipc_message_loop,
171                         bool create_pipe_now,
172                         base::WaitableEvent* shutdown_event) {
173#if defined(OS_POSIX)
174  // Attempting to initialize with an invalid channel handle.
175  // See http://crbug.com/97285 for details.
176  if (mode_ == IPC::Channel::MODE_CLIENT && -1 == channel_handle_.socket.fd)
177    return false;
178#endif
179
180  channel_.reset(new IPC::SyncChannel(
181      channel_handle_, mode_, this, ipc_message_loop, create_pipe_now,
182      shutdown_event));
183
184#if defined(OS_POSIX)
185  // Check the validity of fd for bug investigation.  Remove after fixed.
186  // See crbug.com/97285 for details.
187  if (mode_ == IPC::Channel::MODE_SERVER)
188    CHECK_NE(-1, channel_->GetClientFileDescriptor());
189#endif
190
191  channel_valid_ = true;
192  return true;
193}
194
195bool NPChannelBase::Send(IPC::Message* message) {
196  if (!channel_) {
197    VLOG(1) << "Channel is NULL; dropping message";
198    delete message;
199    return false;
200  }
201
202  if (send_unblocking_only_during_unblock_dispatch_ &&
203      in_unblock_dispatch_ == 0 &&
204      message->is_sync()) {
205    message->set_unblock(false);
206  }
207
208  return channel_->Send(message);
209}
210
211int NPChannelBase::Count() {
212  return static_cast<int>(GetChannelMap()->size());
213}
214
215bool NPChannelBase::OnMessageReceived(const IPC::Message& message) {
216  // Push this channel as the current channel being processed. This also forms
217  // a stack of scoped_refptr avoiding ourselves (or any instance higher
218  // up the callstack) from being deleted while processing a message.
219  base::AutoReset<scoped_refptr<NPChannelBase> > keep_alive(
220      &GetChannelGlobals()->current_channel, this);
221
222  bool handled;
223  if (message.should_unblock())
224    in_unblock_dispatch_++;
225  if (message.routing_id() == MSG_ROUTING_CONTROL) {
226    handled = OnControlMessageReceived(message);
227  } else {
228    handled = router_.RouteMessage(message);
229    if (!handled && message.is_sync()) {
230      // The listener has gone away, so we must respond or else the caller will
231      // hang waiting for a reply.
232      IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
233      reply->set_reply_error();
234      Send(reply);
235    }
236  }
237  if (message.should_unblock())
238    in_unblock_dispatch_--;
239
240  return handled;
241}
242
243void NPChannelBase::OnChannelConnected(int32 peer_pid) {
244  peer_pid_ = peer_pid;
245}
246
247void NPChannelBase::AddRoute(int route_id,
248                             IPC::Listener* listener,
249                             NPObjectBase* npobject) {
250  if (npobject) {
251    npobject_listeners_[route_id] = npobject;
252  } else {
253    non_npobject_count_++;
254  }
255
256  router_.AddRoute(route_id, listener);
257}
258
259void NPChannelBase::RemoveRoute(int route_id) {
260  router_.RemoveRoute(route_id);
261
262  ListenerMap::iterator iter = npobject_listeners_.find(route_id);
263  if (iter != npobject_listeners_.end()) {
264    // This was an NPObject proxy or stub, it's not involved in the refcounting.
265
266    // If this RemoveRoute call from the NPObject is a result of us calling
267    // OnChannelError below, don't call erase() here because that'll corrupt
268    // the iterator below.
269    if (in_remove_route_) {
270      iter->second = NULL;
271    } else {
272      npobject_listeners_.erase(iter);
273    }
274
275    return;
276  }
277
278  non_npobject_count_--;
279  DCHECK(non_npobject_count_ >= 0);
280
281  if (!non_npobject_count_) {
282    base::AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true);
283    for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin();
284         npobj_iter != npobject_listeners_.end(); ++npobj_iter) {
285      if (npobj_iter->second) {
286        npobj_iter->second->GetChannelListener()->OnChannelError();
287      }
288    }
289
290    for (ChannelMap::iterator iter = GetChannelMap()->begin();
291         iter != GetChannelMap()->end(); ++iter) {
292      if (iter->second.get() == this) {
293        GetChannelMap()->erase(iter);
294        return;
295      }
296    }
297
298    NOTREACHED();
299  }
300}
301
302bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
303  NOTREACHED() <<
304      "should override in subclass if you care about control messages";
305  return false;
306}
307
308void NPChannelBase::OnChannelError() {
309  channel_valid_ = false;
310
311  // TODO(shess): http://crbug.com/97285
312  // Once an error is seen on a channel, remap the channel to prevent
313  // it from being vended again.  Keep the channel in the map so
314  // RemoveRoute() can clean things up correctly.
315  for (ChannelMap::iterator iter = GetChannelMap()->begin();
316       iter != GetChannelMap()->end(); ++iter) {
317    if (iter->second.get() == this) {
318      // Insert new element before invalidating |iter|.
319      (*GetChannelMap())[iter->first + "-error"] = iter->second;
320      GetChannelMap()->erase(iter);
321      break;
322    }
323  }
324}
325
326void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
327                                               NPObject* object) {
328  proxy_map_[route_id] = object;
329}
330
331void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) {
332  proxy_map_.erase(route_id);
333}
334
335void NPChannelBase::AddMappingForNPObjectStub(int route_id,
336                                              NPObject* object) {
337  DCHECK(object != NULL);
338  stub_map_[object] = route_id;
339}
340
341void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
342                                                 NPObject* object) {
343  DCHECK(object != NULL);
344  stub_map_.erase(object);
345}
346
347void NPChannelBase::AddMappingForNPObjectOwner(int route_id,
348                                               struct _NPP* owner) {
349  DCHECK(owner != NULL);
350  route_to_owner_[route_id] = owner;
351  owner_to_route_[owner] = route_id;
352}
353
354void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) {
355  DCHECK(owner != NULL);
356  default_owner_ = owner;
357}
358
359void NPChannelBase::RemoveMappingForNPObjectOwner(int route_id) {
360  DCHECK(route_to_owner_.find(route_id) != route_to_owner_.end());
361  owner_to_route_.erase(route_to_owner_[route_id]);
362  route_to_owner_.erase(route_id);
363}
364
365NPObject* NPChannelBase::GetExistingNPObjectProxy(int route_id) {
366  ProxyMap::iterator iter = proxy_map_.find(route_id);
367  return iter != proxy_map_.end() ? iter->second : NULL;
368}
369
370int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject* npobject) {
371  StubMap::iterator iter = stub_map_.find(npobject);
372  return iter != stub_map_.end() ? iter->second : MSG_ROUTING_NONE;
373}
374
375NPP NPChannelBase::GetExistingNPObjectOwner(int route_id) {
376  RouteToOwnerMap::iterator iter = route_to_owner_.find(route_id);
377  return iter != route_to_owner_.end() ? iter->second : default_owner_;
378}
379
380int NPChannelBase::GetExistingRouteForNPObjectOwner(NPP owner) {
381  OwnerToRouteMap::iterator iter = owner_to_route_.find(owner);
382  return iter != owner_to_route_.end() ? iter->second : MSG_ROUTING_NONE;
383}
384
385}  // namespace content
386