Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / plugin / plugin_channel.cc
blob90993e94725748a27a0ccaac1ac000cd6bedb6aa
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.
5 #include "content/plugin/plugin_channel.h"
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"
26 #if defined(OS_POSIX)
27 #include "ipc/ipc_channel_posix.h"
28 #endif
30 using blink::WebBindings;
32 namespace content {
34 namespace {
36 // How long we wait before releasing the plugin process.
37 const int kPluginReleaseTimeMinutes = 5;
39 } // namespace
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) { }
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;
55 return modal_dialog_event_map_[render_view_id].event;
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;
67 if (--(modal_dialog_event_map_[render_view_id].refcount))
68 return;
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);
76 bool Send(IPC::Message* message) {
77 // Need this function for the IPC_MESSAGE_HANDLER_DELAY_REPLY macro.
78 return sender_->Send(message);
81 // IPC::MessageFilter:
82 void OnFilterAdded(IPC::Sender* sender) override { sender_ = sender; }
84 bool OnMessageReceived(const IPC::Message& message) override {
85 IPC_BEGIN_MESSAGE_MAP(PluginChannel::MessageFilter, message)
86 IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_Init, OnInit)
87 IPC_MESSAGE_HANDLER(PluginMsg_SignalModalDialogEvent,
88 OnSignalModalDialogEvent)
89 IPC_MESSAGE_HANDLER(PluginMsg_ResetModalDialogEvent,
90 OnResetModalDialogEvent)
91 IPC_END_MESSAGE_MAP()
92 return message.type() == PluginMsg_SignalModalDialogEvent::ID ||
93 message.type() == PluginMsg_ResetModalDialogEvent::ID;
96 protected:
97 ~MessageFilter() override {
98 // Clean up in case of renderer crash.
99 for (ModalDialogEventMap::iterator i = modal_dialog_event_map_.begin();
100 i != modal_dialog_event_map_.end(); ++i) {
101 delete i->second.event;
105 private:
106 void OnInit(const PluginMsg_Init_Params& params, IPC::Message* reply_msg) {
107 base::AutoLock auto_lock(modal_dialog_event_map_lock_);
108 if (modal_dialog_event_map_.count(params.host_render_view_routing_id)) {
109 modal_dialog_event_map_[params.host_render_view_routing_id].refcount++;
110 return;
113 WaitableEventWrapper wrapper;
114 wrapper.event = new base::WaitableEvent(true, false);
115 wrapper.refcount = 1;
116 modal_dialog_event_map_[params.host_render_view_routing_id] = wrapper;
119 void OnSignalModalDialogEvent(int render_view_id) {
120 base::AutoLock auto_lock(modal_dialog_event_map_lock_);
121 if (modal_dialog_event_map_.count(render_view_id))
122 modal_dialog_event_map_[render_view_id].event->Signal();
125 void OnResetModalDialogEvent(int render_view_id) {
126 base::AutoLock auto_lock(modal_dialog_event_map_lock_);
127 if (modal_dialog_event_map_.count(render_view_id))
128 modal_dialog_event_map_[render_view_id].event->Reset();
131 struct WaitableEventWrapper {
132 base::WaitableEvent* event;
133 int refcount; // There could be multiple plugin instances per tab.
135 typedef std::map<int, WaitableEventWrapper> ModalDialogEventMap;
136 ModalDialogEventMap modal_dialog_event_map_;
137 base::Lock modal_dialog_event_map_lock_;
139 IPC::Sender* sender_;
142 PluginChannel* PluginChannel::GetPluginChannel(
143 int renderer_id,
144 base::SingleThreadTaskRunner* ipc_task_runner) {
145 // Map renderer ID to a (single) channel to that process.
146 std::string channel_key = base::StringPrintf(
147 "%d.r%d", base::GetCurrentProcId(), renderer_id);
149 PluginChannel* channel =
150 static_cast<PluginChannel*>(NPChannelBase::GetChannel(
151 channel_key, IPC::Channel::MODE_SERVER, ClassFactory, ipc_task_runner,
152 false, ChildProcess::current()->GetShutDownEvent()));
154 if (channel)
155 channel->renderer_id_ = renderer_id;
157 return channel;
160 // static
161 void PluginChannel::NotifyRenderersOfPendingShutdown() {
162 Broadcast(new PluginHostMsg_PluginShuttingDown());
165 bool PluginChannel::Send(IPC::Message* msg) {
166 in_send_++;
167 if (log_messages_) {
168 VLOG(1) << "sending message @" << msg << " on channel @" << this
169 << " with type " << msg->type();
171 bool result = NPChannelBase::Send(msg);
172 in_send_--;
173 return result;
176 bool PluginChannel::OnMessageReceived(const IPC::Message& msg) {
177 if (log_messages_) {
178 VLOG(1) << "received message @" << &msg << " on channel @" << this
179 << " with type " << msg.type();
181 return NPChannelBase::OnMessageReceived(msg);
184 void PluginChannel::OnChannelError() {
185 NPChannelBase::OnChannelError();
186 CleanUp();
189 int PluginChannel::GenerateRouteID() {
190 static int last_id = 0;
191 return ++last_id;
194 base::WaitableEvent* PluginChannel::GetModalDialogEvent(int render_view_id) {
195 return filter_->GetModalDialogEvent(render_view_id);
198 PluginChannel::~PluginChannel() {
199 PluginThread::current()->Send(new PluginProcessHostMsg_ChannelDestroyed(
200 renderer_id_));
201 process_ref_.ReleaseWithDelay(
202 base::TimeDelta::FromMinutes(kPluginReleaseTimeMinutes));
205 void PluginChannel::CleanUp() {
206 // We need to clean up the stubs so that they call NPPDestroy. This will
207 // also lead to them releasing their reference on this object so that it can
208 // be deleted.
209 for (size_t i = 0; i < plugin_stubs_.size(); ++i)
210 RemoveRoute(plugin_stubs_[i]->instance_id());
212 // Need to addref this object temporarily because otherwise removing the last
213 // stub will cause the destructor of this object to be called, however at
214 // that point plugin_stubs_ will have one element and its destructor will be
215 // called twice.
216 scoped_refptr<PluginChannel> me(this);
218 while (!plugin_stubs_.empty()) {
219 // Separate vector::erase and ~WebPluginDelegateStub.
220 // See https://code.google.com/p/chromium/issues/detail?id=314088
221 scoped_refptr<WebPluginDelegateStub> stub = plugin_stubs_[0];
222 plugin_stubs_.erase(plugin_stubs_.begin());
226 bool PluginChannel::Init(base::SingleThreadTaskRunner* ipc_task_runner,
227 bool create_pipe_now,
228 base::WaitableEvent* shutdown_event) {
229 if (!NPChannelBase::Init(ipc_task_runner, create_pipe_now, shutdown_event))
230 return false;
232 channel_->AddFilter(filter_.get());
233 return true;
236 PluginChannel::PluginChannel()
237 : renderer_id_(-1),
238 in_send_(0),
239 incognito_(false),
240 filter_(new MessageFilter()),
241 npp_(new struct _NPP) {
242 set_send_unblocking_only_during_unblock_dispatch();
243 const base::CommandLine* command_line =
244 base::CommandLine::ForCurrentProcess();
245 log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
247 // Register |npp_| as the default owner for any object we receive via IPC,
248 // and register it with WebBindings as a valid owner.
249 SetDefaultNPObjectOwner(npp_.get());
250 WebBindings::registerObjectOwner(npp_.get());
253 bool PluginChannel::OnControlMessageReceived(const IPC::Message& msg) {
254 bool handled = true;
255 IPC_BEGIN_MESSAGE_MAP(PluginChannel, msg)
256 IPC_MESSAGE_HANDLER(PluginMsg_CreateInstance, OnCreateInstance)
257 IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_DestroyInstance,
258 OnDestroyInstance)
259 IPC_MESSAGE_HANDLER(PluginMsg_GenerateRouteID, OnGenerateRouteID)
260 IPC_MESSAGE_HANDLER(PluginProcessMsg_ClearSiteData, OnClearSiteData)
261 IPC_MESSAGE_HANDLER(PluginHostMsg_DidAbortLoading, OnDidAbortLoading)
262 IPC_MESSAGE_UNHANDLED(handled = false)
263 IPC_END_MESSAGE_MAP()
264 DCHECK(handled);
265 return handled;
268 void PluginChannel::OnCreateInstance(const std::string& mime_type,
269 int* instance_id) {
270 *instance_id = GenerateRouteID();
271 scoped_refptr<WebPluginDelegateStub> stub(new WebPluginDelegateStub(
272 mime_type, *instance_id, this));
273 AddRoute(*instance_id, stub.get(), NULL);
274 plugin_stubs_.push_back(stub);
277 void PluginChannel::OnDestroyInstance(int instance_id,
278 IPC::Message* reply_msg) {
279 for (size_t i = 0; i < plugin_stubs_.size(); ++i) {
280 if (plugin_stubs_[i]->instance_id() == instance_id) {
281 scoped_refptr<MessageFilter> filter(filter_);
282 int render_view_id =
283 plugin_stubs_[i]->webplugin()->host_render_view_routing_id();
284 // Separate vector::erase and ~WebPluginDelegateStub.
285 // See https://code.google.com/p/chromium/issues/detail?id=314088
286 scoped_refptr<WebPluginDelegateStub> stub = plugin_stubs_[i];
287 plugin_stubs_.erase(plugin_stubs_.begin() + i);
288 stub = NULL;
290 Send(reply_msg);
291 RemoveRoute(instance_id);
292 // NOTE: *this* might be deleted as a result of calling RemoveRoute.
293 // Don't release the modal dialog event right away, but do it after the
294 // stack unwinds since the plugin can be destroyed later if it's in use
295 // right now.
296 base::MessageLoop::current()->PostNonNestableTask(
297 FROM_HERE,
298 base::Bind(&MessageFilter::ReleaseModalDialogEvent,
299 filter.get(),
300 render_view_id));
301 return;
305 NOTREACHED() << "Couldn't find WebPluginDelegateStub to destroy";
308 void PluginChannel::OnGenerateRouteID(int* route_id) {
309 *route_id = GenerateRouteID();
312 void PluginChannel::OnClearSiteData(const std::string& site,
313 uint64 flags,
314 uint64 max_age) {
315 bool success = false;
316 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
317 base::FilePath path = command_line->GetSwitchValuePath(switches::kPluginPath);
318 scoped_refptr<PluginLib> plugin_lib(PluginLib::CreatePluginLib(path));
319 if (plugin_lib.get()) {
320 NPError err = plugin_lib->NP_Initialize();
321 if (err == NPERR_NO_ERROR) {
322 const char* site_str = site.empty() ? NULL : site.c_str();
323 err = plugin_lib->NP_ClearSiteData(site_str, flags, max_age);
324 std::string site_name =
325 site.empty() ? "NULL"
326 : base::StringPrintf("\"%s\"", site_str);
327 VLOG(1) << "NPP_ClearSiteData(" << site_name << ", " << flags << ", "
328 << max_age << ") returned " << err;
329 success = (err == NPERR_NO_ERROR);
332 Send(new PluginProcessHostMsg_ClearSiteDataResult(success));
335 void PluginChannel::OnDidAbortLoading(int render_view_id) {
336 for (size_t i = 0; i < plugin_stubs_.size(); ++i) {
337 if (plugin_stubs_[i]->webplugin()->host_render_view_routing_id() ==
338 render_view_id) {
339 plugin_stubs_[i]->delegate()->instance()->CloseStreams();
344 } // namespace content