From bf20c145265baf22c4a5d82f9b57094af5942b11 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 8 Feb 2011 18:44:14 -0600 Subject: [PATCH] Bug 626602, part 9: When possible, copy from a background to an opaque surface and have transparent plugins draw directly on the copied background, instead of doing alpha recovery or hoping plugins give us alpha values. r=bsmedberg,karlt sr=roc --- dom/plugins/PPluginBackgroundDestroyer.ipdl | 70 +++++ dom/plugins/PPluginInstance.ipdl | 12 + dom/plugins/PluginBackgroundDestroyer.h | 108 +++++++ dom/plugins/PluginInstanceChild.cpp | 276 +++++++++++++----- dom/plugins/PluginInstanceChild.h | 26 +- dom/plugins/PluginInstanceParent.cpp | 185 ++++++++++++ dom/plugins/PluginInstanceParent.h | 33 ++- dom/plugins/PluginModuleChild.cpp | 1 - dom/plugins/PluginModuleChild.h | 3 - dom/plugins/ipdl.mk | 1 + .../plugin/test/testplugin/nptest_gtk2.cpp | 8 +- 11 files changed, 643 insertions(+), 80 deletions(-) create mode 100644 dom/plugins/PPluginBackgroundDestroyer.ipdl create mode 100644 dom/plugins/PluginBackgroundDestroyer.h diff --git a/dom/plugins/PPluginBackgroundDestroyer.ipdl b/dom/plugins/PPluginBackgroundDestroyer.ipdl new file mode 100644 index 000000000000..b15ad545e624 --- /dev/null +++ b/dom/plugins/PPluginBackgroundDestroyer.ipdl @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=8 et : + */ +/* ***** 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 Code. + * + * 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): + * Chris Jones + * + * 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 protocol PPluginInstance; + +namespace mozilla { +namespace plugins { + +/** + * This protocol exists to allow us to correctly destroy background + * surfaces. The browser owns the surfaces, but shares a "reference" + * with the plugin. The browser needs to notify the plugin when the + * background is going to be destroyed, but it can't rely on the + * plugin to destroy it because the plugin may crash at any time. So + * the plugin instance relinquishes destruction of the its old + * background to actors of this protocol, which can deal with crashy + * corner cases more easily than the instance. + */ +protocol PPluginBackgroundDestroyer { + manager PPluginInstance; + + // The ctor message for this protocol serves double-duty as + // notification that that the background is stale. + +parent: + __delete__(); + +state DESTROYING: + recv __delete__; +}; + +} // namespace plugins +} // namespace mozilla diff --git a/dom/plugins/PPluginInstance.ipdl b/dom/plugins/PPluginInstance.ipdl index 1daa4623c9d6..b5015cd71865 100644 --- a/dom/plugins/PPluginInstance.ipdl +++ b/dom/plugins/PPluginInstance.ipdl @@ -37,6 +37,7 @@ * * ***** END LICENSE BLOCK ***** */ +include protocol PPluginBackgroundDestroyer; include protocol PPluginModule; include protocol PPluginScriptableObject; include protocol PBrowserStream; @@ -57,6 +58,7 @@ using mozilla::gfxSurfaceType; using gfxIntSize; using mozilla::null_t; using mozilla::plugins::WindowsSharedMemoryHandle; +using nsIntRect; namespace mozilla { namespace plugins { @@ -83,6 +85,7 @@ rpc protocol PPluginInstance { manager PPluginModule; + manages PPluginBackgroundDestroyer; manages PPluginScriptableObject; manages PBrowserStream; manages PPluginStream; @@ -126,6 +129,13 @@ child: // with type equals to surfaceType async AsyncSetWindow(gfxSurfaceType surfaceType, NPRemoteWindow window); + // There is now an opaque background behind this instance (or the + // background was updated). The changed area is |rect|. The + // browser owns the background surface, and it's read-only from + // within the plugin process. |background| is either null_t to + // refer to the existing background or a fresh descriptor. + async UpdateBackground(SurfaceDescriptor background, nsIntRect rect); + rpc NPP_Destroy() returns (NPError rv); @@ -236,6 +246,8 @@ parent: child: rpc SetPluginFocus(); rpc UpdateWindow(); + + async PPluginBackgroundDestroyer(); }; } // namespace plugins diff --git a/dom/plugins/PluginBackgroundDestroyer.h b/dom/plugins/PluginBackgroundDestroyer.h new file mode 100644 index 000000000000..9b3b64f1db47 --- /dev/null +++ b/dom/plugins/PluginBackgroundDestroyer.h @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=8 et : + */ +/* ***** 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 Code. + * + * 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): + * Chris Jones + * + * 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 dom_plugins_PluginBackgroundDestroyer +#define dom_plugins_PluginBackgroundDestroyer + +#include "mozilla/plugins/PPluginBackgroundDestroyerChild.h" +#include "mozilla/plugins/PPluginBackgroundDestroyerParent.h" + +#include "gfxASurface.h" +#include "gfxSharedImageSurface.h" + +namespace mozilla { +namespace plugins { + +/** + * When instances of this class are destroyed, the old background goes + * along with them, completing the destruction process (whether or not + * the plugin stayed alive long enough to ack). + */ +class PluginBackgroundDestroyerParent : public PPluginBackgroundDestroyerParent { +public: + PluginBackgroundDestroyerParent(gfxASurface* aDyingBackground) + : mDyingBackground(aDyingBackground) + { } + + virtual ~PluginBackgroundDestroyerParent() { } + +private: + NS_OVERRIDE + virtual void ActorDestroy(ActorDestroyReason why) + { + switch(why) { + case Deletion: + case AncestorDeletion: + if (gfxSharedImageSurface::IsSharedImage(mDyingBackground)) { + gfxSharedImageSurface* s = + static_cast(mDyingBackground.get()); + DeallocShmem(s->GetShmem()); + } + break; + default: + // We're shutting down or crashed, let automatic cleanup + // take care of our shmem, if we have one. + break; + } + } + + nsRefPtr mDyingBackground; +}; + +/** + * This class exists solely to instruct its instance to release its + * current background, a new one may be coming. + */ +class PluginBackgroundDestroyerChild : public PPluginBackgroundDestroyerChild { +public: + PluginBackgroundDestroyerChild() { } + virtual ~PluginBackgroundDestroyerChild() { } + +private: + // Implementing this for good hygiene. + NS_OVERRIDE + virtual void ActorDestroy(ActorDestroyReason why) + { } +}; + +} // namespace plugins +} // namespace mozilla + +#endif // dom_plugins_PluginBackgroundDestroyer diff --git a/dom/plugins/PluginInstanceChild.cpp b/dom/plugins/PluginInstanceChild.cpp index e8c3c7dcbe9d..f6553e190c40 100644 --- a/dom/plugins/PluginInstanceChild.cpp +++ b/dom/plugins/PluginInstanceChild.cpp @@ -37,6 +37,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "PluginBackgroundDestroyer.h" #include "PluginInstanceChild.h" #include "PluginModuleChild.h" #include "BrowserStreamChild.h" @@ -166,7 +167,6 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface) #endif // OS_WIN #if defined(OS_WIN) InitPopupMenuHook(); - HookSystemParametersInfo(); #endif // OS_WIN #ifdef MOZ_X11 // Maemo flash can render plugin with any provided rectangle and not require this quirk. @@ -924,6 +924,11 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow) } #endif + PLUGIN_LOG_DEBUG( + ("[InstanceChild][%p] Answer_SetWindow w=, clip=", + this, mWindow.x, mWindow.y, mWindow.width, mWindow.height, + mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom)); + if (mPluginIface->setwindow) (void) mPluginIface->setwindow(&mData, &mWindow); @@ -1228,47 +1233,6 @@ PluginInstanceChild::PluginWindowProc(HWND hWnd, return res; } -/* system parameters info hook for flash */ - -typedef BOOL (WINAPI *User32SystemParametersInfoW)(UINT uiAction, - UINT uiParam, - PVOID pvParam, - UINT fWinIni); - -static User32SystemParametersInfoW sUser32SystemParametersInfoWStub = NULL; - -static BOOL WINAPI User32SystemParametersInfoHook(UINT uiAction, - UINT uiParam, - PVOID pvParam, - UINT fWinIni) -{ - if (!sUser32SystemParametersInfoWStub) { - NS_NOTREACHED("sUser32SystemParametersInfoWStub not set??"); - return FALSE; - } - - // Tell them cleartype is disabled, so they don't mess with - // the alpha channel in our buffers. - if (uiAction == SPI_GETFONTSMOOTHINGTYPE && pvParam) { - *((UINT*)(pvParam)) = FE_FONTSMOOTHINGSTANDARD; - return TRUE; - } - - return sUser32SystemParametersInfoWStub(uiAction, uiParam, pvParam, fWinIni); -} - -void -PluginInstanceChild::HookSystemParametersInfo() -{ - if (!(GetQuirks() & PluginModuleChild::QUIRK_FLASH_MASK_CLEARTYPE_SETTINGS)) - return; - if (sUser32SystemParametersInfoWStub) - return; - sUser32Intercept.Init("gdi32.dll"); - sUser32Intercept.AddHook("SystemParametersInfoW", User32SystemParametersInfoHook, - (void**) &sUser32SystemParametersInfoWStub); -} - /* set window long ptr hook for flash */ /* @@ -2287,6 +2251,10 @@ PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType, const NPRemoteWindow& aWindow, bool aIsAsync) { + PLUGIN_LOG_DEBUG( + ("[InstanceChild][%p] AsyncSetWindow to ", + this, aWindow.x, aWindow.y, aWindow.width, aWindow.height)); + AssertPluginThread(); NS_ASSERTION(!aWindow.window, "Remote window should be null."); NS_ASSERTION(!mPendingPluginCall, "Can't do SetWindow during plugin call!"); @@ -2300,6 +2268,10 @@ PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType, mWindow.window = NULL; if (mWindow.width != aWindow.width || mWindow.height != aWindow.height) { + // We weakly assume here that the SetWindow arrives before the + // next UpdateBackground, for the new window size, if we were + // going to get one. + mBackground = nsnull; ClearCurrentSurface(); mAccumulatedInvalidRect = nsIntRect(0, 0, aWindow.width, aWindow.height); } @@ -2347,9 +2319,11 @@ PluginInstanceChild::CreateOptSurface(void) NS_ASSERTION(!mCurrentSurface, "mCurrentSurfaceActor can get out of sync."); nsRefPtr retsurf; + // Use an opaque surface unless we're transparent and *don't* have + // a background to source from. gfxASurface::gfxImageFormat format = - mIsTransparent ? gfxASurface::ImageFormatARGB32 : - gfxASurface::ImageFormatRGB24; + (mIsTransparent && !mBackground) ? gfxASurface::ImageFormatARGB32 : + gfxASurface::ImageFormatRGB24; #if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6) // On Maemo 5, we must send the Visibility event to activate the plugin @@ -2387,10 +2361,12 @@ PluginInstanceChild::CreateOptSurface(void) #ifdef XP_WIN if (mSurfaceType == gfxASurface::SurfaceTypeWin32 || mSurfaceType == gfxASurface::SurfaceTypeD2D) { + bool willHaveTransparentPixels = mIsTransparent && !mBackground; SharedDIBSurface* s = new SharedDIBSurface(); if (!s->Create(reinterpret_cast(mWindow.window), - mWindow.width, mWindow.height, mIsTransparent)) + mWindow.width, mWindow.height, + willHaveTransparentPixels)) return false; mCurrentSurface = s; @@ -2471,6 +2447,8 @@ PluginInstanceChild::MaybeCreatePlatformHelperSurface(void) return false; } } +#elif defined(XP_WIN) + mDoAlphaExtraction = mIsTransparent && !mBackground; #endif return true; @@ -2529,7 +2507,7 @@ PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow) mWindow.window = nsnull; mWsInfo.depth = gfxUtils::ImageFormatToDepth(img->Format()); mWsInfo.colormap = 0; - needWindowUpdate = PR_TRUE; + needWindowUpdate = true; } } #endif // MAEMO @@ -2595,6 +2573,11 @@ PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow) } #endif + PLUGIN_LOG_DEBUG( + ("[InstanceChild][%p] UpdateWindow w=, clip=", + this, mWindow.x, mWindow.y, mWindow.width, mWindow.height, + mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom)); + if (mPluginIface->setwindow) { mPluginIface->setwindow(&mData, &mWindow); } @@ -2604,12 +2587,6 @@ void PluginInstanceChild::PaintRectToPlatformSurface(const nsIntRect& aRect, gfxASurface* aSurface) { - bool temporarilyMakeVisible = !IsVisible() && !mHasPainted; - if (temporarilyMakeVisible) { - mWindow.clipRect.right = mWindow.width; - mWindow.clipRect.bottom = mWindow.height; - } - UpdateWindowAttributes(); #ifdef MOZ_X11 @@ -2698,14 +2675,6 @@ PluginInstanceChild::PaintRectToPlatformSurface(const nsIntRect& aRect, #else NS_RUNTIMEABORT("Surface type not implemented."); #endif - - if (temporarilyMakeVisible) { - mWindow.clipRect.right = mWindow.clipRect.bottom = 0; - - if (mPluginIface->setwindow) { - mPluginIface->setwindow(&mData, &mWindow); - } - } } void @@ -2735,7 +2704,7 @@ PluginInstanceChild::PaintRectToSurface(const nsIntRect& aRect, } #endif - if (mIsTransparent) { + if (aColor.a > 0.0) { // Clear surface content for transparent rendering nsRefPtr ctx = new gfxContext(renderSurface); ctx->SetColor(aColor); @@ -2858,6 +2827,14 @@ PluginInstanceChild::PaintRectWithAlphaExtraction(const nsIntRect& aRect, } } +bool +PluginInstanceChild::CanPaintOnBackground() +{ + return (mBackground && + mCurrentSurface && + mCurrentSurface->GetSize() == mBackground->GetSize()); +} + bool PluginInstanceChild::ShowPluginFrame() { @@ -2872,11 +2849,20 @@ PluginInstanceChild::ShowPluginFrame() return false; } - // Make expose rect not bigger than clip rect - mAccumulatedInvalidRect.IntersectRect(mAccumulatedInvalidRect, - nsIntRect(mWindow.clipRect.left, mWindow.clipRect.top, - mWindow.clipRect.right - mWindow.clipRect.left, - mWindow.clipRect.bottom - mWindow.clipRect.top)); + bool temporarilyMakeVisible = !IsVisible() && !mHasPainted; + if (temporarilyMakeVisible) { + mWindow.clipRect.right = mWindow.width; + mWindow.clipRect.bottom = mWindow.height; + } else if (!IsVisible()) { + // If we're not visible, don't bother painting a <0,0,0,0> + // rect. If we're eventually made visible, the visibility + // change will invalidate our window. + return true; + } + + NS_ASSERTION(mWindow.width == (mWindow.clipRect.right - mWindow.clipRect.left) && + mWindow.height == (mWindow.clipRect.bottom - mWindow.clipRect.top), + "Clip rect should be same size as window when using layers"); // Clear accRect here to be able to pass // test_invalidate_during_plugin_paint test @@ -2884,17 +2870,54 @@ PluginInstanceChild::ShowPluginFrame() mAccumulatedInvalidRect.Empty(); if (!ReadbackDifferenceRect(rect)) { - // Just repaint whole plugin, because we cannot read back from Shmem which is owned by another process + // We couldn't read back the pixels that differ between the + // current surface and last, so we have to invalidate the + // entire window. rect.SetRect(0, 0, mWindow.width, mWindow.height); } - if (mDoAlphaExtraction) { + bool haveTransparentPixels = + gfxASurface::CONTENT_COLOR_ALPHA == mCurrentSurface->GetContentType(); + PLUGIN_LOG_DEBUG( + ("[InstanceChild][%p] Painting%s ", + this, haveTransparentPixels ? " with alpha" : "", + rect.x, rect.y, rect.width, rect.height)); + + if (CanPaintOnBackground()) { + PLUGIN_LOG_DEBUG((" (on background)")); + // Source the background pixels ... + { + nsRefPtr ctx = new gfxContext(mCurrentSurface); + ctx->SetSource(mBackground); + ctx->SetOperator(gfxContext::OPERATOR_SOURCE); + ctx->Rectangle(gfxRect(rect.x, rect.y, rect.width, rect.height)); + ctx->Fill(); + } + // ... and hand off to the plugin + // BEWARE: mBackground may die during this call + PaintRectToSurface(rect, mCurrentSurface, gfxRGBA(0.0, 0.0, 0.0, 0.0)); + } else if (mDoAlphaExtraction) { + PLUGIN_LOG_DEBUG((" (with alpha recovery)")); PaintRectWithAlphaExtraction(rect, mCurrentSurface); } else { + PLUGIN_LOG_DEBUG((" (onto opaque surface)")); PaintRectToSurface(rect, mCurrentSurface, gfxRGBA(0.0, 0.0, 0.0, 0.0)); } mHasPainted = true; + if (temporarilyMakeVisible) { + mWindow.clipRect.right = mWindow.clipRect.bottom = 0; + + PLUGIN_LOG_DEBUG( + ("[InstanceChild][%p] Undoing temporary clipping w=, clip=", + this, mWindow.x, mWindow.y, mWindow.width, mWindow.height, + mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom)); + + if (mPluginIface->setwindow) { + mPluginIface->setwindow(&mData, &mWindow); + } + } + NPRect r = { (uint16_t)rect.y, (uint16_t)rect.x, (uint16_t)rect.YMost(), (uint16_t)rect.XMost() }; SurfaceDescriptor currSurf; @@ -2918,7 +2941,7 @@ PluginInstanceChild::ShowPluginFrame() mCurrentSurfaceActor = SendPPluginSurfaceConstructor(handle, mCurrentSurface->GetSize(), - mIsTransparent); + haveTransparentPixels); } currSurf = mCurrentSurfaceActor; s->Flush(); @@ -2962,9 +2985,17 @@ PluginInstanceChild::ReadbackDifferenceRect(const nsIntRect& rect) return false; #endif + if (mCurrentSurface->GetContentType() != mBackSurface->GetContentType()) + return false; + if (mSurfaceDifferenceRect.IsEmpty()) return true; + PLUGIN_LOG_DEBUG( + ("[InstanceChild][%p] Reading back part of ", + this, mSurfaceDifferenceRect.x, mSurfaceDifferenceRect.y, + mSurfaceDifferenceRect.width, mSurfaceDifferenceRect.height)); + // Read back previous content nsRefPtr ctx = new gfxContext(mCurrentSurface); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); @@ -3041,6 +3072,110 @@ PluginInstanceChild::InvalidateRect(NPRect* aInvalidRect) SendNPN_InvalidateRect(*aInvalidRect); } +bool +PluginInstanceChild::RecvUpdateBackground(const SurfaceDescriptor& aBackground, + const nsIntRect& aRect) +{ + NS_ABORT_IF_FALSE(mIsTransparent, "Only transparent plugins use backgrounds"); + + if (SurfaceDescriptor::Tnull_t == aBackground.type()) { + // null_t here means "use existing background". We're + // notified when we lose our background through + // PPluginBackgroundDestroyer. We might have already dropped + // our background from an ill-timed SetWindow, though. + if (!mBackground) { + return true; + } + } else { + NS_ABORT_IF_FALSE(!mBackground, "Shouldn't have background here"); + + // Now that we have a new background, our choice of surface + // format and/or size should have changed. + ClearCurrentSurface(); + + // XXX refactor me + switch (aBackground.type()) { +#ifdef MOZ_X11 + case SurfaceDescriptor::TSurfaceDescriptorX11: { + SurfaceDescriptorX11 xdesc = aBackground.get_SurfaceDescriptorX11(); + XRenderPictFormat pf; + pf.id = xdesc.xrenderPictID(); + XRenderPictFormat *incFormat = + XRenderFindFormat(DefaultXDisplay(), PictFormatID, &pf, 0); + mBackground = + new gfxXlibSurface(DefaultScreenOfDisplay(DefaultXDisplay()), + xdesc.XID(), incFormat, xdesc.size()); + break; + } +#endif + case SurfaceDescriptor::TShmem: { + mBackground = gfxSharedImageSurface::Open(aBackground.get_Shmem()); + break; + } + default: + NS_RUNTIMEABORT("Unexpected background surface descriptor"); + } + } + + if (!mBackground) { + return false; + } + + // XXX refactor me + mAccumulatedInvalidRect.UnionRect(aRect, mAccumulatedInvalidRect); + + // The browser is limping along with a stale copy of our pixels. + // Try to repaint ASAP. + if (!ShowPluginFrame()) { + NS_WARNING("Couldn't immediately repaint plugin instance"); + AsyncShowPluginFrame(); + } + + return true; +} + +PPluginBackgroundDestroyerChild* +PluginInstanceChild::AllocPPluginBackgroundDestroyer() +{ + return new PluginBackgroundDestroyerChild(); +} + +bool +PluginInstanceChild::RecvPPluginBackgroundDestroyerConstructor( + PPluginBackgroundDestroyerChild* aActor) +{ + // Our background changed, so we have to invalidate the area + // painted with the old background. If the background was + // destroyed because we have a new background, then we expect to + // be notified of that "soon", before processing the asynchronous + // invalidation here. If we're *not* getting a new background, + // our current front surface is stale and we want to repaint + // "soon" so that we can hand the browser back a surface with + // alpha values. (We should be notified of that invalidation soon + // too, but we don't assume that here.) + if (mBackground) { + gfxIntSize bgsize = mBackground->GetSize(); + mAccumulatedInvalidRect.UnionRect( + nsIntRect(0, 0, bgsize.width, bgsize.height), mAccumulatedInvalidRect); + AsyncShowPluginFrame(); + + // NB: we don't have to XSync here because only ShowPluginFrame() + // uses mBackground, and it always XSyncs after finishing. + mBackground = nsnull; + ClearCurrentSurface(); + } + + return PPluginBackgroundDestroyerChild::Send__delete__(aActor); +} + +bool +PluginInstanceChild::DeallocPPluginBackgroundDestroyer( + PPluginBackgroundDestroyerChild* aActor) +{ + delete aActor; + return true; +} + uint32_t PluginInstanceChild::ScheduleTimer(uint32_t interval, bool repeat, TimerFunc func) @@ -3130,7 +3265,8 @@ PluginInstanceChild::SwapSurfaces() // Outdated back surface... not usable anymore due to changed plugin size. // Dropping obsolete surface if (mCurrentSurface && mBackSurface && - mCurrentSurface->GetSize() != mBackSurface->GetSize()) { + (mCurrentSurface->GetSize() != mBackSurface->GetSize() || + mCurrentSurface->GetContentType() != mBackSurface->GetContentType())) { mCurrentSurface = nsnull; #ifdef XP_WIN if (mCurrentSurfaceActor) { diff --git a/dom/plugins/PluginInstanceChild.h b/dom/plugins/PluginInstanceChild.h index a4dbbd730947..83e7d7f5fced 100644 --- a/dom/plugins/PluginInstanceChild.h +++ b/dom/plugins/PluginInstanceChild.h @@ -236,6 +236,22 @@ private: InternalGetNPObjectForValue(NPNVariable aValue, NPObject** aObject); + NS_OVERRIDE + virtual bool RecvUpdateBackground(const SurfaceDescriptor& aBackground, + const nsIntRect& aRect); + + NS_OVERRIDE + virtual PPluginBackgroundDestroyerChild* + AllocPPluginBackgroundDestroyer(); + + NS_OVERRIDE + virtual bool + RecvPPluginBackgroundDestroyerConstructor(PPluginBackgroundDestroyerChild* aActor); + + NS_OVERRIDE + virtual bool + DeallocPPluginBackgroundDestroyer(PPluginBackgroundDestroyerChild* aActor); + #if defined(OS_WIN) static bool RegisterWindowClass(); bool CreatePluginWindow(); @@ -290,7 +306,6 @@ private: int nIndex, LONG newLong); #endif - void HookSystemParametersInfo(); class FlashThrottleAsyncMsg : public ChildAsyncCall { @@ -407,6 +422,8 @@ private: const NPCocoaEvent *mCurrentEvent; #endif + bool CanPaintOnBackground(); + bool IsVisible() { return mWindow.clipRect.top != 0 || mWindow.clipRect.left != 0 || @@ -488,6 +505,13 @@ private: // surface which is on ParentProcess side nsRefPtr mBackSurface; + // (Not to be confused with mBackSurface). This is a recent copy + // of the opaque pixels under our object frame, if + // |mIsTransparent|. We ask the plugin render directly onto a + // copy of the background pixels if available, and fall back on + // alpha recovery otherwise. + nsRefPtr mBackground; + #ifdef XP_WIN // These actors mirror mCurrentSurface/mBackSurface PPluginSurfaceChild* mCurrentSurfaceActor; diff --git a/dom/plugins/PluginInstanceParent.cpp b/dom/plugins/PluginInstanceParent.cpp index ba3118f141f8..b509ae54f861 100644 --- a/dom/plugins/PluginInstanceParent.cpp +++ b/dom/plugins/PluginInstanceParent.cpp @@ -40,6 +40,7 @@ #include "PluginInstanceParent.h" #include "BrowserStreamParent.h" +#include "PluginBackgroundDestroyer.h" #include "PluginModuleParent.h" #include "PluginStreamParent.h" #include "StreamNotifyParent.h" @@ -108,6 +109,7 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent, , mDrawingModel(NPDrawingModelCoreGraphics) , mIOSurface(nsnull) #endif + , mNewBackground(false) { InitQuirksModes(aMimeType); } @@ -498,6 +500,12 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect, const SurfaceDescriptor& newSurface, SurfaceDescriptor* prevSurface) { + PLUGIN_LOG_DEBUG( + ("[InstanceParent][%p] RecvShow for ", + this, updatedRect.left, updatedRect.top, + updatedRect.right - updatedRect.left, + updatedRect.bottom - updatedRect.top)); + nsRefPtr surface; if (newSurface.type() == SurfaceDescriptor::TShmem) { if (!newSurface.get_Shmem().IsReadable()) { @@ -529,6 +537,11 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect, #ifdef MOZ_X11 if (mFrontSurface && mFrontSurface->GetType() == gfxASurface::SurfaceTypeXlib) + // This is the "old front buffer" we're about to hand back to + // the plugin. We might still have drawing operations + // referencing it, so we XSync here to let them finish before + // the plugin starts scribbling on it again, or worse, + // destroys it. XSync(DefaultXDisplay(), False); #endif @@ -540,6 +553,9 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect, mFrontSurface = surface; RecvNPN_InvalidateRect(updatedRect); + PLUGIN_LOG_DEBUG((" (RecvShow invalidated for surface %p)", + mFrontSurface.get())); + return true; } @@ -572,6 +588,175 @@ PluginInstanceParent::GetSurface(gfxASurface** aSurface) return NS_ERROR_NOT_AVAILABLE; } +nsresult +PluginInstanceParent::SetBackgroundUnknown() +{ + PLUGIN_LOG_DEBUG(("[InstanceParent][%p] SetBackgroundUnknown", this)); + + NS_ABORT_IF_FALSE(!mNewBackground, "Forgot EndUpdateBackground?"); + + if (mBackground) { + DestroyBackground(); + NS_ABORT_IF_FALSE(!mBackground, "Background not destroyed"); + } + + return NS_OK; +} + +nsresult +PluginInstanceParent::BeginUpdateBackground(const nsIntRect& aRect, + gfxContext** aCtx) +{ + PLUGIN_LOG_DEBUG( + ("[InstanceParent][%p] BeginUpdateBackground for ", + this, aRect.x, aRect.y, aRect.width, aRect.height)); + + NS_ABORT_IF_FALSE(!mNewBackground, "Forgot EndUpdateBackground?"); + + if (!mBackground) { + // XXX if we failed to create a background surface on one + // update, there's no guarantee that later updates will be for + // the entire background area until successful. We might want + // to fix that eventually. + NS_ABORT_IF_FALSE(aRect.TopLeft() == nsIntPoint(0, 0), + "Expecting rect for whole frame"); + if (!CreateBackground(aRect.Size())) { + *aCtx = nsnull; + return NS_OK; + } + + // The new background forwarded over in EndUpdate below. + mNewBackground = true; + } + +#ifdef DEBUG + gfxIntSize sz = mBackground->GetSize(); + NS_ABORT_IF_FALSE(nsIntRect(0, 0, sz.width, sz.height).Contains(aRect), + "Update outside of background area"); +#endif + + nsRefPtr ctx = new gfxContext(mBackground); + *aCtx = ctx.forget().get(); + + return NS_OK; +} + +nsresult +PluginInstanceParent::EndUpdateBackground(gfxContext* aCtx, + const nsIntRect& aRect) +{ + PLUGIN_LOG_DEBUG( + ("[InstanceParent][%p] EndUpdateBackground for ", + this, aRect.x, aRect.y, aRect.width, aRect.height)); + +#ifdef MOZ_X11 + // Have to XSync here to avoid the plugin trying to draw with this + // surface racing with its creation in the X server. We also want + // to avoid the plugin drawing onto stale pixels, then handing us + // back a front surface from those pixels that we might + // recomposite for "a while" until the next update. This XSync + // still doesn't guarantee that the plugin draws onto a consistent + // view of its background, but it does mean that the plugin is + // drawing onto pixels no older than those in the latest + // EndUpdateBackground(). + XSync(DefaultXDisplay(), False); +#endif + + unused << SendUpdateBackground( + mNewBackground ? BackgroundDescriptor() : SurfaceDescriptor(null_t()), + aRect); + mNewBackground = false; + + return NS_OK; +} + +bool +PluginInstanceParent::CreateBackground(const nsIntSize& aSize) +{ + NS_ABORT_IF_FALSE(!mBackground, "Already have a background"); + + // XXX refactor me + +#if defined(MOZ_X11) + Screen* screen = DefaultScreenOfDisplay(DefaultXDisplay()); + Visual* visual = DefaultVisualOfScreen(screen); + mBackground = gfxXlibSurface::Create(screen, visual, + gfxIntSize(aSize.width, aSize.height)); + return !!mBackground; + +#elif defined(XP_WIN) + // We have chosen to create an unsafe surface in which the plugin + // can read from the region while we're writing to it. + mBackground = + gfxSharedImageSurface::CreateUnsafe( + this, + gfxIntSize(aSize.width, aSize.height), + gfxASurface::ImageFormatRGB24); + return !!mBackground; +#else + return nsnull; +#endif +} + +void +PluginInstanceParent::DestroyBackground() +{ + if (!mBackground) { + return; + } + + // Relinquish ownership of |mBackground| to its destroyer + PPluginBackgroundDestroyerParent* pbd = + new PluginBackgroundDestroyerParent(mBackground); + mBackground = nsnull; + + // If this fails, there's no problem: |bd| will be destroyed along + // with the old background surface. + unused << SendPPluginBackgroundDestroyerConstructor(pbd); +} + +SurfaceDescriptor +PluginInstanceParent::BackgroundDescriptor() +{ + NS_ABORT_IF_FALSE(mBackground, "Need a background here"); + + // XXX refactor me + +#ifdef MOZ_X11 + gfxXlibSurface* xsurf = static_cast(mBackground.get()); + return SurfaceDescriptorX11(xsurf->XDrawable(), xsurf->XRenderFormat()->id, + xsurf->GetSize()); +#endif + +#ifdef XP_WIN + NS_ABORT_IF_FALSE(gfxSharedImageSurface::IsSharedImage(mBackground), + "Expected shared image surface"); + gfxSharedImageSurface* shmem = + static_cast(mBackground.get()); + return shmem->GetShmem(); +#endif + + // If this is ever used, which it shouldn't be, it will trigger a + // hard assertion in IPDL-generated code. + return SurfaceDescriptor(); +} + +PPluginBackgroundDestroyerParent* +PluginInstanceParent::AllocPPluginBackgroundDestroyer() +{ + NS_RUNTIMEABORT("'Power-user' ctor is used exclusively"); + return nsnull; +} + +bool +PluginInstanceParent::DeallocPPluginBackgroundDestroyer( + PPluginBackgroundDestroyerParent* aActor) +{ + delete aActor; + return true; +} + + NPError PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow) { diff --git a/dom/plugins/PluginInstanceParent.h b/dom/plugins/PluginInstanceParent.h index 79502b76df8f..acb50a77b5e7 100644 --- a/dom/plugins/PluginInstanceParent.h +++ b/dom/plugins/PluginInstanceParent.h @@ -277,13 +277,27 @@ public: nsresult AsyncSetWindow(NPWindow* window); nsresult GetSurface(gfxASurface** aSurface); - nsresult SetBackgroundUnknown() { return NS_OK; } + nsresult SetBackgroundUnknown(); nsresult BeginUpdateBackground(const nsIntRect& aRect, - gfxContext** aCtx) { return NS_OK; } + gfxContext** aCtx); nsresult EndUpdateBackground(gfxContext* aCtx, - const nsIntRect& aRect) { return NS_OK; } + const nsIntRect& aRect); private: + // Create an appropriate platform surface for a background of size + // |aSize|. Return true if successful. + bool CreateBackground(const nsIntSize& aSize); + void DestroyBackground(); + SurfaceDescriptor BackgroundDescriptor() /*const*/; + + NS_OVERRIDE + virtual PPluginBackgroundDestroyerParent* + AllocPPluginBackgroundDestroyer(); + + NS_OVERRIDE + virtual bool + DeallocPPluginBackgroundDestroyer(PPluginBackgroundDestroyerParent* aActor); + // Quirks mode support for various plugin mime types enum PluginQuirks { // OSX: Don't use the refresh timer for plug-ins @@ -340,6 +354,19 @@ private: // ObjectFrame layer wrapper nsRefPtr mFrontSurface; + // For windowless+transparent instances, this surface contains a + // "pretty recent" copy of the pixels under its frame. + // On the plugin side, we use this surface to avoid doing alpha + // recovery when possible. This surface is created and owned by + // the browser, but a "read-only" reference is sent to the plugin. + // + // We have explicitly chosen not to provide any guarantees about + // the consistency of the pixels in |mBackground|. A plugin may + // be able to observe partial updates to the background. + nsRefPtr mBackground; + // True when we just created a background and it needs to be + // shared to the plugin subprocess. + bool mNewBackground; }; diff --git a/dom/plugins/PluginModuleChild.cpp b/dom/plugins/PluginModuleChild.cpp index 79f2c9e91d68..0e5d979c7653 100644 --- a/dom/plugins/PluginModuleChild.cpp +++ b/dom/plugins/PluginModuleChild.cpp @@ -1876,7 +1876,6 @@ PluginModuleChild::InitQuirksModes(const nsCString& aMimeType) mQuirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS; mQuirks |= QUIRK_FLASH_HOOK_SETLONGPTR; mQuirks |= QUIRK_FLASH_HOOK_GETWINDOWINFO; - mQuirks |= QUIRK_FLASH_MASK_CLEARTYPE_SETTINGS; } #endif } diff --git a/dom/plugins/PluginModuleChild.h b/dom/plugins/PluginModuleChild.h index 59811c2dd798..14989b30317e 100644 --- a/dom/plugins/PluginModuleChild.h +++ b/dom/plugins/PluginModuleChild.h @@ -246,9 +246,6 @@ public: // results so mouse input works when flash is displaying it's settings // window. QUIRK_FLASH_HOOK_GETWINDOWINFO = 1 << 5, - // Win: Flash trashes the alpha channel in our buffers when cleartype - // is enabled. Mask this setting so they don't know it's enabled. - QUIRK_FLASH_MASK_CLEARTYPE_SETTINGS = 1 << 6, }; int GetQuirks() { return mQuirks; } diff --git a/dom/plugins/ipdl.mk b/dom/plugins/ipdl.mk index 6aa91f9e11f5..e7439a8783b1 100644 --- a/dom/plugins/ipdl.mk +++ b/dom/plugins/ipdl.mk @@ -35,6 +35,7 @@ # ***** END LICENSE BLOCK ***** IPDLSRCS = \ + PPluginBackgroundDestroyer.ipdl \ PPluginModule.ipdl \ PPluginIdentifier.ipdl \ PPluginInstance.ipdl \ diff --git a/modules/plugin/test/testplugin/nptest_gtk2.cpp b/modules/plugin/test/testplugin/nptest_gtk2.cpp index b5adb5a20f7f..ba6632c8aba9 100644 --- a/modules/plugin/test/testplugin/nptest_gtk2.cpp +++ b/modules/plugin/test/testplugin/nptest_gtk2.cpp @@ -381,13 +381,17 @@ pluginHandleEvent(InstanceData* instanceData, void* event) if (expose.x < clip.left || expose.y < clip.top || expose.x + expose.width > clip.right || expose.y + expose.height > clip.bottom) { - g_warning("expose rectangle not in clip rectangle"); + g_warning("expose rectangle (x=%d,y=%d,w=%d,h=%d) not in clip rectangle (l=%d,t=%d,r=%d,b=%d)", + expose.x, expose.y, expose.width, expose.height, + clip.left, clip.top, clip.right, clip.bottom); return 0; } if (expose.x < window.x || expose.y < window.y || expose.x + expose.width > window.x + int32_t(window.width) || expose.y + expose.height > window.y + int32_t(window.height)) { - g_warning("expose rectangle not in plugin rectangle"); + g_warning("expose rectangle (x=%d,y=%d,w=%d,h=%d) not in plugin rectangle (x=%d,y=%d,w=%d,h=%d)", + expose.x, expose.y, expose.width, expose.height, + window.x, window.y, window.width, window.height); return 0; }