Home | History | Annotate | Download | only in npapi
      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 
     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 
     54 ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); }
     55 
     56 #endif  // OS_ANDROID
     57 
     58 ChannelMap* GetChannelMap() {
     59   return &GetChannelGlobals()->channel_map;
     60 }
     61 
     62 }  // namespace
     63 
     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 
    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 
    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 
    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 
    136 NPChannelBase* NPChannelBase::GetCurrentChannel() {
    137   return GetChannelGlobals()->current_channel.get();
    138 }
    139 
    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 
    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 
    167 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
    168   return NULL;
    169 }
    170 
    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 
    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 
    212 int NPChannelBase::Count() {
    213   return static_cast<int>(GetChannelMap()->size());
    214 }
    215 
    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 
    244 void NPChannelBase::OnChannelConnected(int32 peer_pid) {
    245   peer_pid_ = peer_pid;
    246 }
    247 
    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 
    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 
    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 
    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 
    327 void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
    328                                                NPObject* object) {
    329   proxy_map_[route_id] = object;
    330 }
    331 
    332 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) {
    333   proxy_map_.erase(route_id);
    334 }
    335 
    336 void NPChannelBase::AddMappingForNPObjectStub(int route_id,
    337                                               NPObject* object) {
    338   DCHECK(object != NULL);
    339   stub_map_[object] = route_id;
    340 }
    341 
    342 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
    343                                                  NPObject* object) {
    344   DCHECK(object != NULL);
    345   stub_map_.erase(object);
    346 }
    347 
    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 
    355 void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) {
    356   DCHECK(owner != NULL);
    357   default_owner_ = owner;
    358 }
    359 
    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 
    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 
    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 
    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 
    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