Suppress leak caused by 107541 (chrome not calling SkGraphics::Term())
[chromium-blink-merge.git] / content / plugin / plugin_channel.cc
blob31cba22626161d3c70d3abbcf87d344e97eb0cd4
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/plugin_messages.h"
17 #include "content/common/plugin_process_messages.h"
18 #include "content/plugin/plugin_thread.h"
19 #include "content/plugin/webplugin_delegate_stub.h"
20 #include "content/plugin/webplugin_proxy.h"
21 #include "content/public/common/content_switches.h"
22 #include "third_party/WebKit/public/web/WebBindings.h"
24 #if defined(OS_POSIX)
25 #include "base/posix/eintr_wrapper.h"
26 #include "ipc/ipc_channel_posix.h"
27 #endif
29 using WebKit::WebBindings;
31 namespace content {
33 namespace {
35 void PluginReleaseCallback() {
36 ChildProcess::current()->ReleaseProcess();
39 // How long we wait before releasing the plugin process.
40 const int kPluginReleaseTimeMinutes = 5;
42 } // namespace
44 // If a sync call to the renderer results in a modal dialog, we need to have a
45 // way to know so that we can run a nested message loop to simulate what would
46 // happen in a single process browser and avoid deadlock.
47 class PluginChannel::MessageFilter : public IPC::ChannelProxy::MessageFilter {
48 public:
49 MessageFilter() : channel_(NULL) { }
51 base::WaitableEvent* GetModalDialogEvent(int render_view_id) {
52 base::AutoLock auto_lock(modal_dialog_event_map_lock_);
53 if (!modal_dialog_event_map_.count(render_view_id)) {
54 NOTREACHED();
55 return NULL;
58 return modal_dialog_event_map_[render_view_id].event;
61 // Decrement the ref count associated with the modal dialog event for the
62 // given tab.
63 void ReleaseModalDialogEvent(int render_view_id) {
64 base::AutoLock auto_lock(modal_dialog_event_map_lock_);
65 if (!modal_dialog_event_map_.count(render_view_id)) {
66 NOTREACHED();
67 return;
70 if (--(modal_dialog_event_map_[render_view_id].refcount))
71 return;
73 // Delete the event when the stack unwinds as it could be in use now.
74 base::MessageLoop::current()->DeleteSoon(
75 FROM_HERE, modal_dialog_event_map_[render_view_id].event);
76 modal_dialog_event_map_.erase(render_view_id);
79 bool Send(IPC::Message* message) {
80 // Need this function for the IPC_MESSAGE_HANDLER_DELAY_REPLY macro.
81 return channel_->Send(message);
84 // IPC::ChannelProxy::MessageFilter:
85 virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE {
86 channel_ = channel;
89 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
90 IPC_BEGIN_MESSAGE_MAP(PluginChannel::MessageFilter, message)
91 IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_Init, OnInit)
92 IPC_MESSAGE_HANDLER(PluginMsg_SignalModalDialogEvent,
93 OnSignalModalDialogEvent)
94 IPC_MESSAGE_HANDLER(PluginMsg_ResetModalDialogEvent,
95 OnResetModalDialogEvent)
96 IPC_END_MESSAGE_MAP()
97 return message.type() == PluginMsg_SignalModalDialogEvent::ID ||
98 message.type() == PluginMsg_ResetModalDialogEvent::ID;
101 protected:
102 virtual ~MessageFilter() {
103 // Clean up in case of renderer crash.
104 for (ModalDialogEventMap::iterator i = modal_dialog_event_map_.begin();
105 i != modal_dialog_event_map_.end(); ++i) {
106 delete i->second.event;
110 private:
111 void OnInit(const PluginMsg_Init_Params& params, IPC::Message* reply_msg) {
112 base::AutoLock auto_lock(modal_dialog_event_map_lock_);
113 if (modal_dialog_event_map_.count(params.host_render_view_routing_id)) {
114 modal_dialog_event_map_[params.host_render_view_routing_id].refcount++;
115 return;
118 WaitableEventWrapper wrapper;
119 wrapper.event = new base::WaitableEvent(true, false);
120 wrapper.refcount = 1;
121 modal_dialog_event_map_[params.host_render_view_routing_id] = wrapper;
124 void OnSignalModalDialogEvent(int render_view_id) {
125 base::AutoLock auto_lock(modal_dialog_event_map_lock_);
126 if (modal_dialog_event_map_.count(render_view_id))
127 modal_dialog_event_map_[render_view_id].event->Signal();
130 void OnResetModalDialogEvent(int render_view_id) {
131 base::AutoLock auto_lock(modal_dialog_event_map_lock_);
132 if (modal_dialog_event_map_.count(render_view_id))
133 modal_dialog_event_map_[render_view_id].event->Reset();
136 struct WaitableEventWrapper {
137 base::WaitableEvent* event;
138 int refcount; // There could be multiple plugin instances per tab.
140 typedef std::map<int, WaitableEventWrapper> ModalDialogEventMap;
141 ModalDialogEventMap modal_dialog_event_map_;
142 base::Lock modal_dialog_event_map_lock_;
144 IPC::Channel* channel_;
147 PluginChannel* PluginChannel::GetPluginChannel(
148 int renderer_id, base::MessageLoopProxy* ipc_message_loop) {
149 // Map renderer ID to a (single) channel to that process.
150 std::string channel_key = base::StringPrintf(
151 "%d.r%d", base::GetCurrentProcId(), renderer_id);
153 PluginChannel* channel =
154 static_cast<PluginChannel*>(NPChannelBase::GetChannel(
155 channel_key,
156 IPC::Channel::MODE_SERVER,
157 ClassFactory,
158 ipc_message_loop,
159 false,
160 ChildProcess::current()->GetShutDownEvent()));
162 if (channel)
163 channel->renderer_id_ = renderer_id;
165 return channel;
168 // static
169 void PluginChannel::NotifyRenderersOfPendingShutdown() {
170 Broadcast(new PluginHostMsg_PluginShuttingDown());
173 bool PluginChannel::Send(IPC::Message* msg) {
174 in_send_++;
175 if (log_messages_) {
176 VLOG(1) << "sending message @" << msg << " on channel @" << this
177 << " with type " << msg->type();
179 bool result = NPChannelBase::Send(msg);
180 in_send_--;
181 return result;
184 bool PluginChannel::OnMessageReceived(const IPC::Message& msg) {
185 if (log_messages_) {
186 VLOG(1) << "received message @" << &msg << " on channel @" << this
187 << " with type " << msg.type();
189 return NPChannelBase::OnMessageReceived(msg);
192 void PluginChannel::OnChannelError() {
193 NPChannelBase::OnChannelError();
194 CleanUp();
197 int PluginChannel::GenerateRouteID() {
198 static int last_id = 0;
199 return ++last_id;
202 base::WaitableEvent* PluginChannel::GetModalDialogEvent(int render_view_id) {
203 return filter_->GetModalDialogEvent(render_view_id);
206 PluginChannel::~PluginChannel() {
207 base::MessageLoop::current()->PostDelayedTask(
208 FROM_HERE,
209 base::Bind(&PluginReleaseCallback),
210 base::TimeDelta::FromMinutes(kPluginReleaseTimeMinutes));
213 void PluginChannel::CleanUp() {
214 // We need to clean up the stubs so that they call NPPDestroy. This will
215 // also lead to them releasing their reference on this object so that it can
216 // be deleted.
217 for (size_t i = 0; i < plugin_stubs_.size(); ++i)
218 RemoveRoute(plugin_stubs_[i]->instance_id());
220 // Need to addref this object temporarily because otherwise removing the last
221 // stub will cause the destructor of this object to be called, however at
222 // that point plugin_stubs_ will have one element and its destructor will be
223 // called twice.
224 scoped_refptr<PluginChannel> me(this);
226 plugin_stubs_.clear();
229 bool PluginChannel::Init(base::MessageLoopProxy* ipc_message_loop,
230 bool create_pipe_now,
231 base::WaitableEvent* shutdown_event) {
232 if (!NPChannelBase::Init(ipc_message_loop, create_pipe_now, shutdown_event))
233 return false;
235 channel_->AddFilter(filter_.get());
236 return true;
239 PluginChannel::PluginChannel()
240 : renderer_id_(-1),
241 in_send_(0),
242 incognito_(false),
243 filter_(new MessageFilter()),
244 npp_(new struct _NPP) {
245 set_send_unblocking_only_during_unblock_dispatch();
246 ChildProcess::current()->AddRefProcess();
247 const CommandLine* command_line = CommandLine::ForCurrentProcess();
248 log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
250 // Register |npp_| as the default owner for any object we receive via IPC,
251 // and register it with WebBindings as a valid owner.
252 SetDefaultNPObjectOwner(npp_.get());
253 WebBindings::registerObjectOwner(npp_.get());
256 bool PluginChannel::OnControlMessageReceived(const IPC::Message& msg) {
257 bool handled = true;
258 IPC_BEGIN_MESSAGE_MAP(PluginChannel, msg)
259 IPC_MESSAGE_HANDLER(PluginMsg_CreateInstance, OnCreateInstance)
260 IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_DestroyInstance,
261 OnDestroyInstance)
262 IPC_MESSAGE_HANDLER(PluginMsg_GenerateRouteID, OnGenerateRouteID)
263 IPC_MESSAGE_HANDLER(PluginProcessMsg_ClearSiteData, OnClearSiteData)
264 IPC_MESSAGE_UNHANDLED(handled = false)
265 IPC_END_MESSAGE_MAP()
266 DCHECK(handled);
267 return handled;
270 void PluginChannel::OnCreateInstance(const std::string& mime_type,
271 int* instance_id) {
272 *instance_id = GenerateRouteID();
273 scoped_refptr<WebPluginDelegateStub> stub(new WebPluginDelegateStub(
274 mime_type, *instance_id, this));
275 AddRoute(*instance_id, stub.get(), NULL);
276 plugin_stubs_.push_back(stub);
279 void PluginChannel::OnDestroyInstance(int instance_id,
280 IPC::Message* reply_msg) {
281 for (size_t i = 0; i < plugin_stubs_.size(); ++i) {
282 if (plugin_stubs_[i]->instance_id() == instance_id) {
283 scoped_refptr<MessageFilter> filter(filter_);
284 int render_view_id =
285 plugin_stubs_[i]->webplugin()->host_render_view_routing_id();
286 plugin_stubs_.erase(plugin_stubs_.begin() + i);
287 Send(reply_msg);
288 RemoveRoute(instance_id);
289 // NOTE: *this* might be deleted as a result of calling RemoveRoute.
290 // Don't release the modal dialog event right away, but do it after the
291 // stack unwinds since the plugin can be destroyed later if it's in use
292 // right now.
293 base::MessageLoop::current()->PostNonNestableTask(
294 FROM_HERE,
295 base::Bind(&MessageFilter::ReleaseModalDialogEvent,
296 filter.get(),
297 render_view_id));
298 return;
302 NOTREACHED() << "Couldn't find WebPluginDelegateStub to destroy";
305 void PluginChannel::OnGenerateRouteID(int* route_id) {
306 *route_id = GenerateRouteID();
309 void PluginChannel::OnClearSiteData(const std::string& site,
310 uint64 flags,
311 uint64 max_age) {
312 bool success = false;
313 CommandLine* command_line = CommandLine::ForCurrentProcess();
314 base::FilePath path = command_line->GetSwitchValuePath(switches::kPluginPath);
315 scoped_refptr<PluginLib> plugin_lib(PluginLib::CreatePluginLib(path));
316 if (plugin_lib.get()) {
317 NPError err = plugin_lib->NP_Initialize();
318 if (err == NPERR_NO_ERROR) {
319 const char* site_str = site.empty() ? NULL : site.c_str();
320 err = plugin_lib->NP_ClearSiteData(site_str, flags, max_age);
321 std::string site_name =
322 site.empty() ? "NULL"
323 : base::StringPrintf("\"%s\"", site_str);
324 VLOG(1) << "NPP_ClearSiteData(" << site_name << ", " << flags << ", "
325 << max_age << ") returned " << err;
326 success = (err == NPERR_NO_ERROR);
329 Send(new PluginProcessHostMsg_ClearSiteDataResult(success));
332 } // namespace content