diff options
Diffstat (limited to 'chrome/plugin/plugin_channel.cc')
-rw-r--r-- | chrome/plugin/plugin_channel.cc | 121 |
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; } |