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