blob: bf14db2f3c9dbbade61cfff528c052f9c1beb088 [file] [log] [blame]
[email protected]76543b92009-08-31 17:27:451// Copyright (c) 2009 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
initial.commit09911bf2008-07-26 23:55:295#include "chrome/plugin/plugin_channel.h"
6
[email protected]bf24d2c2009-02-24 23:07:457#include "base/command_line.h"
[email protected]8beff0762009-09-29 02:18:308#include "base/lock.h"
[email protected]4566f132009-03-12 01:55:139#include "base/process_util.h"
initial.commit09911bf2008-07-26 23:55:2910#include "base/string_util.h"
[email protected]8beff0762009-09-29 02:18:3011#include "base/waitable_event.h"
[email protected]d2e884d2009-06-22 20:37:5212#include "build/build_config.h"
[email protected]51d70e02009-03-27 20:45:5913#include "chrome/common/child_process.h"
14#include "chrome/common/plugin_messages.h"
[email protected]bf24d2c2009-02-24 23:07:4515#include "chrome/common/chrome_switches.h"
[email protected]8930d472009-02-21 08:05:2816#include "chrome/plugin/plugin_thread.h"
[email protected]8beff0762009-09-29 02:18:3017#include "chrome/plugin/webplugin_delegate_stub.h"
18#include "chrome/plugin/webplugin_proxy.h"
initial.commit09911bf2008-07-26 23:55:2919
[email protected]d2e884d2009-06-22 20:37:5220#if defined(OS_POSIX)
[email protected]4a8278472010-03-18 16:14:4921#include "base/eintr_wrapper.h"
[email protected]946d1b22009-07-22 23:57:2122#include "ipc/ipc_channel_posix.h"
[email protected]d2e884d2009-06-22 20:37:5223#endif
24
[email protected]f137d13a2009-08-07 22:57:0625class PluginReleaseTask : public Task {
26 public:
27 void Run() {
28 ChildProcess::current()->ReleaseProcess();
29 }
30};
31
32// How long we wait before releasing the plugin process.
33static const int kPluginReleaseTimeMS = 10000;
34
[email protected]8beff0762009-09-29 02:18:3035
36// If a sync call to the renderer results in a modal dialog, we need to have a
37// way to know so that we can run a nested message loop to simulate what would
38// happen in a single process browser and avoid deadlock.
39class PluginChannel::MessageFilter : public IPC::ChannelProxy::MessageFilter {
40 public:
41 MessageFilter() : channel_(NULL) { }
42 ~MessageFilter() {
43 // Clean up in case of renderer crash.
44 for (ModalDialogEventMap::iterator i = modal_dialog_event_map_.begin();
45 i != modal_dialog_event_map_.end(); ++i) {
46 delete i->second.event;
47 }
48 }
49
50 base::WaitableEvent* GetModalDialogEvent(
51 gfx::NativeViewId containing_window) {
52 AutoLock auto_lock(modal_dialog_event_map_lock_);
53 if (!modal_dialog_event_map_.count(containing_window)) {
54 NOTREACHED();
55 return NULL;
56 }
57
58 return modal_dialog_event_map_[containing_window].event;
59 }
60
61 // Decrement the ref count associated with the modal dialog event for the
62 // given tab.
63 void ReleaseModalDialogEvent(gfx::NativeViewId containing_window) {
64 AutoLock auto_lock(modal_dialog_event_map_lock_);
65 if (!modal_dialog_event_map_.count(containing_window)) {
66 NOTREACHED();
67 return;
68 }
69
70 if (--(modal_dialog_event_map_[containing_window].refcount))
71 return;
72
73 // Delete the event when the stack unwinds as it could be in use now.
74 MessageLoop::current()->DeleteSoon(
75 FROM_HERE, modal_dialog_event_map_[containing_window].event);
76 modal_dialog_event_map_.erase(containing_window);
77 }
78
79 bool Send(IPC::Message* message) {
80 // Need this function for the IPC_MESSAGE_HANDLER_DELAY_REPLY macro.
81 return channel_->Send(message);
82 }
83
84 private:
85 void OnFilterAdded(IPC::Channel* channel) { channel_ = channel; }
86
87 bool OnMessageReceived(const IPC::Message& message) {
88 IPC_BEGIN_MESSAGE_MAP(PluginChannel::MessageFilter, message)
89 IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_Init, OnInit)
90 IPC_MESSAGE_HANDLER(PluginMsg_SignalModalDialogEvent,
91 OnSignalModalDialogEvent)
92 IPC_MESSAGE_HANDLER(PluginMsg_ResetModalDialogEvent,
93 OnResetModalDialogEvent)
94 IPC_END_MESSAGE_MAP()
95 return message.type() == PluginMsg_SignalModalDialogEvent::ID ||
96 message.type() == PluginMsg_ResetModalDialogEvent::ID;
97 }
98
99 void OnInit(const PluginMsg_Init_Params& params, IPC::Message* reply_msg) {
100 AutoLock auto_lock(modal_dialog_event_map_lock_);
101 if (modal_dialog_event_map_.count(params.containing_window)) {
102 modal_dialog_event_map_[params.containing_window].refcount++;
103 return;
104 }
105
106 WaitableEventWrapper wrapper;
107 wrapper.event = new base::WaitableEvent(true, false);
108 wrapper.refcount = 1;
109 modal_dialog_event_map_[params.containing_window] = wrapper;
110 }
111
112 void OnSignalModalDialogEvent(gfx::NativeViewId containing_window) {
113 AutoLock auto_lock(modal_dialog_event_map_lock_);
114 if (modal_dialog_event_map_.count(containing_window))
115 modal_dialog_event_map_[containing_window].event->Signal();
116 }
117
118 void OnResetModalDialogEvent(gfx::NativeViewId containing_window) {
119 AutoLock auto_lock(modal_dialog_event_map_lock_);
120 if (modal_dialog_event_map_.count(containing_window))
121 modal_dialog_event_map_[containing_window].event->Reset();
122 }
123
124 struct WaitableEventWrapper {
125 base::WaitableEvent* event;
126 int refcount; // There could be multiple plugin instances per tab.
127 };
128 typedef std::map<gfx::NativeViewId, WaitableEventWrapper> ModalDialogEventMap;
129 ModalDialogEventMap modal_dialog_event_map_;
130 Lock modal_dialog_event_map_lock_;
131
132 IPC::Channel* channel_;
133};
134
135
[email protected]76543b92009-08-31 17:27:45136PluginChannel* PluginChannel::GetPluginChannel(int renderer_id,
137 MessageLoop* ipc_message_loop) {
138 // Map renderer ID to a (single) channel to that process.
[email protected]9a3a293b2009-06-04 22:28:16139 std::string channel_name = StringPrintf(
[email protected]76543b92009-08-31 17:27:45140 "%d.r%d", base::GetCurrentProcId(), renderer_id);
initial.commit09911bf2008-07-26 23:55:29141
[email protected]c84a7852009-09-16 21:36:44142 PluginChannel* channel =
143 static_cast<PluginChannel*>(PluginChannelBase::GetChannel(
144 channel_name,
145 IPC::Channel::MODE_SERVER,
146 ClassFactory,
147 ipc_message_loop,
148 false));
149
150 if (channel)
151 channel->renderer_id_ = renderer_id;
152
153 return channel;
initial.commit09911bf2008-07-26 23:55:29154}
155
[email protected]3dfc7a42009-06-27 01:43:51156PluginChannel::PluginChannel()
157 : renderer_handle_(0),
[email protected]c84a7852009-09-16 21:36:44158 renderer_id_(-1),
[email protected]3dfc7a42009-06-27 01:43:51159#if defined(OS_POSIX)
160 renderer_fd_(-1),
161#endif
162 in_send_(0),
[email protected]8beff0762009-09-29 02:18:30163 off_the_record_(false),
164 filter_(new MessageFilter()) {
[email protected]aacd63892009-11-18 21:12:34165 SendUnblockingOnlyDuringSyncDispatch();
[email protected]51d70e02009-03-27 20:45:59166 ChildProcess::current()->AddRefProcess();
[email protected]bf24d2c2009-02-24 23:07:45167 const CommandLine* command_line = CommandLine::ForCurrentProcess();
168 log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
initial.commit09911bf2008-07-26 23:55:29169}
170
171PluginChannel::~PluginChannel() {
[email protected]4a8278472010-03-18 16:14:49172#if defined(OS_POSIX)
173 // Won't be needing this any more.
174 CloseRendererFD();
175#endif
176
[email protected]157e5d22009-04-23 18:43:35177 if (renderer_handle_)
178 base::CloseProcessHandle(renderer_handle_);
[email protected]4a8278472010-03-18 16:14:49179
[email protected]f137d13a2009-08-07 22:57:06180 MessageLoop::current()->PostDelayedTask(FROM_HERE, new PluginReleaseTask(),
181 kPluginReleaseTimeMS);
initial.commit09911bf2008-07-26 23:55:29182}
183
184bool PluginChannel::Send(IPC::Message* msg) {
185 in_send_++;
[email protected]bf24d2c2009-02-24 23:07:45186 if (log_messages_) {
187 LOG(INFO) << "sending message @" << msg << " on channel @" << this
188 << " with type " << msg->type();
189 }
initial.commit09911bf2008-07-26 23:55:29190 bool result = PluginChannelBase::Send(msg);
191 in_send_--;
192 return result;
193}
194
[email protected]bf24d2c2009-02-24 23:07:45195void PluginChannel::OnMessageReceived(const IPC::Message& msg) {
196 if (log_messages_) {
197 LOG(INFO) << "received message @" << &msg << " on channel @" << this
198 << " with type " << msg.type();
199 }
200 PluginChannelBase::OnMessageReceived(msg);
201}
202
initial.commit09911bf2008-07-26 23:55:29203void PluginChannel::OnControlMessageReceived(const IPC::Message& msg) {
204 IPC_BEGIN_MESSAGE_MAP(PluginChannel, msg)
205 IPC_MESSAGE_HANDLER(PluginMsg_CreateInstance, OnCreateInstance)
[email protected]f09c7182009-03-10 12:54:04206 IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_DestroyInstance,
207 OnDestroyInstance)
initial.commit09911bf2008-07-26 23:55:29208 IPC_MESSAGE_HANDLER(PluginMsg_GenerateRouteID, OnGenerateRouteID)
209 IPC_MESSAGE_UNHANDLED_ERROR()
210 IPC_END_MESSAGE_MAP()
211}
212
213void PluginChannel::OnCreateInstance(const std::string& mime_type,
214 int* instance_id) {
215 *instance_id = GenerateRouteID();
216 scoped_refptr<WebPluginDelegateStub> stub = new WebPluginDelegateStub(
217 mime_type, *instance_id, this);
218 AddRoute(*instance_id, stub, false);
219 plugin_stubs_.push_back(stub);
220}
221
222void PluginChannel::OnDestroyInstance(int instance_id,
223 IPC::Message* reply_msg) {
224 for (size_t i = 0; i < plugin_stubs_.size(); ++i) {
225 if (plugin_stubs_[i]->instance_id() == instance_id) {
[email protected]49f69ae2009-10-06 19:11:22226 scoped_refptr<MessageFilter> filter(filter_);
227 gfx::NativeViewId window =
228 plugin_stubs_[i]->webplugin()->containing_window();
initial.commit09911bf2008-07-26 23:55:29229 plugin_stubs_.erase(plugin_stubs_.begin() + i);
initial.commit09911bf2008-07-26 23:55:29230 Send(reply_msg);
[email protected]49f69ae2009-10-06 19:11:22231 RemoveRoute(instance_id);
232 // NOTE: *this* might be deleted as a result of calling RemoveRoute.
233 // Don't release the modal dialog event right away, but do it after the
234 // stack unwinds since the plugin can be destroyed later if it's in use
235 // right now.
236 MessageLoop::current()->PostNonNestableTask(FROM_HERE, NewRunnableMethod(
237 filter.get(), &MessageFilter::ReleaseModalDialogEvent, window));
initial.commit09911bf2008-07-26 23:55:29238 return;
239 }
240 }
241
242 NOTREACHED() << "Couldn't find WebPluginDelegateStub to destroy";
243}
244
245void PluginChannel::OnGenerateRouteID(int* route_id) {
246 *route_id = GenerateRouteID();
247}
248
249int PluginChannel::GenerateRouteID() {
[email protected]157e5d22009-04-23 18:43:35250 static int last_id = 0;
251 return ++last_id;
initial.commit09911bf2008-07-26 23:55:29252}
253
[email protected]8beff0762009-09-29 02:18:30254base::WaitableEvent* PluginChannel::GetModalDialogEvent(
255 gfx::NativeViewId containing_window) {
256 return filter_->GetModalDialogEvent(containing_window);
257}
258
[email protected]4566f132009-03-12 01:55:13259void PluginChannel::OnChannelConnected(int32 peer_pid) {
[email protected]4a8278472010-03-18 16:14:49260#if defined(OS_POSIX)
261 // By this point, the renderer must have its own copy of the plugin channel
262 // FD.
263 CloseRendererFD();
264#endif
265
[email protected]6c6cc802009-04-03 17:01:36266 base::ProcessHandle handle;
267 if (!base::OpenProcessHandle(peer_pid, &handle)) {
268 NOTREACHED();
269 }
[email protected]157e5d22009-04-23 18:43:35270 renderer_handle_ = handle;
[email protected]4566f132009-03-12 01:55:13271 PluginChannelBase::OnChannelConnected(peer_pid);
272}
273
initial.commit09911bf2008-07-26 23:55:29274void PluginChannel::OnChannelError() {
[email protected]4a8278472010-03-18 16:14:49275#if defined(OS_POSIX)
276 // Won't be needing this any more.
277 CloseRendererFD();
278#endif
279
[email protected]157e5d22009-04-23 18:43:35280 base::CloseProcessHandle(renderer_handle_);
281 renderer_handle_ = 0;
initial.commit09911bf2008-07-26 23:55:29282 PluginChannelBase::OnChannelError();
283 CleanUp();
284}
285
286void PluginChannel::CleanUp() {
287 // We need to clean up the stubs so that they call NPPDestroy. This will
288 // also lead to them releasing their reference on this object so that it can
289 // be deleted.
290 for (size_t i = 0; i < plugin_stubs_.size(); ++i)
291 RemoveRoute(plugin_stubs_[i]->instance_id());
292
293 // Need to addref this object temporarily because otherwise removing the last
294 // stub will cause the destructor of this object to be called, however at
295 // that point plugin_stubs_ will have one element and its destructor will be
296 // called twice.
297 scoped_refptr<PluginChannel> me(this);
298
299 plugin_stubs_.clear();
license.botbf09a502008-08-24 00:55:55300}
[email protected]3dfc7a42009-06-27 01:43:51301
302bool PluginChannel::Init(MessageLoop* ipc_message_loop, bool create_pipe_now) {
303#if defined(OS_POSIX)
304 // This gets called when the PluginChannel is initially created. At this
305 // point, create the socketpair and assign the plugin side FD to the channel
306 // name. Keep the renderer side FD as a member variable in the PluginChannel
307 // to be able to transmit it through IPC.
308 int plugin_fd;
[email protected]4a8278472010-03-18 16:14:49309 if (!IPC::SocketPair(&plugin_fd, &renderer_fd_))
310 return false;
[email protected]3dfc7a42009-06-27 01:43:51311 IPC::AddChannelSocket(channel_name(), plugin_fd);
312#endif
[email protected]4a8278472010-03-18 16:14:49313
[email protected]8beff0762009-09-29 02:18:30314 if (!PluginChannelBase::Init(ipc_message_loop, create_pipe_now))
315 return false;
316
317 channel_->AddFilter(filter_.get());
318 return true;
[email protected]3dfc7a42009-06-27 01:43:51319}
[email protected]4a8278472010-03-18 16:14:49320
321#if defined(OS_POSIX)
322void PluginChannel::CloseRendererFD() {
323 if (renderer_fd_ != -1) {
324 HANDLE_EINTR(close(renderer_fd_));
325 renderer_fd_ = -1;
326 }
327}
328#endif