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