diff --git a/dom/plugins/COMMessageFilter.cpp b/dom/plugins/COMMessageFilter.cpp new file mode 100644 index 00000000000..e38cd9f0b78 --- /dev/null +++ b/dom/plugins/COMMessageFilter.cpp @@ -0,0 +1,114 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Firefox. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "COMMessageFilter.h" +#include "base/message_loop.h" +#include "mozilla/plugins/PluginModuleChild.h" + +#include + +namespace mozilla { +namespace plugins { + +HRESULT +COMMessageFilter::QueryInterface(REFIID riid, void** ppv) +{ + if (riid == IID_IUnknown || riid == IID_IMessageFilter) { + *ppv = static_cast(this); + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +DWORD COMMessageFilter::AddRef() +{ + ++mRefCnt; + return mRefCnt; +} + +DWORD COMMessageFilter::Release() +{ + DWORD r = --mRefCnt; + if (0 == r) + delete this; + return r; +} + +DWORD +COMMessageFilter::HandleInComingCall(DWORD dwCallType, + HTASK htaskCaller, + DWORD dwTickCount, + LPINTERFACEINFO lpInterfaceInfo) +{ + if (mPreviousFilter) + return mPreviousFilter->HandleInComingCall(dwCallType, htaskCaller, + dwTickCount, lpInterfaceInfo); + return SERVERCALL_ISHANDLED; +} + +DWORD +COMMessageFilter::RetryRejectedCall(HTASK htaskCallee, + DWORD dwTickCount, + DWORD dwRejectType) +{ + if (mPreviousFilter) + return mPreviousFilter->RetryRejectedCall(htaskCallee, dwTickCount, + dwRejectType); + return -1; +} + +DWORD +COMMessageFilter::MessagePending(HTASK htaskCallee, + DWORD dwTickCount, + DWORD dwPendingType) +{ + mPlugin->FlushPendingRPCQueue(); + if (mPreviousFilter) + return mPreviousFilter->MessagePending(htaskCallee, dwTickCount, + dwPendingType); + return PENDINGMSG_WAITNOPROCESS; +} + +void +COMMessageFilter::Initialize(PluginModuleChild* module) +{ + nsRefPtr f = new COMMessageFilter(module); + ::CoRegisterMessageFilter(f, getter_AddRefs(f->mPreviousFilter)); +} + +} // namespace plugins +} // namespace mozilla diff --git a/dom/plugins/COMMessageFilter.h b/dom/plugins/COMMessageFilter.h new file mode 100644 index 00000000000..c93f3c182ed --- /dev/null +++ b/dom/plugins/COMMessageFilter.h @@ -0,0 +1,82 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Firefox. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_COMMessageFilter_h +#define mozilla_plugins_COMMessageFilter_h + +#include +#include "nsISupportsImpl.h" +#include "nsAutoPtr.h" + +namespace mozilla { +namespace plugins { + +class PluginModuleChild; + +class COMMessageFilter : public IMessageFilter +{ +public: + static void Initialize(PluginModuleChild* plugin); + + COMMessageFilter(PluginModuleChild* plugin) + : mPlugin(plugin) + { } + + HRESULT WINAPI QueryInterface(REFIID riid, void** ppv); + DWORD WINAPI AddRef(); + DWORD WINAPI Release(); + + DWORD WINAPI HandleInComingCall(DWORD dwCallType, + HTASK htaskCaller, + DWORD dwTickCount, + LPINTERFACEINFO lpInterfaceInfo); + DWORD WINAPI RetryRejectedCall(HTASK htaskCallee, + DWORD dwTickCount, + DWORD dwRejectType); + DWORD WINAPI MessagePending(HTASK htaskCallee, + DWORD dwTickCount, + DWORD dwPendingType); + +private: + nsAutoRefCnt mRefCnt; + PluginModuleChild* mPlugin; + nsRefPtr mPreviousFilter; +}; + +} // namespace plugins +} // namespace mozilla + +#endif // COMMessageFilter_h diff --git a/dom/plugins/Makefile.in b/dom/plugins/Makefile.in index d7dc08461ac..b0ff221a9b2 100644 --- a/dom/plugins/Makefile.in +++ b/dom/plugins/Makefile.in @@ -113,6 +113,10 @@ CPPSRCS = \ PluginStreamParent.cpp \ $(NULL) +ifeq (WINNT,$(OS_ARCH)) +CPPSRCS += COMMessageFilter.cpp +endif + LOCAL_INCLUDES = \ -I$(topsrcdir)/modules/plugin/base/public/ \ -I$(topsrcdir)/modules/plugin/base/src/ \ diff --git a/dom/plugins/PluginModuleChild.cpp b/dom/plugins/PluginModuleChild.cpp index 956b55d0e99..866980bd701 100644 --- a/dom/plugins/PluginModuleChild.cpp +++ b/dom/plugins/PluginModuleChild.cpp @@ -63,6 +63,10 @@ #include "nsNPAPIPlugin.h" +#ifdef XP_WIN +#include "COMMessageFilter.h" +#endif + using namespace mozilla::plugins; #if defined(XP_WIN) @@ -124,6 +128,8 @@ PluginModuleChild::Init(const std::string& aPluginFilename, { PLUGIN_LOG_DEBUG_METHOD; + COMMessageFilter::Initialize(this); + NS_ASSERTION(aChannel, "need a channel"); if (!mObjectMap.Init()) { diff --git a/ipc/chromium/src/base/message_pump_win.cc b/ipc/chromium/src/base/message_pump_win.cc index 737676c3734..caf36ff31ed 100644 --- a/ipc/chromium/src/base/message_pump_win.cc +++ b/ipc/chromium/src/base/message_pump_win.cc @@ -97,6 +97,10 @@ void MessagePumpForUI::ScheduleWork() { // Make sure the MessagePump does some work for us. PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast(this), 0); + + // In order to wake up any cross-process COM calls which may currently be + // pending on the main thread, we also have to post a UI message. + PostMessage(message_hwnd_, WM_NULL, NULL, 0); } void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { diff --git a/ipc/glue/RPCChannel.cpp b/ipc/glue/RPCChannel.cpp index 789b4e4cbe0..83ee7d67fea 100644 --- a/ipc/glue/RPCChannel.cpp +++ b/ipc/glue/RPCChannel.cpp @@ -315,14 +315,14 @@ RPCChannel::Call(Message* msg, Message* reply) return true; } -void +bool RPCChannel::MaybeProcessDeferredIncall() { AssertWorkerThread(); mMutex.AssertCurrentThreadOwns(); if (mDeferred.empty()) - return; + return false; size_t stackDepth = StackDepth(); @@ -331,7 +331,7 @@ RPCChannel::MaybeProcessDeferredIncall() "fatal logic error"); if (mDeferred.top().rpc_remote_stack_depth_guess() < stackDepth) - return; + return false; // time to process this message Message call = mDeferred.top(); @@ -348,6 +348,7 @@ RPCChannel::MaybeProcessDeferredIncall() CxxStackFrame f(*this, IN_MESSAGE, &call); Incall(call, stackDepth); + return true; } void @@ -371,6 +372,28 @@ RPCChannel::EnqueuePendingMessages() } void +RPCChannel::FlushPendingRPCQueue() +{ + AssertWorkerThread(); + mMutex.AssertNotCurrentThreadOwns(); + + { + MutexAutoLock lock(mMutex); + + if (mDeferred.empty()) { + if (mPending.empty()) + return; + + const Message& last = mPending.back(); + if (!last.is_rpc() || last.is_reply()) + return; + } + } + + while (OnMaybeDequeueOne()); +} + +bool RPCChannel::OnMaybeDequeueOne() { // XXX performance tuning knob: could process all or k pending @@ -385,14 +408,14 @@ RPCChannel::OnMaybeDequeueOne() if (!Connected()) { ReportConnectionError("RPCChannel"); - return; + return false; } if (!mDeferred.empty()) return MaybeProcessDeferredIncall(); if (mPending.empty()) - return; + return false; recvd = mPending.front(); mPending.pop(); @@ -402,17 +425,19 @@ RPCChannel::OnMaybeDequeueOne() // We probably just received a reply in a nested loop for an // RPC call sent before entering that loop. mOutOfTurnReplies[recvd.seqno()] = recvd; - return; + return false; } CxxStackFrame f(*this, IN_MESSAGE, &recvd); if (recvd.is_rpc()) - return Incall(recvd, 0); + Incall(recvd, 0); else if (recvd.is_sync()) - return SyncChannel::OnDispatchMessage(recvd); + SyncChannel::OnDispatchMessage(recvd); else - return AsyncChannel::OnDispatchMessage(recvd); + AsyncChannel::OnDispatchMessage(recvd); + + return true; } void diff --git a/ipc/glue/RPCChannel.h b/ipc/glue/RPCChannel.h index 852ff2c605e..a8dbb2aa4bf 100644 --- a/ipc/glue/RPCChannel.h +++ b/ipc/glue/RPCChannel.h @@ -166,6 +166,14 @@ public: NS_OVERRIDE virtual void OnChannelError(); + /** + * If there is a pending RPC message, process all pending messages. + * + * @note This method is used on Windows when we detect that an outbound + * OLE RPC call is being made to unblock the parent. + */ + void FlushPendingRPCQueue(); + #ifdef OS_WIN void ProcessNativeEventsInRPCCall(); @@ -188,10 +196,15 @@ protected: bool EventOccurred() const; - void MaybeProcessDeferredIncall(); + bool MaybeProcessDeferredIncall(); void EnqueuePendingMessages(); - void OnMaybeDequeueOne(); + /** + * Process one deferred or pending message. + * @return true if a message was processed + */ + bool OnMaybeDequeueOne(); + void Incall(const Message& call, size_t stackDepth); void DispatchIncall(const Message& call); diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index edf312553d4..a4161837eb9 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -2978,9 +2978,14 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): onstack.addstmt(StmtReturn(ExprCall( ExprSelect(p.channelVar(), '.', p.onCxxStackVar().name)))) + # void ProcessIncomingRacingRPCCall + processincoming = MethodDefn( + MethodDecl('FlushPendingRPCQueue', ret=Type.VOID)) + processincoming.addstmt(StmtExpr(ExprCall(ExprSelect(_actorChannel(ExprVar.THIS), '.', 'FlushPendingRPCQueue')))) + self.cls.addstmts([ onentered, onexited, onenteredcall, onexitedcall, - onstack, Whitespace.NL ]) + onstack, processincoming, Whitespace.NL ]) # OnChannelClose() onclose = MethodDefn(MethodDecl('OnChannelClose'))