diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index e1c0cc40782b..4b245656bf3f 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -372,6 +372,78 @@ nsPluginCrashedEvent::Run() return NS_OK; } +class nsStopPluginRunnable : public Runnable, public nsITimerCallback +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + nsStopPluginRunnable(nsPluginInstanceOwner* aInstanceOwner, + nsObjectLoadingContent* aContent) + : mInstanceOwner(aInstanceOwner) + , mContent(aContent) + { + NS_ASSERTION(aInstanceOwner, "need an owner"); + NS_ASSERTION(aContent, "need a nsObjectLoadingContent"); + } + + // Runnable + NS_IMETHOD Run() override; + + // nsITimerCallback + NS_IMETHOD Notify(nsITimer* timer) override; + +protected: + virtual ~nsStopPluginRunnable() {} + +private: + nsCOMPtr mTimer; + RefPtr mInstanceOwner; + nsCOMPtr mContent; +}; + +NS_IMPL_ISUPPORTS_INHERITED(nsStopPluginRunnable, Runnable, nsITimerCallback) + +NS_IMETHODIMP +nsStopPluginRunnable::Notify(nsITimer *aTimer) +{ + return Run(); +} + +NS_IMETHODIMP +nsStopPluginRunnable::Run() +{ + // InitWithCallback calls Release before AddRef so we need to hold a + // strong ref on 'this' since we fall through to this scope if it fails. + nsCOMPtr kungFuDeathGrip = this; + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (appShell) { + uint32_t currentLevel = 0; + appShell->GetEventloopNestingLevel(¤tLevel); + if (currentLevel > mInstanceOwner->GetLastEventloopNestingLevel()) { + if (!mTimer) + mTimer = do_CreateInstance("@mozilla.org/timer;1"); + if (mTimer) { + // Fire 100ms timer to try to tear down this plugin as quickly as + // possible once the nesting level comes back down. + nsresult rv = mTimer->InitWithCallback(this, 100, + nsITimer::TYPE_ONE_SHOT); + if (NS_SUCCEEDED(rv)) { + return rv; + } + } + NS_ERROR("Failed to setup a timer to stop the plugin later (at a safe " + "time). Stopping the plugin now, this might crash."); + } + } + + mTimer = nullptr; + + static_cast(mContent.get())-> + DoStopPlugin(mInstanceOwner, false, true); + + return NS_OK; +} + // You can't take the address of bitfield members, so we have two separate // classes for these :-/ @@ -2998,6 +3070,29 @@ nsObjectLoadingContent::GetSrcURI(nsIURI** aURI) return NS_OK; } +static bool +DoDelayedStop(nsPluginInstanceOwner* aInstanceOwner, + nsObjectLoadingContent* aContent, + bool aDelayedStop) +{ + // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524), + // XStandard (bug 430219), CMISS Zinc (bug 429604). + if (aDelayedStop +#if !(defined XP_WIN || defined MOZ_X11) + && !aInstanceOwner->MatchPluginName("QuickTime") + && !aInstanceOwner->MatchPluginName("Flip4Mac") + && !aInstanceOwner->MatchPluginName("XStandard plugin") + && !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin") +#endif + ) { + nsCOMPtr evt = + new nsStopPluginRunnable(aInstanceOwner, aContent); + NS_DispatchToCurrentThread(evt); + return true; + } + return false; +} + void nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) { EventStates oldState = ObjectState(); @@ -3061,13 +3156,16 @@ nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) { } void -nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner) +nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner, + bool aDelayedStop, + bool aForcedReentry) { // DoStopPlugin can process events -- There may be pending // CheckPluginStopEvent events which can drop in underneath us and destroy the - // instance we are about to destroy. We prevent that with the mIsStopping - // flag. - if (mIsStopping) { + // instance we are about to destroy. We prevent that with the mPluginStopping + // flag. (aForcedReentry is only true from the callback of an earlier delayed + // stop) + if (mIsStopping && !aForcedReentry) { return; } mIsStopping = true; @@ -3076,6 +3174,10 @@ nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner) RefPtr inst; aInstanceOwner->GetInstance(getter_AddRefs(inst)); if (inst) { + if (DoDelayedStop(aInstanceOwner, this, aDelayedStop)) { + return; + } + #if defined(XP_MACOSX) aInstanceOwner->HidePluginWindow(); #endif @@ -3128,11 +3230,27 @@ nsObjectLoadingContent::StopPluginInstance() // the instance owner until the plugin is stopped. mInstanceOwner->SetFrame(nullptr); + bool delayedStop = false; +#ifdef XP_WIN + // Force delayed stop for Real plugin only; see bug 420886, 426852. + RefPtr inst; + mInstanceOwner->GetInstance(getter_AddRefs(inst)); + if (inst) { + const char* mime = nullptr; + if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) { + if (nsPluginHost::GetSpecialType(nsDependentCString(mime)) == + nsPluginHost::eSpecialType_RealPlayer) { + delayedStop = true; + } + } + } +#endif + RefPtr ownerGrip(mInstanceOwner); mInstanceOwner = nullptr; // This can/will re-enter - DoStopPlugin(ownerGrip); + DoStopPlugin(ownerGrip, delayedStop); return NS_OK; } diff --git a/dom/base/nsObjectLoadingContent.h b/dom/base/nsObjectLoadingContent.h index 491302050798..cfd3bf381b84 100644 --- a/dom/base/nsObjectLoadingContent.h +++ b/dom/base/nsObjectLoadingContent.h @@ -331,7 +331,8 @@ class nsObjectLoadingContent : public nsImageLoadingContent void CreateStaticClone(nsObjectLoadingContent* aDest) const; - void DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner); + void DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner, bool aDelayedStop, + bool aForcedReentry = false); nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 92488144cf9b..b4b56866818b 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -1851,6 +1851,11 @@ nsPluginHost::GetSpecialType(const nsACString & aMIMEType) return eSpecialType_Silverlight; } + if (aMIMEType.LowerCaseEqualsASCII("audio/x-pn-realaudio-plugin")) { + NS_WARNING("You are loading RealPlayer"); + return eSpecialType_RealPlayer; + } + if (aMIMEType.LowerCaseEqualsASCII("application/vnd.unity")) { return eSpecialType_Unity; } diff --git a/dom/plugins/base/nsPluginHost.h b/dom/plugins/base/nsPluginHost.h index cfa46139ada3..8eb17218059f 100644 --- a/dom/plugins/base/nsPluginHost.h +++ b/dom/plugins/base/nsPluginHost.h @@ -205,6 +205,8 @@ public: // Some IPC quirks eSpecialType_Silverlight, // Native widget quirks + eSpecialType_RealPlayer, + // Native widget quirks eSpecialType_Unity }; static SpecialType GetSpecialType(const nsACString & aMIMEType); diff --git a/dom/plugins/base/nsPluginNativeWindowWin.cpp b/dom/plugins/base/nsPluginNativeWindowWin.cpp index 16e4ef6c53e0..91f96578aa05 100644 --- a/dom/plugins/base/nsPluginNativeWindowWin.cpp +++ b/dom/plugins/base/nsPluginNativeWindowWin.cpp @@ -129,7 +129,9 @@ public: nsPluginHost::SpecialType mPluginType; }; +static bool sInMessageDispatch = false; static bool sInPreviousMessageDispatch = false; +static UINT sLastMsg = 0; static bool ProcessFlashMessageDelayed(nsPluginNativeWindowWin * aWin, nsNPAPIPluginInstance * aInst, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -199,6 +201,16 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam RefPtr inst; win->GetPluginInstance(inst); + // Real may go into a state where it recursivly dispatches the same event + // when subclassed. If this is Real, lets examine the event and drop it + // on the floor if we get into this recursive situation. See bug 192914. + if (win->mPluginType == nsPluginHost::eSpecialType_RealPlayer) { + if (sInMessageDispatch && msg == sLastMsg) + return true; + // Cache the last message sent + sLastMsg = msg; + } + bool enablePopups = false; // Activate/deactivate mouse capture on the plugin widget @@ -268,6 +280,10 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam case WM_SETFOCUS: case WM_KILLFOCUS: { + // RealPlayer can crash, don't process the message for those, + // see bug 328675. + if (win->mPluginType == nsPluginHost::eSpecialType_RealPlayer && msg == sLastMsg) + return TRUE; // Make sure setfocus and killfocus get through to the widget procedure // even if they are eaten by the plugin. Also make sure we aren't calling // recursively. @@ -297,6 +313,7 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam } } + sInMessageDispatch = true; LRESULT res; WNDPROC proc = (WNDPROC)win->GetWindowProc(); if (PluginWndProc == proc) { @@ -306,6 +323,7 @@ static LRESULT CALLBACK PluginWndProcInternal(HWND hWnd, UINT msg, WPARAM wParam } else { res = CallWindowProc(proc, hWnd, msg, wParam, lParam); } + sInMessageDispatch = false; if (inst) { // Popups are enabled (were enabled before the call to