From 3ae8df01ae333270ddbf80fe7a3a8ca34b6e33d2 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 16 Feb 2011 16:43:31 -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 | 334 +++++++++++++----- dom/plugins/PluginInstanceChild.h | 26 +- dom/plugins/PluginInstanceParent.cpp | 173 +++++++++ dom/plugins/PluginInstanceParent.h | 30 +- dom/plugins/PluginModuleChild.cpp | 1 - dom/plugins/PluginModuleChild.h | 5 +- dom/plugins/ipdl.mk | 1 + .../plugin/test/testplugin/nptest_gtk2.cpp | 8 +- 11 files changed, 675 insertions(+), 93 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 00000000000..b15ad545e62 --- /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 1daa4623c9d..b5015cd7186 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 00000000000..9b3b64f1db4 --- /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 7a0a8e8a558..75f66cc0e76 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" @@ -167,7 +168,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. @@ -925,6 +925,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); @@ -1245,47 +1250,6 @@ PluginInstanceChild::PluginWindowProcInternal(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 */ /* @@ -2306,6 +2270,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!"); @@ -2318,11 +2286,8 @@ PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType, } mWindow.window = NULL; - if (mWindow.width != aWindow.width || mWindow.height != aWindow.height) { - ClearCurrentSurface(); - mAccumulatedInvalidRect = nsIntRect(0, 0, aWindow.width, aWindow.height); - } - if (mWindow.clipRect.top != aWindow.clipRect.top || + if (mWindow.width != aWindow.width || mWindow.height != aWindow.height || + mWindow.clipRect.top != aWindow.clipRect.top || mWindow.clipRect.left != aWindow.clipRect.left || mWindow.clipRect.bottom != aWindow.clipRect.bottom || mWindow.clipRect.right != aWindow.clipRect.right) @@ -2363,12 +2328,16 @@ GfxFromNsRect(const nsIntRect& aRect) bool PluginInstanceChild::CreateOptSurface(void) { + NS_ABORT_IF_FALSE(mSurfaceType != gfxASurface::SurfaceTypeMax, + "Need a valid surface type here"); 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 @@ -2406,10 +2375,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; @@ -2490,6 +2461,8 @@ PluginInstanceChild::MaybeCreatePlatformHelperSurface(void) return false; } } +#elif defined(XP_WIN) + mDoAlphaExtraction = mIsTransparent && !mBackground; #endif return true; @@ -2498,12 +2471,38 @@ PluginInstanceChild::MaybeCreatePlatformHelperSurface(void) bool PluginInstanceChild::EnsureCurrentBuffer(void) { - if (mCurrentSurface) { - return true; + nsIntRect toInvalidate(0, 0, 0, 0); + gfxIntSize winSize = gfxIntSize(mWindow.width, mWindow.height); + + if (mBackground && mBackground->GetSize() != winSize) { + // It would be nice to keep the old background here, but doing + // so can lead to cases in which we permanently keep the old + // background size. + mBackground = nsnull; + toInvalidate.UnionRect(toInvalidate, + nsIntRect(0, 0, winSize.width, winSize.height)); } - if (!mWindow.width || !mWindow.height) { - return false; + if (mCurrentSurface) { + gfxIntSize surfSize = mCurrentSurface->GetSize(); + if (winSize != surfSize || + (mBackground && !CanPaintOnBackground()) || + (mBackground && + gfxASurface::CONTENT_COLOR != mCurrentSurface->GetContentType()) || + (!mBackground && mIsTransparent && + gfxASurface::CONTENT_COLOR == mCurrentSurface->GetContentType())) { + // Don't try to use an old, invalid DC. + mWindow.window = nsnull; + ClearCurrentSurface(); + toInvalidate.UnionRect(toInvalidate, + nsIntRect(0, 0, winSize.width, winSize.height)); + } + } + + mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect, toInvalidate); + + if (mCurrentSurface) { + return true; } if (!CreateOptSurface()) { @@ -2548,7 +2547,7 @@ PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow) mWindow.window = nsnull; mWsInfo.depth = gfxUtils::ImageFormatToDepth(img->Format()); mWsInfo.colormap = 0; - needWindowUpdate = PR_TRUE; + needWindowUpdate = true; } } #endif // MAEMO @@ -2614,6 +2613,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); } @@ -2623,12 +2627,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 @@ -2717,14 +2715,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 @@ -2754,7 +2744,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); @@ -2811,6 +2801,9 @@ PluginInstanceChild::PaintRectWithAlphaExtraction(const nsIntRect& aRect, // We always use a temporary "white image" whiteImage = new gfxImageSurface(targetSize, gfxASurface::ImageFormatRGB24); + if (whiteImage->CairoStatus()) { + return; + } #ifdef XP_WIN // On windows, we need an HDC and so can't paint directly to @@ -2877,43 +2870,109 @@ PluginInstanceChild::PaintRectWithAlphaExtraction(const nsIntRect& aRect, } } +bool +PluginInstanceChild::CanPaintOnBackground() +{ + return (mBackground && + mCurrentSurface && + mCurrentSurface->GetSize() == mBackground->GetSize()); +} + bool PluginInstanceChild::ShowPluginFrame() { - if (mPendingPluginCall) { + // mLayersRendering can be false if we somehow get here without + // receiving AsyncSetWindow() first. mPendingPluginCall is our + // re-entrancy guard; we can't paint while nested inside another + // paint. + if (!mLayersRendering || mPendingPluginCall) { return false; } AutoRestore pending(mPendingPluginCall); mPendingPluginCall = true; + bool temporarilyMakeVisible = !IsVisible() && !mHasPainted; + if (temporarilyMakeVisible && mWindow.width && mWindow.height) { + 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. + ClearCurrentSurface(); + return true; + } + if (!EnsureCurrentBuffer()) { 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)); + 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 nsIntRect rect = mAccumulatedInvalidRect; mAccumulatedInvalidRect.Empty(); + // Fix up old invalidations that might have been made when our + // surface was a different size + gfxIntSize surfaceSize = mCurrentSurface->GetSize(); + rect.IntersectRect(rect, + nsIntRect(0, 0, surfaceSize.width, surfaceSize.height)); + 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 on surface ", + this, haveTransparentPixels ? " with alpha" : "", + rect.x, rect.y, rect.width, rect.height, + mCurrentSurface->GetSize().width, mCurrentSurface->GetSize().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; @@ -2937,7 +2996,7 @@ PluginInstanceChild::ShowPluginFrame() mCurrentSurfaceActor = SendPPluginSurfaceConstructor(handle, mCurrentSurface->GetSize(), - mIsTransparent); + haveTransparentPixels); } currSurf = mCurrentSurfaceActor; s->Flush(); @@ -2981,9 +3040,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); @@ -3009,7 +3076,7 @@ PluginInstanceChild::InvalidateRectDelayed(void) } mCurrentInvalidateTask = nsnull; - if (mAccumulatedInvalidRect.IsEmpty() || (mHasPainted && !IsVisible())) { + if (mAccumulatedInvalidRect.IsEmpty()) { return; } @@ -3021,7 +3088,7 @@ PluginInstanceChild::InvalidateRectDelayed(void) void PluginInstanceChild::AsyncShowPluginFrame(void) { - if (mCurrentInvalidateTask || (mHasPainted && !IsVisible())) { + if (mCurrentInvalidateTask) { return; } @@ -3057,9 +3124,111 @@ PluginInstanceChild::InvalidateRect(NPRect* aInvalidRect) AsyncShowPluginFrame(); return; } + // If we were going to use layers rendering but it's not set up + // yet, and the plugin happens to call this first, we'll forward + // the invalidation to the browser. It's unclear whether + // non-layers plugins need this rect forwarded when their window + // width or height is 0, which it would be for layers plugins + // before their first SetWindow(). SendNPN_InvalidateRect(*aInvalidRect); } +bool +PluginInstanceChild::RecvUpdateBackground(const SurfaceDescriptor& aBackground, + const nsIntRect& aRect) +{ + NS_ABORT_IF_FALSE(mIsTransparent, "Only transparent plugins use backgrounds"); + + if (!mBackground) { + // 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; + } + + gfxIntSize bgSize = mBackground->GetSize(); + mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect, + nsIntRect(0, 0, bgSize.width, bgSize.height)); + AsyncShowPluginFrame(); + return true; + } + + // XXX refactor me + mAccumulatedInvalidRect.UnionRect(aRect, mAccumulatedInvalidRect); + + // The browser is limping along with a stale copy of our pixels. + // Try to repaint ASAP. This will ClearCurrentBackground() if we + // needed it. + 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); + + // NB: we don't have to XSync here because only ShowPluginFrame() + // uses mBackground, and it always XSyncs after finishing. + mBackground = nsnull; + AsyncShowPluginFrame(); + } + + 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) @@ -3149,7 +3318,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 5ef37ba3a4e..28a2679b3bb 100644 --- a/dom/plugins/PluginInstanceChild.h +++ b/dom/plugins/PluginInstanceChild.h @@ -240,6 +240,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(); @@ -294,7 +310,6 @@ private: int nIndex, LONG newLong); #endif - void HookSystemParametersInfo(); class FlashThrottleAsyncMsg : public ChildAsyncCall { @@ -411,6 +426,8 @@ private: const NPCocoaEvent *mCurrentEvent; #endif + bool CanPaintOnBackground(); + bool IsVisible() { return mWindow.clipRect.top != 0 || mWindow.clipRect.left != 0 || @@ -492,6 +509,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 0f71dec89c2..afa6eb665d9 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" @@ -498,6 +499,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 +536,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 +552,9 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect, mFrontSurface = surface; RecvNPN_InvalidateRect(updatedRect); + PLUGIN_LOG_DEBUG((" (RecvShow invalidated for surface %p)", + mFrontSurface.get())); + return true; } @@ -627,6 +642,164 @@ PluginInstanceParent::IsRemoteDrawingCoreAnimation(PRBool *aDrawing) } #endif +nsresult +PluginInstanceParent::SetBackgroundUnknown() +{ + PLUGIN_LOG_DEBUG(("[InstanceParent][%p] SetBackgroundUnknown", this)); + + 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)); + + 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; + } + } + +#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(BackgroundDescriptor(), aRect); + + 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 c7f40527e28..83b5c6fd68e 100644 --- a/dom/plugins/PluginInstanceParent.h +++ b/dom/plugins/PluginInstanceParent.h @@ -282,13 +282,27 @@ public: #ifdef XP_MACOSX nsresult IsRemoteDrawingCoreAnimation(PRBool *aDrawing); #endif - 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 @@ -345,6 +359,16 @@ 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; }; diff --git a/dom/plugins/PluginModuleChild.cpp b/dom/plugins/PluginModuleChild.cpp index 92f85c04cdd..8d1dad11e14 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; mQuirks |= QUIRK_FLASH_FIXUP_MOUSE_CAPTURE; } #endif diff --git a/dom/plugins/PluginModuleChild.h b/dom/plugins/PluginModuleChild.h index 31ebf802ec7..eec27346b48 100644 --- a/dom/plugins/PluginModuleChild.h +++ b/dom/plugins/PluginModuleChild.h @@ -246,12 +246,9 @@ 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, // Win: Addresses a flash bug with mouse capture and full screen // windows. - QUIRK_FLASH_FIXUP_MOUSE_CAPTURE = 1 << 7, + QUIRK_FLASH_FIXUP_MOUSE_CAPTURE = 1 << 6, }; int GetQuirks() { return mQuirks; } diff --git a/dom/plugins/ipdl.mk b/dom/plugins/ipdl.mk index 6aa91f9e11f..e7439a8783b 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 b5adb5a20f7..ba6632c8aba 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; }