Home | History | Annotate | Download | only in plugin
      1 // Copyright (c) 2012 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/plugin/plugin_channel.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/process/process_handle.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/synchronization/lock.h"
     12 #include "base/synchronization/waitable_event.h"
     13 #include "build/build_config.h"
     14 #include "content/child/child_process.h"
     15 #include "content/child/npapi/plugin_instance.h"
     16 #include "content/child/npapi/webplugin_delegate_impl.h"
     17 #include "content/child/plugin_messages.h"
     18 #include "content/common/plugin_process_messages.h"
     19 #include "content/plugin/plugin_thread.h"
     20 #include "content/plugin/webplugin_delegate_stub.h"
     21 #include "content/plugin/webplugin_proxy.h"
     22 #include "content/public/common/content_switches.h"
     23 #include "ipc/message_filter.h"
     24 #include "third_party/WebKit/public/web/WebBindings.h"
     25 
     26 #if defined(OS_POSIX)
     27 #include "ipc/ipc_channel_posix.h"
     28 #endif
     29 
     30 using blink::WebBindings;
     31 
     32 namespace content {
     33 
     34 namespace {
     35 
     36 // How long we wait before releasing the plugin process.
     37 const int kPluginReleaseTimeMinutes = 5;
     38 
     39 }  // namespace
     40 
     41 // If a sync call to the renderer results in a modal dialog, we need to have a
     42 // way to know so that we can run a nested message loop to simulate what would
     43 // happen in a single process browser and avoid deadlock.
     44 class PluginChannel::MessageFilter : public IPC::MessageFilter {
     45  public:
     46   MessageFilter() : sender_(NULL) { }
     47 
     48   base::WaitableEvent* GetModalDialogEvent(int render_view_id) {
     49     base::AutoLock auto_lock(modal_dialog_event_map_lock_);
     50     if (!modal_dialog_event_map_.count(render_view_id)) {
     51       NOTREACHED();
     52       return NULL;
     53     }
     54 
     55     return modal_dialog_event_map_[render_view_id].event;
     56   }
     57 
     58   // Decrement the ref count associated with the modal dialog event for the
     59   // given tab.
     60   void ReleaseModalDialogEvent(int render_view_id) {
     61     base::AutoLock auto_lock(modal_dialog_event_map_lock_);
     62     if (!modal_dialog_event_map_.count(render_view_id)) {
     63       NOTREACHED();
     64       return;
     65     }
     66 
     67     if (--(modal_dialog_event_map_[render_view_id].refcount))
     68       return;
     69 
     70     // Delete the event when the stack unwinds as it could be in use now.
     71     base::MessageLoop::current()->DeleteSoon(
     72         FROM_HERE, modal_dialog_event_map_[render_view_id].event);
     73     modal_dialog_event_map_.erase(render_view_id);
     74   }
     75 
     76   bool Send(IPC::Message* message) {
     77     // Need this function for the IPC_MESSAGE_HANDLER_DELAY_REPLY macro.
     78     return sender_->Send(message);
     79   }
     80 
     81   // IPC::MessageFilter:
     82   virtual void OnFilterAdded(IPC::Sender* sender) OVERRIDE {
     83     sender_ = sender;
     84   }
     85 
     86   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
     87     IPC_BEGIN_MESSAGE_MAP(PluginChannel::MessageFilter, message)
     88       IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_Init, OnInit)
     89       IPC_MESSAGE_HANDLER(PluginMsg_SignalModalDialogEvent,
     90                           OnSignalModalDialogEvent)
     91       IPC_MESSAGE_HANDLER(PluginMsg_ResetModalDialogEvent,
     92                           OnResetModalDialogEvent)
     93     IPC_END_MESSAGE_MAP()
     94     return message.type() == PluginMsg_SignalModalDialogEvent::ID ||
     95            message.type() == PluginMsg_ResetModalDialogEvent::ID;
     96   }
     97 
     98  protected:
     99   virtual ~MessageFilter() {
    100     // Clean up in case of renderer crash.
    101     for (ModalDialogEventMap::iterator i = modal_dialog_event_map_.begin();
    102         i != modal_dialog_event_map_.end(); ++i) {
    103       delete i->second.event;
    104     }
    105   }
    106 
    107  private:
    108   void OnInit(const PluginMsg_Init_Params& params, IPC::Message* reply_msg) {
    109     base::AutoLock auto_lock(modal_dialog_event_map_lock_);
    110     if (modal_dialog_event_map_.count(params.host_render_view_routing_id)) {
    111       modal_dialog_event_map_[params.host_render_view_routing_id].refcount++;
    112       return;
    113     }
    114 
    115     WaitableEventWrapper wrapper;
    116     wrapper.event = new base::WaitableEvent(true, false);
    117     wrapper.refcount = 1;
    118     modal_dialog_event_map_[params.host_render_view_routing_id] = wrapper;
    119   }
    120 
    121   void OnSignalModalDialogEvent(int render_view_id) {
    122     base::AutoLock auto_lock(modal_dialog_event_map_lock_);
    123     if (modal_dialog_event_map_.count(render_view_id))
    124       modal_dialog_event_map_[render_view_id].event->Signal();
    125   }
    126 
    127   void OnResetModalDialogEvent(int render_view_id) {
    128     base::AutoLock auto_lock(modal_dialog_event_map_lock_);
    129     if (modal_dialog_event_map_.count(render_view_id))
    130       modal_dialog_event_map_[render_view_id].event->Reset();
    131   }
    132 
    133   struct WaitableEventWrapper {
    134     base::WaitableEvent* event;
    135     int refcount;  // There could be multiple plugin instances per tab.
    136   };
    137   typedef std::map<int, WaitableEventWrapper> ModalDialogEventMap;
    138   ModalDialogEventMap modal_dialog_event_map_;
    139   base::Lock modal_dialog_event_map_lock_;
    140 
    141   IPC::Sender* sender_;
    142 };
    143 
    144 PluginChannel* PluginChannel::GetPluginChannel(
    145     int renderer_id, base::MessageLoopProxy* ipc_message_loop) {
    146   // Map renderer ID to a (single) channel to that process.
    147   std::string channel_key = base::StringPrintf(
    148       "%d.r%d", base::GetCurrentProcId(), renderer_id);
    149 
    150   PluginChannel* channel =
    151       static_cast<PluginChannel*>(NPChannelBase::GetChannel(
    152           channel_key,
    153           IPC::Channel::MODE_SERVER,
    154           ClassFactory,
    155           ipc_message_loop,
    156           false,
    157           ChildProcess::current()->GetShutDownEvent()));
    158 
    159   if (channel)
    160     channel->renderer_id_ = renderer_id;
    161 
    162   return channel;
    163 }
    164 
    165 // static
    166 void PluginChannel::NotifyRenderersOfPendingShutdown() {
    167   Broadcast(new PluginHostMsg_PluginShuttingDown());
    168 }
    169 
    170 bool PluginChannel::Send(IPC::Message* msg) {
    171   in_send_++;
    172   if (log_messages_) {
    173     VLOG(1) << "sending message @" << msg << " on channel @" << this
    174             << " with type " << msg->type();
    175   }
    176   bool result = NPChannelBase::Send(msg);
    177   in_send_--;
    178   return result;
    179 }
    180 
    181 bool PluginChannel::OnMessageReceived(const IPC::Message& msg) {
    182   if (log_messages_) {
    183     VLOG(1) << "received message @" << &msg << " on channel @" << this
    184             << " with type " << msg.type();
    185   }
    186   return NPChannelBase::OnMessageReceived(msg);
    187 }
    188 
    189 void PluginChannel::OnChannelError() {
    190   NPChannelBase::OnChannelError();
    191   CleanUp();
    192 }
    193 
    194 int PluginChannel::GenerateRouteID() {
    195   static int last_id = 0;
    196   return ++last_id;
    197 }
    198 
    199 base::WaitableEvent* PluginChannel::GetModalDialogEvent(int render_view_id) {
    200   return filter_->GetModalDialogEvent(render_view_id);
    201 }
    202 
    203 PluginChannel::~PluginChannel() {
    204   PluginThread::current()->Send(new PluginProcessHostMsg_ChannelDestroyed(
    205       renderer_id_));
    206   process_ref_.ReleaseWithDelay(
    207       base::TimeDelta::FromMinutes(kPluginReleaseTimeMinutes));
    208 }
    209 
    210 void PluginChannel::CleanUp() {
    211   // We need to clean up the stubs so that they call NPPDestroy.  This will
    212   // also lead to them releasing their reference on this object so that it can
    213   // be deleted.
    214   for (size_t i = 0; i < plugin_stubs_.size(); ++i)
    215     RemoveRoute(plugin_stubs_[i]->instance_id());
    216 
    217   // Need to addref this object temporarily because otherwise removing the last
    218   // stub will cause the destructor of this object to be called, however at
    219   // that point plugin_stubs_ will have one element and its destructor will be
    220   // called twice.
    221   scoped_refptr<PluginChannel> me(this);
    222 
    223   while (!plugin_stubs_.empty()) {
    224     // Separate vector::erase and ~WebPluginDelegateStub.
    225     // See https://code.google.com/p/chromium/issues/detail?id=314088
    226     scoped_refptr<WebPluginDelegateStub> stub = plugin_stubs_[0];
    227     plugin_stubs_.erase(plugin_stubs_.begin());
    228   }
    229 }
    230 
    231 bool PluginChannel::Init(base::MessageLoopProxy* ipc_message_loop,
    232                          bool create_pipe_now,
    233                          base::WaitableEvent* shutdown_event) {
    234   if (!NPChannelBase::Init(ipc_message_loop, create_pipe_now, shutdown_event))
    235     return false;
    236 
    237   channel_->AddFilter(filter_.get());
    238   return true;
    239 }
    240 
    241 PluginChannel::PluginChannel()
    242     : renderer_id_(-1),
    243       in_send_(0),
    244       incognito_(false),
    245       filter_(new MessageFilter()),
    246       npp_(new struct _NPP) {
    247   set_send_unblocking_only_during_unblock_dispatch();
    248   const CommandLine* command_line = CommandLine::ForCurrentProcess();
    249   log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
    250 
    251   // Register |npp_| as the default owner for any object we receive via IPC,
    252   // and register it with WebBindings as a valid owner.
    253   SetDefaultNPObjectOwner(npp_.get());
    254   WebBindings::registerObjectOwner(npp_.get());
    255 }
    256 
    257 bool PluginChannel::OnControlMessageReceived(const IPC::Message& msg) {
    258   bool handled = true;
    259   IPC_BEGIN_MESSAGE_MAP(PluginChannel, msg)
    260     IPC_MESSAGE_HANDLER(PluginMsg_CreateInstance, OnCreateInstance)
    261     IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_DestroyInstance,
    262                                     OnDestroyInstance)
    263     IPC_MESSAGE_HANDLER(PluginMsg_GenerateRouteID, OnGenerateRouteID)
    264     IPC_MESSAGE_HANDLER(PluginProcessMsg_ClearSiteData, OnClearSiteData)
    265     IPC_MESSAGE_HANDLER(PluginHostMsg_DidAbortLoading, OnDidAbortLoading)
    266     IPC_MESSAGE_UNHANDLED(handled = false)
    267   IPC_END_MESSAGE_MAP()
    268   DCHECK(handled);
    269   return handled;
    270 }
    271 
    272 void PluginChannel::OnCreateInstance(const std::string& mime_type,
    273                                      int* instance_id) {
    274   *instance_id = GenerateRouteID();
    275   scoped_refptr<WebPluginDelegateStub> stub(new WebPluginDelegateStub(
    276       mime_type, *instance_id, this));
    277   AddRoute(*instance_id, stub.get(), NULL);
    278   plugin_stubs_.push_back(stub);
    279 }
    280 
    281 void PluginChannel::OnDestroyInstance(int instance_id,
    282                                       IPC::Message* reply_msg) {
    283   for (size_t i = 0; i < plugin_stubs_.size(); ++i) {
    284     if (plugin_stubs_[i]->instance_id() == instance_id) {
    285       scoped_refptr<MessageFilter> filter(filter_);
    286       int render_view_id =
    287           plugin_stubs_[i]->webplugin()->host_render_view_routing_id();
    288       // Separate vector::erase and ~WebPluginDelegateStub.
    289       // See https://code.google.com/p/chromium/issues/detail?id=314088
    290       scoped_refptr<WebPluginDelegateStub> stub = plugin_stubs_[i];
    291       plugin_stubs_.erase(plugin_stubs_.begin() + i);
    292       stub = NULL;
    293 
    294       Send(reply_msg);
    295       RemoveRoute(instance_id);
    296       // NOTE: *this* might be deleted as a result of calling RemoveRoute.
    297       // Don't release the modal dialog event right away, but do it after the
    298       // stack unwinds since the plugin can be destroyed later if it's in use
    299       // right now.
    300       base::MessageLoop::current()->PostNonNestableTask(
    301           FROM_HERE,
    302           base::Bind(&MessageFilter::ReleaseModalDialogEvent,
    303                      filter.get(),
    304                      render_view_id));
    305       return;
    306     }
    307   }
    308 
    309   NOTREACHED() << "Couldn't find WebPluginDelegateStub to destroy";
    310 }
    311 
    312 void PluginChannel::OnGenerateRouteID(int* route_id) {
    313   *route_id = GenerateRouteID();
    314 }
    315 
    316 void PluginChannel::OnClearSiteData(const std::string& site,
    317                                     uint64 flags,
    318                                     uint64 max_age) {
    319   bool success = false;
    320   CommandLine* command_line = CommandLine::ForCurrentProcess();
    321   base::FilePath path = command_line->GetSwitchValuePath(switches::kPluginPath);
    322   scoped_refptr<PluginLib> plugin_lib(PluginLib::CreatePluginLib(path));
    323   if (plugin_lib.get()) {
    324     NPError err = plugin_lib->NP_Initialize();
    325     if (err == NPERR_NO_ERROR) {
    326       const char* site_str = site.empty() ? NULL : site.c_str();
    327       err = plugin_lib->NP_ClearSiteData(site_str, flags, max_age);
    328       std::string site_name =
    329           site.empty() ? "NULL"
    330                        : base::StringPrintf("\"%s\"", site_str);
    331       VLOG(1) << "NPP_ClearSiteData(" << site_name << ", " << flags << ", "
    332               << max_age << ") returned " << err;
    333       success = (err == NPERR_NO_ERROR);
    334     }
    335   }
    336   Send(new PluginProcessHostMsg_ClearSiteDataResult(success));
    337 }
    338 
    339 void PluginChannel::OnDidAbortLoading(int render_view_id) {
    340   for (size_t i = 0; i < plugin_stubs_.size(); ++i) {
    341     if (plugin_stubs_[i]->webplugin()->host_render_view_routing_id() ==
    342             render_view_id) {
    343       plugin_stubs_[i]->delegate()->instance()->CloseStreams();
    344     }
    345   }
    346 }
    347 
    348 }  // namespace content
    349