Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / plugin / plugin_channel.cc
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() : channel_(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 channel_->Send(message);
79   }
80
81   // IPC::MessageFilter:
82   virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE {
83     channel_ = channel;
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::Channel* channel_;
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