np_channel_base.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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 <stack>
8
9#include "base/auto_reset.h"
10#include "base/containers/hash_tables.h"
11#include "base/lazy_instance.h"
12#include "base/strings/string_number_conversions.h"
13#include "ipc/ipc_sync_message.h"
14
15#if defined(OS_POSIX)
16#include "ipc/ipc_channel_posix.h"
17#endif
18
19namespace content {
20
21typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap;
22static base::LazyInstance<ChannelMap>::Leaky
23     g_channels = LAZY_INSTANCE_INITIALIZER;
24
25typedef std::stack<scoped_refptr<NPChannelBase> > NPChannelRefStack;
26static base::LazyInstance<NPChannelRefStack>::Leaky
27    g_lazy_channel_stack = LAZY_INSTANCE_INITIALIZER;
28
29NPChannelBase* 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()) {
37    channel = factory();
38  } else {
39    channel = iter->second;
40  }
41
42  DCHECK(channel.get() != NULL);
43
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);
49    }
50    channel->mode_ = mode;
51    if (channel->Init(ipc_message_loop, create_pipe_now, shutdown_event)) {
52      g_channels.Get()[channel_key] = channel;
53    } else {
54      channel = NULL;
55    }
56  }
57
58  return channel.get();
59}
60
61void NPChannelBase::Broadcast(IPC::Message* message) {
62  for (ChannelMap::iterator iter = g_channels.Get().begin();
63       iter != g_channels.Get().end();
64       ++iter) {
65    iter->second->Send(new IPC::Message(*message));
66  }
67  delete message;
68}
69
70NPChannelBase::NPChannelBase()
71    : mode_(IPC::Channel::MODE_NONE),
72      non_npobject_count_(0),
73      peer_pid_(0),
74      in_remove_route_(false),
75      default_owner_(NULL),
76      channel_valid_(false),
77      in_unblock_dispatch_(0),
78      send_unblocking_only_during_unblock_dispatch_(false) {
79}
80
81NPChannelBase::~NPChannelBase() {
82  // TODO(wez): Establish why these would ever be non-empty at teardown.
83  //DCHECK(npobject_listeners_.empty());
84  //DCHECK(proxy_map_.empty());
85  //DCHECK(stub_map_.empty());
86  DCHECK(owner_to_route_.empty());
87  DCHECK(route_to_owner_.empty());
88}
89
90NPChannelBase* NPChannelBase::GetCurrentChannel() {
91  return g_lazy_channel_stack.Pointer()->top().get();
92}
93
94void NPChannelBase::CleanupChannels() {
95  // Make a copy of the references as we can't iterate the map since items will
96  // be removed from it as we clean them up.
97  std::vector<scoped_refptr<NPChannelBase> > channels;
98  for (ChannelMap::const_iterator iter = g_channels.Get().begin();
99       iter != g_channels.Get().end();
100       ++iter) {
101    channels.push_back(iter->second);
102  }
103
104  for (size_t i = 0; i < channels.size(); ++i)
105    channels[i]->CleanUp();
106
107  // This will clean up channels added to the map for which subsequent
108  // AddRoute wasn't called
109  g_channels.Get().clear();
110}
111
112NPObjectBase* NPChannelBase::GetNPObjectListenerForRoute(int route_id) {
113  ListenerMap::iterator iter = npobject_listeners_.find(route_id);
114  if (iter == npobject_listeners_.end()) {
115    DLOG(WARNING) << "Invalid route id passed in:" << route_id;
116    return NULL;
117  }
118  return iter->second;
119}
120
121base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
122  return NULL;
123}
124
125bool NPChannelBase::Init(base::MessageLoopProxy* ipc_message_loop,
126                         bool create_pipe_now,
127                         base::WaitableEvent* shutdown_event) {
128#if defined(OS_POSIX)
129  // Attempting to initialize with an invalid channel handle.
130  // See http://crbug.com/97285 for details.
131  if (mode_ == IPC::Channel::MODE_CLIENT && -1 == channel_handle_.socket.fd)
132    return false;
133#endif
134
135  channel_.reset(new IPC::SyncChannel(
136      channel_handle_, mode_, this, ipc_message_loop, create_pipe_now,
137      shutdown_event));
138
139#if defined(OS_POSIX)
140  // Check the validity of fd for bug investigation.  Remove after fixed.
141  // See crbug.com/97285 for details.
142  if (mode_ == IPC::Channel::MODE_SERVER)
143    CHECK_NE(-1, channel_->GetClientFileDescriptor());
144#endif
145
146  channel_valid_ = true;
147  return true;
148}
149
150bool NPChannelBase::Send(IPC::Message* message) {
151  if (!channel_) {
152    VLOG(1) << "Channel is NULL; dropping message";
153    delete message;
154    return false;
155  }
156
157  if (send_unblocking_only_during_unblock_dispatch_ &&
158      in_unblock_dispatch_ == 0 &&
159      message->is_sync()) {
160    message->set_unblock(false);
161  }
162
163  return channel_->Send(message);
164}
165
166int NPChannelBase::Count() {
167  return static_cast<int>(g_channels.Get().size());
168}
169
170bool NPChannelBase::OnMessageReceived(const IPC::Message& message) {
171  // This call might cause us to be deleted, so keep an extra reference to
172  // ourself so that we can send the reply and decrement back in_dispatch_.
173  g_lazy_channel_stack.Pointer()->push(
174      scoped_refptr<NPChannelBase>(this));
175
176  bool handled;
177  if (message.should_unblock())
178    in_unblock_dispatch_++;
179  if (message.routing_id() == MSG_ROUTING_CONTROL) {
180    handled = OnControlMessageReceived(message);
181  } else {
182    handled = router_.RouteMessage(message);
183    if (!handled && message.is_sync()) {
184      // The listener has gone away, so we must respond or else the caller will
185      // hang waiting for a reply.
186      IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
187      reply->set_reply_error();
188      Send(reply);
189    }
190  }
191  if (message.should_unblock())
192    in_unblock_dispatch_--;
193
194  g_lazy_channel_stack.Pointer()->pop();
195  return handled;
196}
197
198void NPChannelBase::OnChannelConnected(int32 peer_pid) {
199  peer_pid_ = peer_pid;
200}
201
202void NPChannelBase::AddRoute(int route_id,
203                             IPC::Listener* listener,
204                             NPObjectBase* npobject) {
205  if (npobject) {
206    npobject_listeners_[route_id] = npobject;
207  } else {
208    non_npobject_count_++;
209  }
210
211  router_.AddRoute(route_id, listener);
212}
213
214void NPChannelBase::RemoveRoute(int route_id) {
215  router_.RemoveRoute(route_id);
216
217  ListenerMap::iterator iter = npobject_listeners_.find(route_id);
218  if (iter != npobject_listeners_.end()) {
219    // This was an NPObject proxy or stub, it's not involved in the refcounting.
220
221    // If this RemoveRoute call from the NPObject is a result of us calling
222    // OnChannelError below, don't call erase() here because that'll corrupt
223    // the iterator below.
224    if (in_remove_route_) {
225      iter->second = NULL;
226    } else {
227      npobject_listeners_.erase(iter);
228    }
229
230    return;
231  }
232
233  non_npobject_count_--;
234  DCHECK(non_npobject_count_ >= 0);
235
236  if (!non_npobject_count_) {
237    base::AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true);
238    for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin();
239         npobj_iter != npobject_listeners_.end(); ++npobj_iter) {
240      if (npobj_iter->second) {
241        npobj_iter->second->GetChannelListener()->OnChannelError();
242      }
243    }
244
245    for (ChannelMap::iterator iter = g_channels.Get().begin();
246         iter != g_channels.Get().end(); ++iter) {
247      if (iter->second.get() == this) {
248        g_channels.Get().erase(iter);
249        return;
250      }
251    }
252
253    NOTREACHED();
254  }
255}
256
257bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
258  NOTREACHED() <<
259      "should override in subclass if you care about control messages";
260  return false;
261}
262
263void NPChannelBase::OnChannelError() {
264  channel_valid_ = false;
265
266  // TODO(shess): http://crbug.com/97285
267  // Once an error is seen on a channel, remap the channel to prevent
268  // it from being vended again.  Keep the channel in the map so
269  // RemoveRoute() can clean things up correctly.
270  for (ChannelMap::iterator iter = g_channels.Get().begin();
271       iter != g_channels.Get().end(); ++iter) {
272    if (iter->second.get() == this) {
273      // Insert new element before invalidating |iter|.
274      g_channels.Get()[iter->first + "-error"] = iter->second;
275      g_channels.Get().erase(iter);
276      break;
277    }
278  }
279}
280
281void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
282                                               NPObject* object) {
283  proxy_map_[route_id] = object;
284}
285
286void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) {
287  proxy_map_.erase(route_id);
288}
289
290void NPChannelBase::AddMappingForNPObjectStub(int route_id,
291                                              NPObject* object) {
292  DCHECK(object != NULL);
293  stub_map_[object] = route_id;
294}
295
296void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
297                                                 NPObject* object) {
298  DCHECK(object != NULL);
299  stub_map_.erase(object);
300}
301
302void NPChannelBase::AddMappingForNPObjectOwner(int route_id,
303                                               struct _NPP* owner) {
304  DCHECK(owner != NULL);
305  route_to_owner_[route_id] = owner;
306  owner_to_route_[owner] = route_id;
307}
308
309void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) {
310  DCHECK(owner != NULL);
311  default_owner_ = owner;
312}
313
314void NPChannelBase::RemoveMappingForNPObjectOwner(int route_id) {
315  DCHECK(route_to_owner_.find(route_id) != route_to_owner_.end());
316  owner_to_route_.erase(route_to_owner_[route_id]);
317  route_to_owner_.erase(route_id);
318}
319
320NPObject* NPChannelBase::GetExistingNPObjectProxy(int route_id) {
321  ProxyMap::iterator iter = proxy_map_.find(route_id);
322  return iter != proxy_map_.end() ? iter->second : NULL;
323}
324
325int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject* npobject) {
326  StubMap::iterator iter = stub_map_.find(npobject);
327  return iter != stub_map_.end() ? iter->second : MSG_ROUTING_NONE;
328}
329
330NPP NPChannelBase::GetExistingNPObjectOwner(int route_id) {
331  RouteToOwnerMap::iterator iter = route_to_owner_.find(route_id);
332  return iter != route_to_owner_.end() ? iter->second : default_owner_;
333}
334
335int NPChannelBase::GetExistingRouteForNPObjectOwner(NPP owner) {
336  OwnerToRouteMap::iterator iter = owner_to_route_.find(owner);
337  return iter != owner_to_route_.end() ? iter->second : MSG_ROUTING_NONE;
338}
339
340}  // namespace content
341