• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 namespace content {
20 
21 namespace {
22 
23 typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap;
24 
25 struct 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.
36 base::LazyInstance<base::ThreadLocalPointer<ChannelGlobals> >::Leaky
37     g_channels_tls_ptr = LAZY_INSTANCE_INITIALIZER;
38 
GetChannelGlobals()39 ChannelGlobals* 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 
50 base::LazyInstance<ChannelGlobals>::Leaky g_channels_globals =
51     LAZY_INSTANCE_INITIALIZER;
52 
GetChannelGlobals()53 ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); }
54 
55 #endif  // OS_ANDROID
56 
GetChannelMap()57 ChannelMap* GetChannelMap() {
58   return &GetChannelGlobals()->channel_map;
59 }
60 
61 }  // namespace
62 
GetChannel(const IPC::ChannelHandle & channel_handle,IPC::Channel::Mode mode,ChannelFactory factory,base::MessageLoopProxy * ipc_message_loop,bool create_pipe_now,base::WaitableEvent * shutdown_event)63 NPChannelBase* 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 
Broadcast(IPC::Message * message)106 void 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 
NPChannelBase()115 NPChannelBase::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 
~NPChannelBase()126 NPChannelBase::~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 
GetCurrentChannel()135 NPChannelBase* NPChannelBase::GetCurrentChannel() {
136   return GetChannelGlobals()->current_channel.get();
137 }
138 
CleanupChannels()139 void 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 
GetNPObjectListenerForRoute(int route_id)157 NPObjectBase* 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 
GetModalDialogEvent(int render_view_id)166 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
167   return NULL;
168 }
169 
Init(base::MessageLoopProxy * ipc_message_loop,bool create_pipe_now,base::WaitableEvent * shutdown_event)170 bool 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 
Send(IPC::Message * message)195 bool 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 
Count()211 int NPChannelBase::Count() {
212   return static_cast<int>(GetChannelMap()->size());
213 }
214 
OnMessageReceived(const IPC::Message & message)215 bool 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 
OnChannelConnected(int32 peer_pid)243 void NPChannelBase::OnChannelConnected(int32 peer_pid) {
244   peer_pid_ = peer_pid;
245 }
246 
AddRoute(int route_id,IPC::Listener * listener,NPObjectBase * npobject)247 void 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 
RemoveRoute(int route_id)259 void 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 
OnControlMessageReceived(const IPC::Message & msg)302 bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
303   NOTREACHED() <<
304       "should override in subclass if you care about control messages";
305   return false;
306 }
307 
OnChannelError()308 void 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 
AddMappingForNPObjectProxy(int route_id,NPObject * object)326 void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
327                                                NPObject* object) {
328   proxy_map_[route_id] = object;
329 }
330 
RemoveMappingForNPObjectProxy(int route_id)331 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) {
332   proxy_map_.erase(route_id);
333 }
334 
AddMappingForNPObjectStub(int route_id,NPObject * object)335 void NPChannelBase::AddMappingForNPObjectStub(int route_id,
336                                               NPObject* object) {
337   DCHECK(object != NULL);
338   stub_map_[object] = route_id;
339 }
340 
RemoveMappingForNPObjectStub(int route_id,NPObject * object)341 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
342                                                  NPObject* object) {
343   DCHECK(object != NULL);
344   stub_map_.erase(object);
345 }
346 
AddMappingForNPObjectOwner(int route_id,struct _NPP * owner)347 void 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 
SetDefaultNPObjectOwner(struct _NPP * owner)354 void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) {
355   DCHECK(owner != NULL);
356   default_owner_ = owner;
357 }
358 
RemoveMappingForNPObjectOwner(int route_id)359 void 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 
GetExistingNPObjectProxy(int route_id)365 NPObject* 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 
GetExistingRouteForNPObjectStub(NPObject * npobject)370 int 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 
GetExistingNPObjectOwner(int route_id)375 NPP 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 
GetExistingRouteForNPObjectOwner(NPP owner)380 int 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