Commit 6b328f3d authored by dmichael's avatar dmichael Committed by Commit bot

PPAPI: Never re-enter JavaScript for PostMessage.

Blocking renderer->plugin messages can be interrupted by any message
from the plugin->renderer (even async ones). So while handline a blocking
message, such as HandleInputEvent or HandleBlockingMessage, it's currently
possible to re-enter JavaScript. This patch makes that impossible by
queueing up Plugin->Renderer messages sent via PPB_Messaging::PostMessage
while any renderer->plugin sync message is on the stack.

BUG=384528

Committed: https://crrev.com/f73075c99b5ba30e8d62dc5f13fdfb210d0fc506
Cr-Commit-Position: refs/heads/master@{#296311}

Committed: https://crrev.com/3fe4ceee750b2cd130bd402de3d371d8518c3eba
Cr-Commit-Position: refs/heads/master@{#296807}

Review URL: https://codereview.chromium.org/589213003

Cr-Commit-Position: refs/heads/master@{#297308}
parent b92e6f5c
...@@ -32,7 +32,7 @@ HostDispatcherWrapper::~HostDispatcherWrapper() {} ...@@ -32,7 +32,7 @@ HostDispatcherWrapper::~HostDispatcherWrapper() {}
bool HostDispatcherWrapper::Init(const IPC::ChannelHandle& channel_handle, bool HostDispatcherWrapper::Init(const IPC::ChannelHandle& channel_handle,
PP_GetInterface_Func local_get_interface, PP_GetInterface_Func local_get_interface,
const ppapi::Preferences& preferences, const ppapi::Preferences& preferences,
PepperHungPluginFilter* filter) { scoped_refptr<PepperHungPluginFilter> filter) {
if (channel_handle.name.empty()) if (channel_handle.name.empty())
return false; return false;
...@@ -44,7 +44,13 @@ bool HostDispatcherWrapper::Init(const IPC::ChannelHandle& channel_handle, ...@@ -44,7 +44,13 @@ bool HostDispatcherWrapper::Init(const IPC::ChannelHandle& channel_handle,
dispatcher_delegate_.reset(new PepperProxyChannelDelegateImpl); dispatcher_delegate_.reset(new PepperProxyChannelDelegateImpl);
dispatcher_.reset(new ppapi::proxy::HostDispatcher( dispatcher_.reset(new ppapi::proxy::HostDispatcher(
module_->pp_module(), local_get_interface, filter, permissions_)); module_->pp_module(), local_get_interface, permissions_));
// The HungPluginFilter needs to know when we are blocked on a sync message
// to the plugin. Note the filter outlives the dispatcher, so there is no
// need to remove it as an observer.
dispatcher_->AddSyncMessageStatusObserver(filter.get());
// Guarantee the hung_plugin_filter_ outlives |dispatcher_|.
hung_plugin_filter_ = filter;
if (!dispatcher_->InitHostWithChannel(dispatcher_delegate_.get(), if (!dispatcher_->InitHostWithChannel(dispatcher_delegate_.get(),
peer_pid_, peer_pid_,
...@@ -55,6 +61,9 @@ bool HostDispatcherWrapper::Init(const IPC::ChannelHandle& channel_handle, ...@@ -55,6 +61,9 @@ bool HostDispatcherWrapper::Init(const IPC::ChannelHandle& channel_handle,
dispatcher_delegate_.reset(); dispatcher_delegate_.reset();
return false; return false;
} }
// HungPluginFilter needs to listen for some messages on the IO thread.
dispatcher_->AddIOThreadMessageFilter(filter);
dispatcher_->channel()->SetRestrictDispatchChannelGroup( dispatcher_->channel()->SetRestrictDispatchChannelGroup(
kRendererRestrictDispatchGroup_Pepper); kRendererRestrictDispatchGroup_Pepper);
return true; return true;
......
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
#ifndef CONTENT_RENDERER_PEPPER_HOST_DISPATCHER_WRAPPER_H_ #ifndef CONTENT_RENDERER_PEPPER_HOST_DISPATCHER_WRAPPER_H_
#define CONTENT_RENDERER_PEPPER_HOST_DISPATCHER_WRAPPER_H_ #define CONTENT_RENDERER_PEPPER_HOST_DISPATCHER_WRAPPER_H_
#include "base/memory/ref_counted.h"
#include "base/process/process_handle.h" #include "base/process/process_handle.h"
#include "content/renderer/pepper/pepper_hung_plugin_filter.h"
#include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_instance.h"
#include "ppapi/c/ppp.h" #include "ppapi/c/ppp.h"
#include "ppapi/proxy/host_dispatcher.h" #include "ppapi/proxy/host_dispatcher.h"
...@@ -34,7 +36,7 @@ class HostDispatcherWrapper { ...@@ -34,7 +36,7 @@ class HostDispatcherWrapper {
bool Init(const IPC::ChannelHandle& channel_handle, bool Init(const IPC::ChannelHandle& channel_handle,
PP_GetInterface_Func local_get_interface, PP_GetInterface_Func local_get_interface,
const ppapi::Preferences& preferences, const ppapi::Preferences& preferences,
PepperHungPluginFilter* filter); scoped_refptr<PepperHungPluginFilter> filter);
// Implements GetInterface for the proxied plugin. // Implements GetInterface for the proxied plugin.
const void* GetProxiedInterface(const char* name); const void* GetProxiedInterface(const char* name);
...@@ -69,6 +71,9 @@ class HostDispatcherWrapper { ...@@ -69,6 +71,9 @@ class HostDispatcherWrapper {
scoped_ptr<ppapi::proxy::HostDispatcher> dispatcher_; scoped_ptr<ppapi::proxy::HostDispatcher> dispatcher_;
scoped_ptr<ppapi::proxy::ProxyChannel::Delegate> dispatcher_delegate_; scoped_ptr<ppapi::proxy::ProxyChannel::Delegate> dispatcher_delegate_;
// We hold the hung_plugin_filter_ to guarantee it outlives |dispatcher_|,
// since it is an observer of |dispatcher_| for sync calls.
scoped_refptr<PepperHungPluginFilter> hung_plugin_filter_;
}; };
} // namespace content } // namespace content
......
...@@ -98,12 +98,15 @@ MessageChannel* MessageChannel::Create(PepperPluginInstanceImpl* instance, ...@@ -98,12 +98,15 @@ MessageChannel* MessageChannel::Create(PepperPluginInstanceImpl* instance,
} }
MessageChannel::~MessageChannel() { MessageChannel::~MessageChannel() {
UnregisterSyncMessageStatusObserver();
passthrough_object_.Reset(); passthrough_object_.Reset();
if (instance_) if (instance_)
instance_->MessageChannelDestroyed(); instance_->MessageChannelDestroyed();
} }
void MessageChannel::InstanceDeleted() { void MessageChannel::InstanceDeleted() {
UnregisterSyncMessageStatusObserver();
instance_ = NULL; instance_ = NULL;
} }
...@@ -131,27 +134,38 @@ void MessageChannel::PostMessageToJavaScript(PP_Var message_data) { ...@@ -131,27 +134,38 @@ void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
WebSerializedScriptValue serialized_val = WebSerializedScriptValue serialized_val =
WebSerializedScriptValue::serialize(v8_val); WebSerializedScriptValue::serialize(v8_val);
if (early_message_queue_state_ != SEND_DIRECTLY) { if (js_message_queue_state_ != SEND_DIRECTLY) {
// We can't just PostTask here; the messages would arrive out of // We can't just PostTask here; the messages would arrive out of
// order. Instead, we queue them up until we're ready to post // order. Instead, we queue them up until we're ready to post
// them. // them.
early_message_queue_.push_back(serialized_val); js_message_queue_.push_back(serialized_val);
} else { } else {
// The proxy sent an asynchronous message, so the plugin is already // The proxy sent an asynchronous message, so the plugin is already
// unblocked. Therefore, there's no need to PostTask. // unblocked. Therefore, there's no need to PostTask.
DCHECK(early_message_queue_.empty()); DCHECK(js_message_queue_.empty());
PostMessageToJavaScriptImpl(serialized_val); PostMessageToJavaScriptImpl(serialized_val);
} }
} }
void MessageChannel::Start() { void MessageChannel::Start() {
// We PostTask here instead of draining the message queue directly DCHECK_EQ(WAITING_TO_START, js_message_queue_state_);
// since we haven't finished initializing the PepperWebPluginImpl yet, so DCHECK_EQ(WAITING_TO_START, plugin_message_queue_state_);
// the plugin isn't available in the DOM.
base::MessageLoop::current()->PostTask( ppapi::proxy::HostDispatcher* dispatcher =
FROM_HERE, ppapi::proxy::HostDispatcher::GetForInstance(instance_->pp_instance());
base::Bind(&MessageChannel::DrainEarlyMessageQueue, // The dispatcher is NULL for in-process.
weak_ptr_factory_.GetWeakPtr())); if (dispatcher) {
unregister_observer_callback_ =
dispatcher->AddSyncMessageStatusObserver(this);
}
// We can't drain the JS message queue directly since we haven't finished
// initializing the PepperWebPluginImpl yet, so the plugin isn't available in
// the DOM.
DrainJSMessageQueueSoon();
plugin_message_queue_state_ = SEND_DIRECTLY;
DrainCompletedPluginMessages();
} }
void MessageChannel::SetPassthroughObject(v8::Handle<v8::Object> passthrough) { void MessageChannel::SetPassthroughObject(v8::Handle<v8::Object> passthrough) {
...@@ -170,7 +184,9 @@ void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) { ...@@ -170,7 +184,9 @@ void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) {
MessageChannel::MessageChannel(PepperPluginInstanceImpl* instance) MessageChannel::MessageChannel(PepperPluginInstanceImpl* instance)
: gin::NamedPropertyInterceptor(instance->GetIsolate(), this), : gin::NamedPropertyInterceptor(instance->GetIsolate(), this),
instance_(instance), instance_(instance),
early_message_queue_state_(QUEUE_MESSAGES), js_message_queue_state_(WAITING_TO_START),
blocking_message_depth_(0),
plugin_message_queue_state_(WAITING_TO_START),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
} }
...@@ -180,6 +196,18 @@ gin::ObjectTemplateBuilder MessageChannel::GetObjectTemplateBuilder( ...@@ -180,6 +196,18 @@ gin::ObjectTemplateBuilder MessageChannel::GetObjectTemplateBuilder(
.AddNamedPropertyInterceptor(); .AddNamedPropertyInterceptor();
} }
void MessageChannel::BeginBlockOnSyncMessage() {
js_message_queue_state_ = QUEUE_MESSAGES;
++blocking_message_depth_;
}
void MessageChannel::EndBlockOnSyncMessage() {
DCHECK_GT(blocking_message_depth_, 0);
--blocking_message_depth_;
if (!blocking_message_depth_)
DrainJSMessageQueueSoon();
}
v8::Local<v8::Value> MessageChannel::GetNamedProperty( v8::Local<v8::Value> MessageChannel::GetNamedProperty(
v8::Isolate* isolate, v8::Isolate* isolate,
const std::string& identifier) { const std::string& identifier) {
...@@ -279,7 +307,7 @@ void MessageChannel::PostBlockingMessageToNative(gin::Arguments* args) { ...@@ -279,7 +307,7 @@ void MessageChannel::PostBlockingMessageToNative(gin::Arguments* args) {
NOTREACHED(); NOTREACHED();
} }
if (early_message_queue_state_ == QUEUE_MESSAGES) { if (plugin_message_queue_state_ == WAITING_TO_START) {
try_catch.ThrowException( try_catch.ThrowException(
"Attempted to call a synchronous method on a plugin that was not " "Attempted to call a synchronous method on a plugin that was not "
"yet loaded."); "yet loaded.");
...@@ -392,7 +420,7 @@ void MessageChannel::FromV8ValueComplete(VarConversionResult* result_holder, ...@@ -392,7 +420,7 @@ void MessageChannel::FromV8ValueComplete(VarConversionResult* result_holder,
void MessageChannel::DrainCompletedPluginMessages() { void MessageChannel::DrainCompletedPluginMessages() {
DCHECK(instance_); DCHECK(instance_);
if (early_message_queue_state_ == QUEUE_MESSAGES) if (plugin_message_queue_state_ == WAITING_TO_START)
return; return;
while (!plugin_message_queue_.empty() && while (!plugin_message_queue_.empty() &&
...@@ -410,22 +438,35 @@ void MessageChannel::DrainCompletedPluginMessages() { ...@@ -410,22 +438,35 @@ void MessageChannel::DrainCompletedPluginMessages() {
} }
} }
void MessageChannel::DrainEarlyMessageQueue() { void MessageChannel::DrainJSMessageQueue() {
if (!instance_) if (!instance_)
return; return;
DCHECK(early_message_queue_state_ == QUEUE_MESSAGES); if (js_message_queue_state_ == SEND_DIRECTLY)
return;
// Take a reference on the PluginInstance. This is because JavaScript code // Take a reference on the PluginInstance. This is because JavaScript code
// may delete the plugin, which would destroy the PluginInstance and its // may delete the plugin, which would destroy the PluginInstance and its
// corresponding MessageChannel. // corresponding MessageChannel.
scoped_refptr<PepperPluginInstanceImpl> instance_ref(instance_); scoped_refptr<PepperPluginInstanceImpl> instance_ref(instance_);
while (!early_message_queue_.empty()) { while (!js_message_queue_.empty()) {
PostMessageToJavaScriptImpl(early_message_queue_.front()); PostMessageToJavaScriptImpl(js_message_queue_.front());
early_message_queue_.pop_front(); js_message_queue_.pop_front();
} }
early_message_queue_state_ = SEND_DIRECTLY; js_message_queue_state_ = SEND_DIRECTLY;
}
DrainCompletedPluginMessages(); void MessageChannel::DrainJSMessageQueueSoon() {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&MessageChannel::DrainJSMessageQueue,
weak_ptr_factory_.GetWeakPtr()));
}
void MessageChannel::UnregisterSyncMessageStatusObserver() {
if (!unregister_observer_callback_.is_null()) {
unregister_observer_callback_.Run();
unregister_observer_callback_.Reset();
}
} }
} // namespace content } // namespace content
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "gin/handle.h" #include "gin/handle.h"
#include "gin/interceptor.h" #include "gin/interceptor.h"
#include "gin/wrappable.h" #include "gin/wrappable.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/shared_impl/resource.h" #include "ppapi/shared_impl/resource.h"
#include "third_party/WebKit/public/web/WebSerializedScriptValue.h" #include "third_party/WebKit/public/web/WebSerializedScriptValue.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
...@@ -46,8 +47,10 @@ class PluginObject; ...@@ -46,8 +47,10 @@ class PluginObject;
// - The message target won't be limited to instance, and should support // - The message target won't be limited to instance, and should support
// either plugin-provided or JS objects. // either plugin-provided or JS objects.
// TODO(dmichael): Add support for separate MessagePorts. // TODO(dmichael): Add support for separate MessagePorts.
class MessageChannel : public gin::Wrappable<MessageChannel>, class MessageChannel :
public gin::NamedPropertyInterceptor { public gin::Wrappable<MessageChannel>,
public gin::NamedPropertyInterceptor,
public ppapi::proxy::HostDispatcher::SyncMessageStatusObserver {
public: public:
static gin::WrapperInfo kWrapperInfo; static gin::WrapperInfo kWrapperInfo;
...@@ -102,6 +105,10 @@ class MessageChannel : public gin::Wrappable<MessageChannel>, ...@@ -102,6 +105,10 @@ class MessageChannel : public gin::Wrappable<MessageChannel>,
virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder( virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) OVERRIDE; v8::Isolate* isolate) OVERRIDE;
// ppapi::proxy::HostDispatcher::SyncMessageStatusObserver
virtual void BeginBlockOnSyncMessage() OVERRIDE;
virtual void EndBlockOnSyncMessage() OVERRIDE;
// Post a message to the plugin's HandleMessage function for this channel's // Post a message to the plugin's HandleMessage function for this channel's
// instance. // instance.
void PostMessageToNative(gin::Arguments* args); void PostMessageToNative(gin::Arguments* args);
...@@ -122,8 +129,18 @@ class MessageChannel : public gin::Wrappable<MessageChannel>, ...@@ -122,8 +129,18 @@ class MessageChannel : public gin::Wrappable<MessageChannel>,
const ppapi::ScopedPPVar& result_var, const ppapi::ScopedPPVar& result_var,
bool success); bool success);
// Drain the queue of messages that are going to the plugin. All "completed"
// messages at the head of the queue will be sent; any messages awaiting
// conversion as well as messages after that in the queue will not be sent.
void DrainCompletedPluginMessages(); void DrainCompletedPluginMessages();
void DrainEarlyMessageQueue(); // Drain the queue of messages that are going to JavaScript.
void DrainJSMessageQueue();
// PostTask to call DrainJSMessageQueue() soon. Use this when you want to
// send the messages, but can't immediately (e.g., because the instance is
// not ready or JavaScript is on the stack).
void DrainJSMessageQueueSoon();
void UnregisterSyncMessageStatusObserver();
PepperPluginInstanceImpl* instance_; PepperPluginInstanceImpl* instance_;
...@@ -134,12 +151,21 @@ class MessageChannel : public gin::Wrappable<MessageChannel>, ...@@ -134,12 +151,21 @@ class MessageChannel : public gin::Wrappable<MessageChannel>,
// scripting. // scripting.
v8::Persistent<v8::Object> passthrough_object_; v8::Persistent<v8::Object> passthrough_object_;
std::deque<blink::WebSerializedScriptValue> early_message_queue_; enum MessageQueueState {
enum EarlyMessageQueueState { WAITING_TO_START, // Waiting for Start() to be called. Queue messages.
QUEUE_MESSAGES, // Queue JS messages. QUEUE_MESSAGES, // Queue messages temporarily.
SEND_DIRECTLY, // Post JS messages directly. SEND_DIRECTLY, // Post messages directly.
}; };
EarlyMessageQueueState early_message_queue_state_;
// This queue stores values being posted to JavaScript.
std::deque<blink::WebSerializedScriptValue> js_message_queue_;
MessageQueueState js_message_queue_state_;
// When the renderer is sending a blocking message to the plugin, we will
// queue Plugin->JS messages temporarily to avoid re-entering JavaScript. This
// counts how many blocking renderer->plugin messages are on the stack so that
// we only begin sending messages to JavaScript again when the depth reaches
// zero.
int blocking_message_depth_;
// This queue stores vars that are being sent to the plugin. Because // This queue stores vars that are being sent to the plugin. Because
// conversion can happen asynchronously for object types, the queue stores // conversion can happen asynchronously for object types, the queue stores
...@@ -150,9 +176,14 @@ class MessageChannel : public gin::Wrappable<MessageChannel>, ...@@ -150,9 +176,14 @@ class MessageChannel : public gin::Wrappable<MessageChannel>,
// calls to push_back or pop_front; hence why we're using list. (deque would // calls to push_back or pop_front; hence why we're using list. (deque would
// probably also work, but is less clearly specified). // probably also work, but is less clearly specified).
std::list<VarConversionResult> plugin_message_queue_; std::list<VarConversionResult> plugin_message_queue_;
MessageQueueState plugin_message_queue_state_;
std::map<std::string, ppapi::ScopedPPVar> internal_named_properties_; std::map<std::string, ppapi::ScopedPPVar> internal_named_properties_;
// A callback to invoke at shutdown to ensure we unregister ourselves as
// Observers for sync messages.
base::Closure unregister_observer_callback_;
// This is used to ensure pending tasks will not fire after this object is // This is used to ensure pending tasks will not fire after this object is
// destroyed. // destroyed.
base::WeakPtrFactory<MessageChannel> weak_ptr_factory_; base::WeakPtrFactory<MessageChannel> weak_ptr_factory_;
......
...@@ -26,9 +26,10 @@ namespace content { ...@@ -26,9 +26,10 @@ namespace content {
// thread. This is important since when we're blocked on a sync message to a // thread. This is important since when we're blocked on a sync message to a
// hung plugin, the main thread is frozen. // hung plugin, the main thread is frozen.
// //
// NOTE: This class is refcounted (via SyncMessageStatusReceiver). // NOTE: This class is refcounted (via IPC::MessageFilter).
class PepperHungPluginFilter class PepperHungPluginFilter
: public ppapi::proxy::HostDispatcher::SyncMessageStatusReceiver { : public ppapi::proxy::HostDispatcher::SyncMessageStatusObserver,
public IPC::MessageFilter {
public: public:
// The |frame_routing_id| is the ID of the render_frame so that this class can // The |frame_routing_id| is the ID of the render_frame so that this class can
// send messages to the browser via that frame's route. The |plugin_child_id| // send messages to the browser via that frame's route. The |plugin_child_id|
......
...@@ -47,11 +47,12 @@ InterfaceProxy* Dispatcher::GetInterfaceProxy(ApiID id) { ...@@ -47,11 +47,12 @@ InterfaceProxy* Dispatcher::GetInterfaceProxy(ApiID id) {
return proxy; return proxy;
} }
void Dispatcher::AddIOThreadMessageFilter(IPC::MessageFilter* filter) { void Dispatcher::AddIOThreadMessageFilter(
scoped_refptr<IPC::MessageFilter> filter) {
// Our filter is refcounted. The channel will call the destruct method on the // Our filter is refcounted. The channel will call the destruct method on the
// filter when the channel is done with it, so the corresponding Release() // filter when the channel is done with it, so the corresponding Release()
// happens there. // happens there.
channel()->AddFilter(filter); channel()->AddFilter(filter.get());
} }
bool Dispatcher::OnMessageReceived(const IPC::Message& msg) { bool Dispatcher::OnMessageReceived(const IPC::Message& msg) {
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/tracked_objects.h" #include "base/tracked_objects.h"
#include "ipc/message_filter.h"
#include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_module.h" #include "ppapi/c/pp_module.h"
#include "ppapi/c/ppp.h" #include "ppapi/c/ppp.h"
...@@ -62,8 +63,8 @@ class PPAPI_PROXY_EXPORT Dispatcher : public ProxyChannel { ...@@ -62,8 +63,8 @@ class PPAPI_PROXY_EXPORT Dispatcher : public ProxyChannel {
// created so far. // created so far.
InterfaceProxy* GetInterfaceProxy(ApiID id); InterfaceProxy* GetInterfaceProxy(ApiID id);
// Adds the given filter to the IO thread. Takes ownership of the pointer. // Adds the given filter to the IO thread.
void AddIOThreadMessageFilter(IPC::MessageFilter* filter); void AddIOThreadMessageFilter(scoped_refptr<IPC::MessageFilter> filter);
// IPC::Listener implementation. // IPC::Listener implementation.
virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
......
...@@ -61,13 +61,12 @@ class BoolRestorer { ...@@ -61,13 +61,12 @@ class BoolRestorer {
HostDispatcher::HostDispatcher(PP_Module module, HostDispatcher::HostDispatcher(PP_Module module,
PP_GetInterface_Func local_get_interface, PP_GetInterface_Func local_get_interface,
SyncMessageStatusReceiver* sync_status,
const PpapiPermissions& permissions) const PpapiPermissions& permissions)
: Dispatcher(local_get_interface, permissions), : Dispatcher(local_get_interface, permissions),
sync_status_(sync_status),
pp_module_(module), pp_module_(module),
ppb_proxy_(NULL), ppb_proxy_(NULL),
allow_plugin_reentrancy_(false) { allow_plugin_reentrancy_(false),
weak_ptr_factory_(this) {
if (!g_module_to_dispatcher) if (!g_module_to_dispatcher)
g_module_to_dispatcher = new ModuleToDispatcherMap; g_module_to_dispatcher = new ModuleToDispatcherMap;
(*g_module_to_dispatcher)[pp_module_] = this; (*g_module_to_dispatcher)[pp_module_] = this;
...@@ -94,8 +93,6 @@ bool HostDispatcher::InitHostWithChannel( ...@@ -94,8 +93,6 @@ bool HostDispatcher::InitHostWithChannel(
if (!Dispatcher::InitWithChannel(delegate, peer_pid, channel_handle, if (!Dispatcher::InitWithChannel(delegate, peer_pid, channel_handle,
is_client)) is_client))
return false; return false;
AddIOThreadMessageFilter(sync_status_.get());
Send(new PpapiMsg_SetPreferences(preferences)); Send(new PpapiMsg_SetPreferences(preferences));
return true; return true;
} }
...@@ -157,9 +154,11 @@ bool HostDispatcher::Send(IPC::Message* msg) { ...@@ -157,9 +154,11 @@ bool HostDispatcher::Send(IPC::Message* msg) {
// destroys the plugin module and in turn the dispatcher. // destroys the plugin module and in turn the dispatcher.
ScopedModuleReference scoped_ref(this); ScopedModuleReference scoped_ref(this);
sync_status_->BeginBlockOnSyncMessage(); FOR_EACH_OBSERVER(SyncMessageStatusObserver, sync_status_observer_list_,
BeginBlockOnSyncMessage());
bool result = Dispatcher::Send(msg); bool result = Dispatcher::Send(msg);
sync_status_->EndBlockOnSyncMessage(); FOR_EACH_OBSERVER(SyncMessageStatusObserver, sync_status_observer_list_,
EndBlockOnSyncMessage());
return result; return result;
} else { } else {
...@@ -240,6 +239,19 @@ const void* HostDispatcher::GetProxiedInterface(const std::string& iface_name) { ...@@ -240,6 +239,19 @@ const void* HostDispatcher::GetProxiedInterface(const std::string& iface_name) {
return NULL; return NULL;
} }
base::Closure HostDispatcher::AddSyncMessageStatusObserver(
SyncMessageStatusObserver* obs) {
sync_status_observer_list_.AddObserver(obs);
return base::Bind(&HostDispatcher::RemoveSyncMessageStatusObserver,
weak_ptr_factory_.GetWeakPtr(),
obs);
}
void HostDispatcher::RemoveSyncMessageStatusObserver(
SyncMessageStatusObserver* obs) {
sync_status_observer_list_.RemoveObserver(obs);
}
void HostDispatcher::AddFilter(IPC::Listener* listener) { void HostDispatcher::AddFilter(IPC::Listener* listener) {
filters_.push_back(listener); filters_.push_back(listener);
} }
......
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
#include <vector> #include <vector>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/process/process.h" #include "base/process/process.h"
#include "ipc/message_filter.h" #include "ipc/message_filter.h"
#include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_instance.h"
...@@ -27,11 +28,13 @@ namespace proxy { ...@@ -27,11 +28,13 @@ namespace proxy {
class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher { class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher {
public: public:
// This interface receives notifications about sync messages being sent by // This interface receives notifications about sync messages being sent by
// the dispatcher to the plugin process. It is used to detect a hung plugin. // the dispatcher to the plugin process. Some parts of Chrome may need to
// know whether we are sending a synchronous message to the plugin; e.g. to
// detect a hung plugin or to avoid re-entering JavaScript.
// //
// Note that there can be nested sync messages, so the begin/end status // Note that there can be nested sync messages, so the begin/end status
// actually represents a stack of blocking messages. // actually represents a stack of blocking messages.
class SyncMessageStatusReceiver : public IPC::MessageFilter { class SyncMessageStatusObserver {
public: public:
// Notification that a sync message is about to be sent out. // Notification that a sync message is about to be sent out.
virtual void BeginBlockOnSyncMessage() = 0; virtual void BeginBlockOnSyncMessage() = 0;
...@@ -41,7 +44,7 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher { ...@@ -41,7 +44,7 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher {
virtual void EndBlockOnSyncMessage() = 0; virtual void EndBlockOnSyncMessage() = 0;
protected: protected:
virtual ~SyncMessageStatusReceiver() {} virtual ~SyncMessageStatusObserver() {}
}; };
// Constructor for the renderer side. This will take a reference to the // Constructor for the renderer side. This will take a reference to the
...@@ -50,7 +53,6 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher { ...@@ -50,7 +53,6 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher {
// You must call InitHostWithChannel after the constructor. // You must call InitHostWithChannel after the constructor.
HostDispatcher(PP_Module module, HostDispatcher(PP_Module module,
PP_GetInterface_Func local_get_interface, PP_GetInterface_Func local_get_interface,
SyncMessageStatusReceiver* sync_status,
const PpapiPermissions& permissions); const PpapiPermissions& permissions);
~HostDispatcher(); ~HostDispatcher();
...@@ -102,6 +104,13 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher { ...@@ -102,6 +104,13 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher {
// Returns the proxy interface for talking to the implementation. // Returns the proxy interface for talking to the implementation.
const PPB_Proxy_Private* ppb_proxy() const { return ppb_proxy_; } const PPB_Proxy_Private* ppb_proxy() const { return ppb_proxy_; }
// Register an observer that will be invoked when the dispatcher begins
// sending a sync message and finishes sending a sync message.
// Returns a Closure that can be used to unregister the observer (the Closure
// is bound to a weak pointer, so is safe to call even after the
// HostDispatcher is gone.)
base::Closure AddSyncMessageStatusObserver(SyncMessageStatusObserver* obs);
void AddFilter(IPC::Listener* listener); void AddFilter(IPC::Listener* listener);
protected: protected:
...@@ -114,7 +123,7 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher { ...@@ -114,7 +123,7 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher {
const std::string& source, const std::string& source,
const std::string& value); const std::string& value);
scoped_refptr<SyncMessageStatusReceiver> sync_status_; void RemoveSyncMessageStatusObserver(SyncMessageStatusObserver* obs);
PP_Module pp_module_; PP_Module pp_module_;
...@@ -133,8 +142,12 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher { ...@@ -133,8 +142,12 @@ class PPAPI_PROXY_EXPORT HostDispatcher : public Dispatcher {
// ultimately call back into the plugin. // ultimately call back into the plugin.
bool allow_plugin_reentrancy_; bool allow_plugin_reentrancy_;
ObserverList<SyncMessageStatusObserver> sync_status_observer_list_;
std::vector<IPC::Listener*> filters_; std::vector<IPC::Listener*> filters_;
base::WeakPtrFactory<HostDispatcher> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HostDispatcher); DISALLOW_COPY_AND_ASSIGN(HostDispatcher);
}; };
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "ipc/ipc_sync_channel.h" #include "ipc/ipc_sync_channel.h"
#include "ipc/message_filter.h"
#include "ppapi/c/pp_errors.h" #include "ppapi/c/pp_errors.h"
#include "ppapi/c/private/ppb_proxy_private.h" #include "ppapi/c/private/ppb_proxy_private.h"
#include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/ppapi_messages.h"
...@@ -407,16 +408,8 @@ void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread( ...@@ -407,16 +408,8 @@ void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread(
// HostProxyTestHarness -------------------------------------------------------- // HostProxyTestHarness --------------------------------------------------------
class HostProxyTestHarness::MockSyncMessageStatusReceiver
: public HostDispatcher::SyncMessageStatusReceiver {
public:
virtual void BeginBlockOnSyncMessage() OVERRIDE {}
virtual void EndBlockOnSyncMessage() OVERRIDE {}
};
HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config) HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config)
: globals_config_(globals_config), : globals_config_(globals_config) {
status_receiver_(new MockSyncMessageStatusReceiver) {
} }
HostProxyTestHarness::~HostProxyTestHarness() { HostProxyTestHarness::~HostProxyTestHarness() {
...@@ -437,7 +430,6 @@ void HostProxyTestHarness::SetUpHarness() { ...@@ -437,7 +430,6 @@ void HostProxyTestHarness::SetUpHarness() {
host_dispatcher_.reset(new HostDispatcher( host_dispatcher_.reset(new HostDispatcher(
pp_module(), pp_module(),
&MockGetInterface, &MockGetInterface,
status_receiver_.release(),
PpapiPermissions::AllPermissions())); PpapiPermissions::AllPermissions()));
host_dispatcher_->InitWithTestSink(&sink()); host_dispatcher_->InitWithTestSink(&sink());
HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get()); HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get());
...@@ -456,7 +448,6 @@ void HostProxyTestHarness::SetUpHarnessWithChannel( ...@@ -456,7 +448,6 @@ void HostProxyTestHarness::SetUpHarnessWithChannel(
host_dispatcher_.reset(new HostDispatcher( host_dispatcher_.reset(new HostDispatcher(
pp_module(), pp_module(),
&MockGetInterface, &MockGetInterface,
status_receiver_.release(),
PpapiPermissions::AllPermissions())); PpapiPermissions::AllPermissions()));
ppapi::Preferences preferences; ppapi::Preferences preferences;
host_dispatcher_->InitHostWithChannel(&delegate_mock_, host_dispatcher_->InitHostWithChannel(&delegate_mock_,
......
...@@ -285,16 +285,12 @@ class HostProxyTestHarness : public ProxyTestHarnessBase { ...@@ -285,16 +285,12 @@ class HostProxyTestHarness : public ProxyTestHarnessBase {
}; };
private: private:
class MockSyncMessageStatusReceiver;
void CreateHostGlobals(); void CreateHostGlobals();
GlobalsConfiguration globals_config_; GlobalsConfiguration globals_config_;
scoped_ptr<ppapi::TestGlobals> host_globals_; scoped_ptr<ppapi::TestGlobals> host_globals_;
scoped_ptr<HostDispatcher> host_dispatcher_; scoped_ptr<HostDispatcher> host_dispatcher_;
DelegateMock delegate_mock_; DelegateMock delegate_mock_;
scoped_ptr<MockSyncMessageStatusReceiver> status_receiver_;
}; };
class HostProxyTest : public HostProxyTestHarness, public testing::Test { class HostProxyTest : public HostProxyTestHarness, public testing::Test {
......
...@@ -279,13 +279,8 @@ std::string TestMessageHandler::TestPostMessageAndAwaitResponse() { ...@@ -279,13 +279,8 @@ std::string TestMessageHandler::TestPostMessageAndAwaitResponse() {
js_code += values_to_test[i]; js_code += values_to_test[i];
js_code += " result: \" + result);\n"; js_code += " result: \" + result);\n";
} }
// TODO(dmichael): Setting a property uses GetInstanceObject, which sends sync
// message, which can get interrupted with message to eval script, etc.
// FINISHED_WAITING message can therefore jump ahead. This test is
// currently carefully crafted to avoid races by doing all the JS in one call.
// That should be fixed before this API goes to stable. See crbug.com/384528
js_code += "plugin.postMessage('FINISHED_TEST');\n";
instance_->EvalScript(js_code); instance_->EvalScript(js_code);
instance_->EvalScript("plugin.postMessage('FINISHED_TEST');\n");
handler.WaitForTestFinishedMessage(); handler.WaitForTestFinishedMessage();
handler.Unregister(); handler.Unregister();
ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy()); ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment