summaryrefslogtreecommitdiffstats
path: root/chrome/plugin/plugin_channel.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/plugin/plugin_channel.cc')
-rw-r--r--chrome/plugin/plugin_channel.cc121
1 files changed, 119 insertions, 2 deletions
diff --git a/chrome/plugin/plugin_channel.cc b/chrome/plugin/plugin_channel.cc
index f852a21..54ed6f3 100644
--- a/chrome/plugin/plugin_channel.cc
+++ b/chrome/plugin/plugin_channel.cc
@@ -5,13 +5,17 @@
#include "chrome/plugin/plugin_channel.h"
#include "base/command_line.h"
+#include "base/lock.h"
#include "base/process_util.h"
#include "base/string_util.h"
+#include "base/waitable_event.h"
#include "build/build_config.h"
#include "chrome/common/child_process.h"
#include "chrome/common/plugin_messages.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/plugin/plugin_thread.h"
+#include "chrome/plugin/webplugin_delegate_stub.h"
+#include "chrome/plugin/webplugin_proxy.h"
#if defined(OS_POSIX)
#include "ipc/ipc_channel_posix.h"
@@ -27,6 +31,107 @@ class PluginReleaseTask : public Task {
// How long we wait before releasing the plugin process.
static const int kPluginReleaseTimeMS = 10000;
+
+// If a sync call to the renderer results in a modal dialog, we need to have a
+// way to know so that we can run a nested message loop to simulate what would
+// happen in a single process browser and avoid deadlock.
+class PluginChannel::MessageFilter : public IPC::ChannelProxy::MessageFilter {
+ public:
+ MessageFilter() : channel_(NULL) { }
+ ~MessageFilter() {
+ // Clean up in case of renderer crash.
+ for (ModalDialogEventMap::iterator i = modal_dialog_event_map_.begin();
+ i != modal_dialog_event_map_.end(); ++i) {
+ delete i->second.event;
+ }
+ }
+
+ base::WaitableEvent* GetModalDialogEvent(
+ gfx::NativeViewId containing_window) {
+ AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (!modal_dialog_event_map_.count(containing_window)) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ return modal_dialog_event_map_[containing_window].event;
+ }
+
+ // Decrement the ref count associated with the modal dialog event for the
+ // given tab.
+ void ReleaseModalDialogEvent(gfx::NativeViewId containing_window) {
+ AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (!modal_dialog_event_map_.count(containing_window)) {
+ NOTREACHED();
+ return;
+ }
+
+ if (--(modal_dialog_event_map_[containing_window].refcount))
+ return;
+
+ // Delete the event when the stack unwinds as it could be in use now.
+ MessageLoop::current()->DeleteSoon(
+ FROM_HERE, modal_dialog_event_map_[containing_window].event);
+ modal_dialog_event_map_.erase(containing_window);
+ }
+
+ bool Send(IPC::Message* message) {
+ // Need this function for the IPC_MESSAGE_HANDLER_DELAY_REPLY macro.
+ return channel_->Send(message);
+ }
+
+ private:
+ void OnFilterAdded(IPC::Channel* channel) { channel_ = channel; }
+
+ bool OnMessageReceived(const IPC::Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(PluginChannel::MessageFilter, message)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_Init, OnInit)
+ IPC_MESSAGE_HANDLER(PluginMsg_SignalModalDialogEvent,
+ OnSignalModalDialogEvent)
+ IPC_MESSAGE_HANDLER(PluginMsg_ResetModalDialogEvent,
+ OnResetModalDialogEvent)
+ IPC_END_MESSAGE_MAP()
+ return message.type() == PluginMsg_SignalModalDialogEvent::ID ||
+ message.type() == PluginMsg_ResetModalDialogEvent::ID;
+ }
+
+ void OnInit(const PluginMsg_Init_Params& params, IPC::Message* reply_msg) {
+ AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (modal_dialog_event_map_.count(params.containing_window)) {
+ modal_dialog_event_map_[params.containing_window].refcount++;
+ return;
+ }
+
+ WaitableEventWrapper wrapper;
+ wrapper.event = new base::WaitableEvent(true, false);
+ wrapper.refcount = 1;
+ modal_dialog_event_map_[params.containing_window] = wrapper;
+ }
+
+ void OnSignalModalDialogEvent(gfx::NativeViewId containing_window) {
+ AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (modal_dialog_event_map_.count(containing_window))
+ modal_dialog_event_map_[containing_window].event->Signal();
+ }
+
+ void OnResetModalDialogEvent(gfx::NativeViewId containing_window) {
+ AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (modal_dialog_event_map_.count(containing_window))
+ modal_dialog_event_map_[containing_window].event->Reset();
+ }
+
+ struct WaitableEventWrapper {
+ base::WaitableEvent* event;
+ int refcount; // There could be multiple plugin instances per tab.
+ };
+ typedef std::map<gfx::NativeViewId, WaitableEventWrapper> ModalDialogEventMap;
+ ModalDialogEventMap modal_dialog_event_map_;
+ Lock modal_dialog_event_map_lock_;
+
+ IPC::Channel* channel_;
+};
+
+
PluginChannel* PluginChannel::GetPluginChannel(int renderer_id,
MessageLoop* ipc_message_loop) {
// Map renderer ID to a (single) channel to that process.
@@ -54,7 +159,8 @@ PluginChannel::PluginChannel()
renderer_fd_(-1),
#endif
in_send_(0),
- off_the_record_(false) {
+ off_the_record_(false),
+ filter_(new MessageFilter()) {
SendUnblockingOnlyDuringDispatch();
ChildProcess::current()->AddRefProcess();
const CommandLine* command_line = CommandLine::ForCurrentProcess();
@@ -116,6 +222,8 @@ void PluginChannel::OnDestroyInstance(int instance_id,
IPC::Message* reply_msg) {
for (size_t i = 0; i < plugin_stubs_.size(); ++i) {
if (plugin_stubs_[i]->instance_id() == instance_id) {
+ filter_->ReleaseModalDialogEvent(
+ plugin_stubs_[i]->webplugin()->containing_window());
plugin_stubs_.erase(plugin_stubs_.begin() + i);
RemoveRoute(instance_id);
Send(reply_msg);
@@ -135,6 +243,11 @@ int PluginChannel::GenerateRouteID() {
return ++last_id;
}
+base::WaitableEvent* PluginChannel::GetModalDialogEvent(
+ gfx::NativeViewId containing_window) {
+ return filter_->GetModalDialogEvent(containing_window);
+}
+
void PluginChannel::OnChannelConnected(int32 peer_pid) {
base::ProcessHandle handle;
if (!base::OpenProcessHandle(peer_pid, &handle)) {
@@ -177,5 +290,9 @@ bool PluginChannel::Init(MessageLoop* ipc_message_loop, bool create_pipe_now) {
IPC::SocketPair(&plugin_fd, &renderer_fd_);
IPC::AddChannelSocket(channel_name(), plugin_fd);
#endif
- return PluginChannelBase::Init(ipc_message_loop, create_pipe_now);
+ if (!PluginChannelBase::Init(ipc_message_loop, create_pipe_now))
+ return false;
+
+ channel_->AddFilter(filter_.get());
+ return true;
}