• 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/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 
20 namespace content {
21 
22 namespace {
23 
24 typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap;
25 
26 struct 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.
37 base::LazyInstance<base::ThreadLocalPointer<ChannelGlobals> >::Leaky
38     g_channels_tls_ptr = LAZY_INSTANCE_INITIALIZER;
39 
GetChannelGlobals()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);
45   }
46   return globals;
47 }
48 
49 #else
50 
51 base::LazyInstance<ChannelGlobals>::Leaky g_channels_globals =
52     LAZY_INSTANCE_INITIALIZER;
53 
GetChannelGlobals()54 ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); }
55 
56 #endif  // OS_ANDROID
57 
GetChannelMap()58 ChannelMap* GetChannelMap() {
59   return &GetChannelGlobals()->channel_map;
60 }
61 
62 }  // namespace
63 
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)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
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 
Broadcast(IPC::Message * message)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));
112   }
113   delete message;
114 }
115 
NPChannelBase()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) {
125 }
126 
~NPChannelBase()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());
134 }
135 
GetCurrentChannel()136 NPChannelBase* NPChannelBase::GetCurrentChannel() {
137   return GetChannelGlobals()->current_channel.get();
138 }
139 
CleanupChannels()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);
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 
GetNPObjectListenerForRoute(int route_id)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;
163   }
164   return iter->second;
165 }
166 
GetModalDialogEvent(int render_view_id)167 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
168   return NULL;
169 }
170 
Init(base::MessageLoopProxy * ipc_message_loop,bool create_pipe_now,base::WaitableEvent * shutdown_event)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
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 
Send(IPC::Message * message)196 bool 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 
Count()212 int NPChannelBase::Count() {
213   return static_cast<int>(GetChannelMap()->size());
214 }
215 
OnMessageReceived(const IPC::Message & message)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);
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 
OnChannelConnected(int32 peer_pid)244 void NPChannelBase::OnChannelConnected(int32 peer_pid) {
245   peer_pid_ = peer_pid;
246 }
247 
AddRoute(int route_id,IPC::Listener * listener,NPObjectBase * npobject)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_++;
255   }
256 
257   router_.AddRoute(route_id, listener);
258 }
259 
RemoveRoute(int route_id)260 void 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 
OnControlMessageReceived(const IPC::Message & msg)303 bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
304   NOTREACHED() <<
305       "should override in subclass if you care about control messages";
306   return false;
307 }
308 
OnChannelError()309 void 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 
AddMappingForNPObjectProxy(int route_id,NPObject * object)327 void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
328                                                NPObject* object) {
329   proxy_map_[route_id] = object;
330 }
331 
RemoveMappingForNPObjectProxy(int route_id)332 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) {
333   proxy_map_.erase(route_id);
334 }
335 
AddMappingForNPObjectStub(int route_id,NPObject * object)336 void NPChannelBase::AddMappingForNPObjectStub(int route_id,
337                                               NPObject* object) {
338   DCHECK(object != NULL);
339   stub_map_[object] = route_id;
340 }
341 
RemoveMappingForNPObjectStub(int route_id,NPObject * object)342 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
343                                                  NPObject* object) {
344   DCHECK(object != NULL);
345   stub_map_.erase(object);
346 }
347 
AddMappingForNPObjectOwner(int route_id,struct _NPP * owner)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;
353 }
354 
SetDefaultNPObjectOwner(struct _NPP * owner)355 void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) {
356   DCHECK(owner != NULL);
357   default_owner_ = owner;
358 }
359 
RemoveMappingForNPObjectOwner(int route_id)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);
364 }
365 
GetExistingNPObjectProxy(int 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;
369 }
370 
GetExistingRouteForNPObjectStub(NPObject * npobject)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;
374 }
375 
GetExistingNPObjectOwner(int route_id)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_;
379 }
380 
GetExistingRouteForNPObjectOwner(NPP 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;
384 }
385 
386 }  // namespace content
387