diff --git a/browser/locales/en-US/feedback/main.dtd b/browser/locales/en-US/feedback/main.dtd index 074195302b8..e4c25cf8b40 100644 --- a/browser/locales/en-US/feedback/main.dtd +++ b/browser/locales/en-US/feedback/main.dtd @@ -29,3 +29,5 @@ + + diff --git a/dom/base/ConsoleAPI.js b/dom/base/ConsoleAPI.js index 3403d2ad9c5..8fcd9ca0238 100644 --- a/dom/base/ConsoleAPI.js +++ b/dom/base/ConsoleAPI.js @@ -62,7 +62,7 @@ ConsoleAPI.prototype = { } let self = this; - return { + let chromeObject = { // window.console API log: function CA_log() { self.notifyObservers(id, "log", arguments); @@ -76,10 +76,32 @@ ConsoleAPI.prototype = { error: function CA_error() { self.notifyObservers(id, "error", arguments); }, - // many flavors of console objects exist on the web, so calling - // unimplemented methods shouldn't be fatal. See bug 614350 - __noSuchMethod__: function CA_nsm() {} + __exposedProps__: { + log: "r", + info: "r", + warn: "r", + error: "r" + } }; + + // We need to return an actual content object here, instead of a wrapped + // chrome object. This allows things like console.log.bind() to work. + let sandbox = Cu.Sandbox(aWindow); + let contentObject = Cu.evalInSandbox( + "(function(x) {\ + var bind = Function.bind;\ + var obj = {\ + log: bind.call(x.log, x),\ + info: bind.call(x.info, x),\ + warn: bind.call(x.warn, x),\ + error: bind.call(x.error, x),\ + __noSuchMethod__: function() {}\ + };\ + Object.defineProperty(obj, '__mozillaConsole__', { value: true });\ + return obj;\ + })", sandbox)(chromeObject); + + return contentObject; }, /** 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 6eff1f1774d..f6553e190c4 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 @@ -2538,8 +2516,8 @@ PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow) HDC dc = NULL; if (curSurface) { - NS_ASSERTION(SharedDIBSurface::IsSharedDIBSurface(curSurface), - "Expected (SharedDIB) image surface."); + if (!SharedDIBSurface::IsSharedDIBSurface(curSurface)) + NS_RUNTIMEABORT("Expected SharedDIBSurface!"); SharedDIBSurface* dibsurf = static_cast(curSurface.get()); dc = dibsurf->GetHDC(); @@ -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); @@ -2760,50 +2729,112 @@ void PluginInstanceChild::PaintRectWithAlphaExtraction(const nsIntRect& aRect, gfxASurface* aSurface) { - // Paint onto black image - bool needImageSurface = true; - nsRefPtr blackImage; - gfxIntSize clipSize(aRect.width, aRect.height); - gfxPoint deviceOffset(-aRect.x, -aRect.y); - // Try to re-use existing image surface, and avoid one copy - if (aSurface->GetType() == gfxASurface::SurfaceTypeImage) { - gfxImageSurface *surface = static_cast(aSurface); - if (surface->Format() == gfxASurface::ImageFormatARGB32) { - needImageSurface = false; - blackImage = surface->GetSubimage(GfxFromNsRect(aRect)); + NS_ABORT_IF_FALSE(aSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA, + "Refusing to pointlessly recover alpha"); + + nsIntRect rect(aRect); + // If |aSurface| can be used to paint and can have alpha values + // recovered directly to it, do that to save a tmp surface and + // copy. + bool useSurfaceSubimageForBlack = false; + if (gfxASurface::SurfaceTypeImage == aSurface->GetType()) { + gfxImageSurface* surfaceAsImage = + static_cast(aSurface); + useSurfaceSubimageForBlack = + (surfaceAsImage->Format() == gfxASurface::ImageFormatARGB32); + // If we're going to use a subimage, nudge the rect so that we + // can use optimal alpha recovery. If we're not using a + // subimage, the temporaries should automatically get + // fast-path alpha recovery so we don't need to do anything. + if (useSurfaceSubimageForBlack) { + rect = + gfxAlphaRecovery::AlignRectForSubimageRecovery(aRect, + surfaceAsImage); } } - // otherwise create new helper surface - if (needImageSurface) { - blackImage = new gfxImageSurface(clipSize, gfxASurface::ImageFormatARGB32); + + nsRefPtr whiteImage; + nsRefPtr blackImage; + gfxRect targetRect(rect.x, rect.y, rect.width, rect.height); + gfxIntSize targetSize(rect.width, rect.height); + gfxPoint deviceOffset = -targetRect.pos; + + // We always use a temporary "white image" + whiteImage = new gfxImageSurface(targetSize, gfxASurface::ImageFormatRGB24); + +#ifdef XP_WIN + // On windows, we need an HDC and so can't paint directly to + // vanilla image surfaces. Bifurcate this painting code so that + // we don't accidentally attempt that. + if (!SharedDIBSurface::IsSharedDIBSurface(aSurface)) + NS_RUNTIMEABORT("Expected SharedDIBSurface!"); + + // Paint the plugin directly onto the target, with a white + // background and copy the result + PaintRectToSurface(rect, aSurface, gfxRGBA(1.0, 1.0, 1.0)); + { + gfxRect copyRect(gfxPoint(0, 0), targetRect.size); + nsRefPtr ctx = new gfxContext(whiteImage); + ctx->SetOperator(gfxContext::OPERATOR_SOURCE); + ctx->SetSource(aSurface, deviceOffset); + ctx->Rectangle(copyRect); + ctx->Fill(); } - // Paint to black image - blackImage->SetDeviceOffset(deviceOffset); - PaintRectToSurface(aRect, blackImage, gfxRGBA(0.0, 0.0, 0.0)); + // Paint the plugin directly onto the target, with a black + // background + PaintRectToSurface(rect, aSurface, gfxRGBA(0.0, 0.0, 0.0)); - // Paint onto white image - nsRefPtr whiteImage = - new gfxImageSurface(clipSize, gfxASurface::ImageFormatRGB24); + // Don't copy the result, just extract a subimage so that we can + // recover alpha directly into the target + gfxImageSurface *image = static_cast(aSurface); + blackImage = image->GetSubimage(targetRect); +#else + // Paint onto white background whiteImage->SetDeviceOffset(deviceOffset); - PaintRectToSurface(aRect, whiteImage, gfxRGBA(1.0, 1.0, 1.0)); + PaintRectToSurface(rect, whiteImage, gfxRGBA(1.0, 1.0, 1.0)); - // Extract Alpha from black and white image and store to black Image - gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height); - if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage, nsnull)) { + if (useSurfaceSubimageForBlack) { + gfxImageSurface *surface = static_cast(aSurface); + blackImage = surface->GetSubimage(targetRect); + } else { + blackImage = new gfxImageSurface(targetSize, + gfxASurface::ImageFormatARGB32); + } + + // Paint onto black background + blackImage->SetDeviceOffset(deviceOffset); + PaintRectToSurface(rect, blackImage, gfxRGBA(0.0, 0.0, 0.0)); +#endif + + NS_ABORT_IF_FALSE(whiteImage && blackImage, "Didn't paint enough!"); + + // Extract alpha from black and white image and store to black + // image + if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) { return; } - if (needImageSurface) { + // If we had to use a temporary black surface, copy the pixels + // with alpha back to the target + if (!useSurfaceSubimageForBlack) { nsRefPtr ctx = new gfxContext(aSurface); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->SetSource(blackImage); - ctx->Rectangle(GfxFromNsRect(aRect)); + ctx->Rectangle(targetRect); ctx->Fill(); } } +bool +PluginInstanceChild::CanPaintOnBackground() +{ + return (mBackground && + mCurrentSurface && + mCurrentSurface->GetSize() == mBackground->GetSize()); +} + bool PluginInstanceChild::ShowPluginFrame() { @@ -2818,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 @@ -2830,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; @@ -2864,7 +2941,7 @@ PluginInstanceChild::ShowPluginFrame() mCurrentSurfaceActor = SendPPluginSurfaceConstructor(handle, mCurrentSurface->GetSize(), - mIsTransparent); + haveTransparentPixels); } currSurf = mCurrentSurfaceActor; s->Flush(); @@ -2908,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); @@ -2987,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) @@ -3076,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 a4dbbd73094..83e7d7f5fce 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 ba3118f141f..b509ae54f86 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 34e30bbbd6b..acb50a77b5e 100644 --- a/dom/plugins/PluginInstanceParent.h +++ b/dom/plugins/PluginInstanceParent.h @@ -277,8 +277,27 @@ public: nsresult AsyncSetWindow(NPWindow* window); nsresult GetSurface(gfxASurface** aSurface); + nsresult SetBackgroundUnknown(); + nsresult BeginUpdateBackground(const nsIntRect& aRect, + gfxContext** aCtx); + nsresult EndUpdateBackground(gfxContext* aCtx, + 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 @@ -335,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/PluginLibrary.h b/dom/plugins/PluginLibrary.h index e7e64d026d2..7e0ae6fde1e 100644 --- a/dom/plugins/PluginLibrary.h +++ b/dom/plugins/PluginLibrary.h @@ -46,9 +46,11 @@ #include "nsTArray.h" #include "nsPluginError.h" -class nsNPAPIPlugin; class gfxASurface; +class gfxContext; class nsCString; +struct nsIntRect; +class nsNPAPIPlugin; namespace mozilla { @@ -89,6 +91,16 @@ public: virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window) = 0; virtual nsresult GetSurface(NPP instance, gfxASurface** aSurface) = 0; virtual bool UseAsyncPainting() = 0; + /** + * The next three methods are the third leg in the trip to + * PluginInstanceParent. They approximately follow the ReadbackSink + * API. + */ + virtual nsresult SetBackgroundUnknown(NPP instance) = 0; + virtual nsresult BeginUpdateBackground(NPP instance, + const nsIntRect&, gfxContext**) = 0; + virtual nsresult EndUpdateBackground(NPP instance, + gfxContext*, const nsIntRect&) = 0; }; diff --git a/dom/plugins/PluginModuleChild.cpp b/dom/plugins/PluginModuleChild.cpp index dfde96cfa17..0e5d979c765 100644 --- a/dom/plugins/PluginModuleChild.cpp +++ b/dom/plugins/PluginModuleChild.cpp @@ -137,14 +137,9 @@ PluginModuleChild::~PluginModuleChild() if (mLibrary) { PR_UnloadLibrary(mLibrary); } -#ifdef MOZ_WIDGET_QT - nsQAppInstance::Release(); - if (sGtkLib) { - PR_UnloadLibrary(sGtkLib); - sGtkLib = nsnull; - s_gtk_init = nsnull; - } -#endif + + DeinitGraphics(); + gInstance = nsnull; } @@ -559,6 +554,26 @@ PluginModuleChild::InitGraphics() return true; } +void +PluginModuleChild::DeinitGraphics() +{ +#ifdef MOZ_WIDGET_QT + nsQAppInstance::Release(); + if (sGtkLib) { + PR_UnloadLibrary(sGtkLib); + sGtkLib = nsnull; + s_gtk_init = nsnull; + } +#endif + +#if defined(MOZ_X11) && defined(NS_FREE_PERMANENT_DATA) + // We free some data off of XDisplay close hooks, ensure they're + // run. Closing the display is pretty scary, so we only do it to + // silence leak checkers. + XCloseDisplay(DefaultXDisplay()); +#endif +} + bool PluginModuleChild::AnswerNP_Shutdown(NPError *rv) { @@ -1861,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 0247ce08f8e..14989b30317 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; } @@ -261,6 +258,7 @@ public: private: void InitQuirksModes(const nsCString& aMimeType); bool InitGraphics(); + void DeinitGraphics(); #if defined(MOZ_WIDGET_GTK2) static gboolean DetectNestedEventLoop(gpointer data); static gboolean ProcessBrowserEvents(gpointer data); diff --git a/dom/plugins/PluginModuleParent.cpp b/dom/plugins/PluginModuleParent.cpp index a8080720796..e6715e72115 100644 --- a/dom/plugins/PluginModuleParent.cpp +++ b/dom/plugins/PluginModuleParent.cpp @@ -682,6 +682,40 @@ PluginModuleParent::GetSurface(NPP instance, gfxASurface** aSurface) return i->GetSurface(aSurface); } +nsresult +PluginModuleParent::SetBackgroundUnknown(NPP instance) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return NS_ERROR_FAILURE; + + return i->SetBackgroundUnknown(); +} + +nsresult +PluginModuleParent::BeginUpdateBackground(NPP instance, + const nsIntRect& aRect, + gfxContext** aCtx) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return NS_ERROR_FAILURE; + + return i->BeginUpdateBackground(aRect, aCtx); +} + +nsresult +PluginModuleParent::EndUpdateBackground(NPP instance, + gfxContext* aCtx, + const nsIntRect& aRect) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return NS_ERROR_FAILURE; + + return i->EndUpdateBackground(aCtx, aRect); +} + #if defined(XP_UNIX) && !defined(XP_MACOSX) nsresult PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) diff --git a/dom/plugins/PluginModuleParent.h b/dom/plugins/PluginModuleParent.h index deb8bb47bee..e63301d1b85 100644 --- a/dom/plugins/PluginModuleParent.h +++ b/dom/plugins/PluginModuleParent.h @@ -232,6 +232,16 @@ private: virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window); virtual nsresult GetSurface(NPP instance, gfxASurface** aSurface); NS_OVERRIDE virtual bool UseAsyncPainting() { return true; } + NS_OVERRIDE + virtual nsresult SetBackgroundUnknown(NPP instance); + NS_OVERRIDE + virtual nsresult BeginUpdateBackground(NPP instance, + const nsIntRect& aRect, + gfxContext** aCtx); + NS_OVERRIDE + virtual nsresult EndUpdateBackground(NPP instance, + gfxContext* aCtx, + const nsIntRect& aRect); #if defined(XP_UNIX) && !defined(XP_MACOSX) virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error); 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/extensions/auth/nsAuthSSPI.cpp b/extensions/auth/nsAuthSSPI.cpp index 9390c59d6b5..7b2e47e737d 100644 --- a/extensions/auth/nsAuthSSPI.cpp +++ b/extensions/auth/nsAuthSSPI.cpp @@ -21,6 +21,7 @@ * Contributor(s): * Darin Fisher * Jim Mathies + * Guillermo Robla Vicario * * 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 @@ -52,6 +53,7 @@ #include "nsIDNSRecord.h" #include "nsNetCID.h" #include "nsCOMPtr.h" +#include "nsICryptoHash.h" #include @@ -190,6 +192,8 @@ nsAuthSSPI::nsAuthSSPI(pType package) : mServiceFlags(REQ_DEFAULT) , mMaxTokenLen(0) , mPackage(package) + , mCertDERData(nsnull) + , mCertDERLength(0) { memset(&mCred, 0, sizeof(mCred)); memset(&mCtxt, 0, sizeof(mCtxt)); @@ -212,6 +216,14 @@ nsAuthSSPI::~nsAuthSSPI() void nsAuthSSPI::Reset() { + mIsFirst = PR_TRUE; + + if (mCertDERData){ + nsMemory::Free(mCertDERData); + mCertDERData = nsnull; + mCertDERLength = 0; + } + if (mCtxt.dwLower || mCtxt.dwUpper) { (sspi->DeleteSecurityContext)(&mCtxt); memset(&mCtxt, 0, sizeof(mCtxt)); @@ -229,6 +241,10 @@ nsAuthSSPI::Init(const char *serviceName, { LOG((" nsAuthSSPI::Init\n")); + mIsFirst = PR_TRUE; + mCertDERLength = 0; + mCertDERData = nsnull; + // The caller must supply a service name to be used. (For why we now require // a service name for NTLM, see bug 487872.) NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG); @@ -314,19 +330,32 @@ nsAuthSSPI::Init(const char *serviceName, return NS_OK; } +// The arguments inToken and inTokenLen are used to pass in the server +// certificate (when available) in the first call of the function. The +// second time these arguments hold an input token. NS_IMETHODIMP nsAuthSSPI::GetNextToken(const void *inToken, PRUint32 inTokenLen, void **outToken, PRUint32 *outTokenLen) { + // String for end-point bindings. + const char end_point[] = "tls-server-end-point:"; + const int end_point_length = sizeof(end_point) - 1; + const int hash_size = 32; // Size of a SHA256 hash. + const int cbt_size = hash_size + end_point_length; + SECURITY_STATUS rc; TimeStamp ignored; DWORD ctxAttr, ctxReq = 0; CtxtHandle *ctxIn; SecBufferDesc ibd, obd; - SecBuffer ib, ob; + // Optional second input buffer for the CBT (Channel Binding Token) + SecBuffer ib[2], ob; + // Pointer to the block of memory that stores the CBT + char* sspi_cbt = nsnull; + SEC_CHANNEL_BINDINGS pendpoint_binding; LOG(("entering nsAuthSSPI::GetNextToken()\n")); @@ -341,26 +370,123 @@ nsAuthSSPI::GetNextToken(const void *inToken, ctxReq |= ISC_REQ_MUTUAL_AUTH; if (inToken) { - ib.BufferType = SECBUFFER_TOKEN; - ib.cbBuffer = inTokenLen; - ib.pvBuffer = (void *) inToken; - ibd.ulVersion = SECBUFFER_VERSION; - ibd.cBuffers = 1; - ibd.pBuffers = &ib; - ctxIn = &mCtxt; - } - else { - // If there is no input token, then we are starting a new - // authentication sequence. If we have already initialized our - // security context, then we're in trouble because it means that the - // first sequence failed. We need to bail or else we might end up in - // an infinite loop. - if (mCtxt.dwLower || mCtxt.dwUpper) { + if (mIsFirst) { + // First time if it comes with a token, + // the token represents the server certificate. + mIsFirst = PR_FALSE; + mCertDERLength = inTokenLen; + mCertDERData = nsMemory::Alloc(inTokenLen); + if (!mCertDERData) + return NS_ERROR_OUT_OF_MEMORY; + memcpy(mCertDERData, inToken, inTokenLen); + + // We are starting a new authentication sequence. + // If we have already initialized our + // security context, then we're in trouble because it means that the + // first sequence failed. We need to bail or else we might end up in + // an infinite loop. + if (mCtxt.dwLower || mCtxt.dwUpper) { + LOG(("Cannot restart authentication sequence!")); + return NS_ERROR_UNEXPECTED; + } + ctxIn = nsnull; + // The certificate needs to be erased before being passed + // to InitializeSecurityContextW(). + inToken = nsnull; + inTokenLen = 0; + } else { + ibd.ulVersion = SECBUFFER_VERSION; + ibd.cBuffers = 0; + ibd.pBuffers = ib; + + // If we have stored a certificate, the Channel Binding Token + // needs to be generated and sent in the first input buffer. + if (mCertDERLength > 0) { + // First we create a proper Endpoint Binding structure. + pendpoint_binding.dwInitiatorAddrType = 0; + pendpoint_binding.cbInitiatorLength = 0; + pendpoint_binding.dwInitiatorOffset = 0; + pendpoint_binding.dwAcceptorAddrType = 0; + pendpoint_binding.cbAcceptorLength = 0; + pendpoint_binding.dwAcceptorOffset = 0; + pendpoint_binding.cbApplicationDataLength = cbt_size; + pendpoint_binding.dwApplicationDataOffset = + sizeof(SEC_CHANNEL_BINDINGS); + + // Then add it to the array of sec buffers accordingly. + ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS; + ib[ibd.cBuffers].cbBuffer = + pendpoint_binding.cbApplicationDataLength + + pendpoint_binding.dwApplicationDataOffset; + + sspi_cbt = (char *) nsMemory::Alloc(ib[ibd.cBuffers].cbBuffer); + if (!sspi_cbt){ + return NS_ERROR_OUT_OF_MEMORY; + } + + // Helper to write in the memory block that stores the CBT + char* sspi_cbt_ptr = sspi_cbt; + + ib[ibd.cBuffers].pvBuffer = sspi_cbt; + ibd.cBuffers++; + + memcpy(sspi_cbt_ptr, &pendpoint_binding, + pendpoint_binding.dwApplicationDataOffset); + sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset; + + memcpy(sspi_cbt_ptr, end_point, end_point_length); + sspi_cbt_ptr += end_point_length; + + // Start hashing. We are always doing SHA256, but depending + // on the certificate, a different alogirthm might be needed. + nsCAutoString hashString; + + nsresult rv; + nsCOMPtr crypto; + crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) + rv = crypto->Init(nsICryptoHash::SHA256); + if (NS_SUCCEEDED(rv)) + rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength); + if (NS_SUCCEEDED(rv)) + rv = crypto->Finish(PR_FALSE, hashString); + if (NS_FAILED(rv)) { + nsMemory::Free(mCertDERData); + mCertDERData = nsnull; + mCertDERLength = 0; + nsMemory::Free(sspi_cbt); + return rv; + } + + // Once the hash has been computed, we store it in memory right + // after the Endpoint structure and the "tls-server-end-point:" + // char array. + memcpy(sspi_cbt_ptr, hashString.get(), hash_size); + + // Free memory used to store the server certificate + nsMemory::Free(mCertDERData); + mCertDERData = nsnull; + mCertDERLength = 0; + } // End of CBT computation. + + // We always need this SECBUFFER. + ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN; + ib[ibd.cBuffers].cbBuffer = inTokenLen; + ib[ibd.cBuffers].pvBuffer = (void *) inToken; + ibd.cBuffers++; + ctxIn = &mCtxt; + } + } else { // First time and without a token (no server certificate) + // We are starting a new authentication sequence. If we have already + // initialized our security context, then we're in trouble because it + // means that the first sequence failed. We need to bail or else we + // might end up in an infinite loop. + if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) { LOG(("Cannot restart authentication sequence!")); return NS_ERROR_UNEXPECTED; } - ctxIn = NULL; + mIsFirst = PR_FALSE; } obd.ulVersion = SECBUFFER_VERSION; @@ -369,8 +495,11 @@ nsAuthSSPI::GetNextToken(const void *inToken, ob.BufferType = SECBUFFER_TOKEN; ob.cbBuffer = mMaxTokenLen; ob.pvBuffer = nsMemory::Alloc(ob.cbBuffer); - if (!ob.pvBuffer) + if (!ob.pvBuffer){ + if (sspi_cbt) + nsMemory::Free(sspi_cbt); return NS_ERROR_OUT_OF_MEMORY; + } memset(ob.pvBuffer, 0, ob.cbBuffer); NS_ConvertUTF8toUTF16 wSN(mServiceName); @@ -396,7 +525,9 @@ nsAuthSSPI::GetNextToken(const void *inToken, else LOG(("InitializeSecurityContext: continue.\n")); #endif - + if (sspi_cbt) + nsMemory::Free(sspi_cbt); + if (!ob.cbBuffer) { nsMemory::Free(ob.pvBuffer); ob.pvBuffer = NULL; diff --git a/extensions/auth/nsAuthSSPI.h b/extensions/auth/nsAuthSSPI.h index c4050fe9834..a7945e800f7 100644 --- a/extensions/auth/nsAuthSSPI.h +++ b/extensions/auth/nsAuthSSPI.h @@ -20,6 +20,7 @@ * * Contributor(s): * Darin Fisher + * Guillermo Robla Vicario * * 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 @@ -81,6 +82,9 @@ private: nsString mDomain; nsString mUsername; nsString mPassword; + PRBool mIsFirst; + void* mCertDERData; + PRUint32 mCertDERLength; }; #endif /* nsAuthSSPI_h__ */ diff --git a/gfx/ipc/SharedDIBWin.cpp b/gfx/ipc/SharedDIBWin.cpp index 53b328d49ef..c9ca4ef870c 100644 --- a/gfx/ipc/SharedDIBWin.cpp +++ b/gfx/ipc/SharedDIBWin.cpp @@ -37,6 +37,7 @@ * ***** END LICENSE BLOCK ***** */ #include "SharedDIBWin.h" +#include "gfxAlphaRecovery.h" #include "nsMathUtils.h" #include "nsDebug.h" @@ -44,6 +45,9 @@ namespace mozilla { namespace gfx { static const PRUint32 kBytesPerPixel = 4; +static const PRUint32 kByteAlign = 1 << gfxAlphaRecovery::GoodAlignmentLog2(); +static const PRUint32 kHeaderBytes = + (sizeof(BITMAPV4HEADER) + kByteAlign - 1) & ~(kByteAlign - 1); SharedDIBWin::SharedDIBWin() : mSharedHdc(nsnull) @@ -140,7 +144,7 @@ SharedDIBWin::SetupBitmapHeader(PRUint32 aWidth, PRUint32 aHeight, if (aTransparent) aHeader->bV4AlphaMask = 0xFF000000; - return (sizeof(BITMAPV4HEADER) + (-aHeader->bV4Height * aHeader->bV4Width * kBytesPerPixel)); + return (kHeaderBytes + (-aHeader->bV4Height * aHeader->bV4Width * kBytesPerPixel)); } nsresult @@ -156,7 +160,7 @@ SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER *aHdr) DIB_RGB_COLORS, &mBitmapBits, mShMem->handle(), - (unsigned long)sizeof(BITMAPV4HEADER)); + kHeaderBytes); if (!mSharedBmp) return NS_ERROR_FAILURE; diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index a1ffd9eb936..5c1e30cecad 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -45,6 +45,7 @@ #include "ImageLayers.h" #include "Layers.h" #include "gfxPlatform.h" +#include "ReadbackLayer.h" using namespace mozilla::layers; @@ -65,6 +66,15 @@ namespace { // XXX pretty general utilities, could centralize +nsACString& +AppendToString(nsACString& s, const void* p, + const char* pfx="", const char* sfx="") +{ + s += pfx; + s += nsPrintfCString(64, "%p", p); + return s += sfx; +} + nsACString& AppendToString(nsACString& s, const gfxPattern::GraphicsFilter& f, const char* pfx="", const char* sfx="") @@ -391,6 +401,30 @@ ContainerLayer::ComputeEffectiveTransformsForChildren(const gfx3DMatrix& aTransf } } +void +ContainerLayer::DidRemoveChild(Layer* aLayer) +{ + ThebesLayer* tl = aLayer->AsThebesLayer(); + if (tl && tl->UsedForReadback()) { + for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) { + if (l->GetType() == TYPE_READBACK) { + static_cast(l)->NotifyThebesLayerRemoved(tl); + } + } + } + if (aLayer->GetType() == TYPE_READBACK) { + static_cast(aLayer)->NotifyRemoved(); + } +} + +void +ContainerLayer::DidInsertChild(Layer* aLayer) +{ + if (aLayer->GetType() == TYPE_READBACK) { + mMayHaveReadbackChild = PR_TRUE; + } +} + #ifdef MOZ_LAYERS_HAVE_LOG static nsACString& PrintInfo(nsACString& aTo, ShadowLayer* aShadowLayer); @@ -534,6 +568,22 @@ ImageLayer::PrintInfo(nsACString& aTo, const char* aPrefix) return aTo; } +nsACString& +ReadbackLayer::PrintInfo(nsACString& aTo, const char* aPrefix) +{ + Layer::PrintInfo(aTo, aPrefix); + AppendToString(aTo, mSize, " [size=", "]"); + if (mBackgroundLayer) { + AppendToString(aTo, mBackgroundLayer, " [backgroundLayer=", "]"); + AppendToString(aTo, mBackgroundLayerOffset, " [backgroundOffset=", "]"); + } else if (mBackgroundColor.a == 1.0) { + AppendToString(aTo, mBackgroundColor, " [backgroundColor=", "]"); + } else { + aTo += " [nobackground]"; + } + return aTo; +} + //-------------------------------------------------- // LayerManager @@ -665,6 +715,10 @@ nsACString& ImageLayer::PrintInfo(nsACString& aTo, const char* aPrefix) { return aTo; } +nsACString& +ReadbackLayer::PrintInfo(nsACString& aTo, const char* aPrefix) +{ return aTo; } + void LayerManager::Dump(FILE* aFile, const char* aPrefix) {} void LayerManager::DumpSelf(FILE* aFile, const char* aPrefix) {} void LayerManager::Log(const char* aPrefix) {} diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 896313dd400..70847ddc5ad 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -78,6 +78,8 @@ class ColorLayer; class ImageContainer; class CanvasLayer; class ShadowLayer; +class ReadbackLayer; +class ReadbackProcessor; class SpecificLayerAttributes; /** @@ -394,6 +396,11 @@ public: * Create a CanvasLayer for this manager's layer tree. */ virtual already_AddRefed CreateCanvasLayer() = 0; + /** + * CONSTRUCTION PHASE ONLY + * Create a ReadbackLayer for this manager's layer tree. + */ + virtual already_AddRefed CreateReadbackLayer() { return nsnull; } /** * Can be called anytime @@ -503,13 +510,15 @@ class THEBES_API Layer { NS_INLINE_DECL_REFCOUNTING(Layer) public: + // Keep these in alphabetical order enum LayerType { - TYPE_THEBES, + TYPE_CANVAS, + TYPE_COLOR, TYPE_CONTAINER, TYPE_IMAGE, - TYPE_COLOR, - TYPE_CANVAS, - TYPE_SHADOW + TYPE_READBACK, + TYPE_SHADOW, + TYPE_THEBES }; virtual ~Layer() {} @@ -928,12 +937,16 @@ public: mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), nsnull); } + bool UsedForReadback() { return mUsedForReadback; } + void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; } + protected: ThebesLayer(LayerManager* aManager, void* aImplData) : Layer(aManager, aImplData) , mValidRegion() , mXResolution(1.0) , mYResolution(1.0) + , mUsedForReadback(false) { mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT } @@ -956,6 +969,11 @@ protected: // sense for all backends to fully support it. float mXResolution; float mYResolution; + /** + * Set when this ThebesLayer is participating in readback, i.e. some + * ReadbackLayer (may) be getting its background from this layer. + */ + bool mUsedForReadback; }; /** @@ -1025,12 +1043,18 @@ public: PRBool SupportsComponentAlphaChildren() { return mSupportsComponentAlphaChildren; } protected: + friend class ReadbackProcessor; + + void DidInsertChild(Layer* aLayer); + void DidRemoveChild(Layer* aLayer); + ContainerLayer(LayerManager* aManager, void* aImplData) : Layer(aManager, aImplData), mFirstChild(nsnull), mLastChild(nsnull), mUseIntermediateSurface(PR_FALSE), - mSupportsComponentAlphaChildren(PR_FALSE) + mSupportsComponentAlphaChildren(PR_FALSE), + mMayHaveReadbackChild(PR_FALSE) { mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT } @@ -1053,6 +1077,7 @@ protected: FrameMetrics mFrameMetrics; PRPackedBool mUseIntermediateSurface; PRPackedBool mSupportsComponentAlphaChildren; + PRPackedBool mMayHaveReadbackChild; }; /** diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 09dca58a1ff..e22dce92223 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -68,12 +68,14 @@ EXPORTS = \ Layers.h \ LayerManagerOGL.h \ LayerManagerOGLProgram.h \ + ReadbackLayer.h \ $(NULL) CPPSRCS = \ - Layers.cpp \ BasicImages.cpp \ BasicLayers.cpp \ + Layers.cpp \ + ReadbackProcessor.cpp \ ThebesLayerBuffer.cpp \ CanvasLayerOGL.cpp \ ColorLayerOGL.cpp \ @@ -104,6 +106,7 @@ endif ifdef MOZ_ENABLE_D3D10_LAYER EXPORTS += \ LayerManagerD3D10.h \ + ReadbackManagerD3D10.h \ $(NULL) CPPSRCS += \ @@ -113,6 +116,7 @@ CPPSRCS += \ ImageLayerD3D10.cpp \ ColorLayerD3D10.cpp \ CanvasLayerD3D10.cpp \ + ReadbackManagerD3D10.cpp \ $(NULL) endif endif diff --git a/gfx/layers/ReadbackLayer.h b/gfx/layers/ReadbackLayer.h new file mode 100644 index 00000000000..4ede5fd9da8 --- /dev/null +++ b/gfx/layers/ReadbackLayer.h @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert O'Callahan + * + * 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 GFX_READBACKLAYER_H +#define GFX_READBACKLAYER_H + +#include "Layers.h" + +namespace mozilla { +namespace layers { + +class ReadbackProcessor; + +/** + * A ReadbackSink receives a stream of updates to a rectangle of pixels. + * These update callbacks are always called on the main thread, either during + * EndTransaction or from the event loop. + */ +class THEBES_API ReadbackSink { +public: + ReadbackSink() {} + virtual ~ReadbackSink() {} + + /** + * Sends an update to indicate that the background is currently unknown. + */ + virtual void SetUnknown(PRUint64 aSequenceNumber) = 0; + /** + * Called by the layer system to indicate that the contents of part of + * the readback area are changing. + * @param aRect is the rectangle of content that is being updated, + * in the coordinate system of the ReadbackLayer. + * @param aSequenceNumber updates issued out of order should be ignored. + * Only use updates whose sequence counter is greater than all other updates + * seen so far. Return null when a non-fresh sequence value is given. + * @return a context into which the update should be drawn. This should be + * set up to clip to aRect. Zero should never be passed as a sequence number. + * If this returns null, EndUpdate should NOT be called. If it returns + * non-null, EndUpdate must be called. + * + * We don't support partially unknown backgrounds. Therefore, the + * first BeginUpdate after a SetUnknown will have the complete background. + */ + virtual already_AddRefed + BeginUpdate(const nsIntRect& aRect, PRUint64 aSequenceNumber) = 0; + /** + * EndUpdate must be called immediately after BeginUpdate, without returning + * to the event loop. + * @param aContext the context returned by BeginUpdate + * Implicitly Restore()s the state of aContext. + */ + virtual void EndUpdate(gfxContext* aContext, const nsIntRect& aRect) = 0; +}; + +/** + * A ReadbackLayer never renders anything. It enables clients to extract + * the rendered contents of the layer tree below the ReadbackLayer. + * The rendered contents are delivered asynchronously via calls to a + * ReadbackSink object supplied by the client. + * + * This is a "best effort" API; it is possible for the layer system to tell + * the ReadbackSink that the contents of the readback area are unknown. + * + * This API exists to work around the limitations of transparent windowless + * plugin rendering APIs. It should not be used for anything else. + */ +class THEBES_API ReadbackLayer : public Layer { +public: + MOZ_LAYER_DECL_NAME("ReadbackLayer", TYPE_READBACK) + + virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) + { + // Snap our local transform first, and snap the inherited transform as well. + // This makes our snapping equivalent to what would happen if our content + // was drawn into a ThebesLayer (gfxContext would snap using the local + // transform, then we'd snap again when compositing the ThebesLayer). + mEffectiveTransform = + SnapTransform(GetLocalTransform(), gfxRect(0, 0, mSize.width, mSize.height), + nsnull)* + SnapTransform(aTransformToSurface, gfxRect(0, 0, 0, 0), nsnull); + } + + /** + * CONSTRUCTION PHASE ONLY + * Set the callback object to which readback updates will be delivered. + * This also resets the "needed rectangle" so that on the next layer tree + * transaction we will try to deliver the full contents of the readback + * area to the sink. + * This layer takes ownership of the sink. It will be deleted when the + * layer is destroyed or when a new sink is set. + * Initially the contents of the readback area are completely unknown. + */ + void SetSink(ReadbackSink* aSink) + { + SetUnknown(); + mSink = aSink; + } + ReadbackSink* GetSink() { return mSink; } + + /** + * CONSTRUCTION PHASE ONLY + * Set the size of content that should be read back. The readback area + * has its top-left at 0,0 and has size aSize. + * Can only be called while the sink is null! + */ + void SetSize(const nsIntSize& aSize) + { + NS_ASSERTION(!mSink, "Should have no sink while changing size!"); + mSize = aSize; + } + const nsIntSize& GetSize() { return mSize; } + nsIntRect GetRect() { return nsIntRect(nsIntPoint(0, 0), mSize); } + + PRBool IsBackgroundKnown() + { + return mBackgroundLayer || mBackgroundColor.a == 1.0; + } + + void NotifyRemoved() { + SetUnknown(); + mSink = nsnull; + } + + void NotifyThebesLayerRemoved(ThebesLayer* aLayer) + { + if (mBackgroundLayer == aLayer) { + mBackgroundLayer = nsnull; + } + } + + const nsIntPoint& GetBackgroundLayerOffset() { return mBackgroundLayerOffset; } + + PRUint64 AllocateSequenceNumber() { return ++mSequenceCounter; } + + void SetUnknown() + { + if (IsBackgroundKnown()) { + if (mSink) { + mSink->SetUnknown(AllocateSequenceNumber()); + } + mBackgroundLayer = nsnull; + mBackgroundColor = gfxRGBA(0,0,0,0); + } + } + +protected: + friend class ReadbackProcessor; + + ReadbackLayer(LayerManager* aManager, void* aImplData) : + Layer(aManager, aImplData), + mSequenceCounter(0), + mSize(0,0), + mBackgroundLayer(nsnull), + mBackgroundLayerOffset(0, 0), + mBackgroundColor(gfxRGBA(0,0,0,0)) + {} + + // Print interesting information about this into aTo. Internally + // used to implement Dump*() and Log*(). + virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix); + + PRUint64 mSequenceCounter; + nsAutoPtr mSink; + nsIntSize mSize; + + // This can refer to any (earlier) sibling ThebesLayer. That ThebesLayer + // must have mUsedForReadback set on it. If the ThebesLayer is removed + // for the container, this will be set to null by NotifyThebesLayerRemoved. + // This ThebesLayer contains the contents which have previously been reported + // to mSink. The ThebesLayer had only an integer translation transform, + // and it covered the entire readback area. This layer also had only an + // integer translation transform. + ThebesLayer* mBackgroundLayer; + // When mBackgroundLayer is non-null, this is the offset to add to + // convert from the coordinates of mBackgroundLayer to the coordinates + // of this layer. + nsIntPoint mBackgroundLayerOffset; + // When mBackgroundColor is opaque, this is the color of the ColorLayer + // that contained the contents we reported to mSink, which covered the + // entire readback area. + gfxRGBA mBackgroundColor; +}; + +} +} +#endif /* GFX_READBACKLAYER_H */ diff --git a/gfx/layers/ReadbackProcessor.cpp b/gfx/layers/ReadbackProcessor.cpp new file mode 100644 index 00000000000..e3b78ce0150 --- /dev/null +++ b/gfx/layers/ReadbackProcessor.cpp @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert O'Callahan + * + * 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 "ReadbackProcessor.h" + +namespace mozilla { +namespace layers { + +void +ReadbackProcessor::BuildUpdates(ContainerLayer* aContainer) +{ + NS_ASSERTION(mAllUpdates.IsEmpty(), "Some updates not processed?"); + + if (!aContainer->mMayHaveReadbackChild) + return; + + aContainer->mMayHaveReadbackChild = PR_FALSE; + // go backwards so the updates read from earlier layers are later in the + // array. + for (Layer* l = aContainer->GetLastChild(); l; l = l->GetPrevSibling()) { + if (l->GetType() == Layer::TYPE_READBACK) { + aContainer->mMayHaveReadbackChild = PR_TRUE; + BuildUpdatesForLayer(static_cast(l)); + } + } +} + +static Layer* +FindBackgroundLayer(ReadbackLayer* aLayer, nsIntPoint* aOffset) +{ + gfxMatrix transform; + if (!aLayer->GetTransform().Is2D(&transform) || + transform.HasNonIntegerTranslation()) + return nsnull; + nsIntPoint transformOffset(PRInt32(transform.x0), PRInt32(transform.y0)); + + for (Layer* l = aLayer->GetPrevSibling(); l; l = l->GetPrevSibling()) { + gfxMatrix backgroundTransform; + if (!l->GetTransform().Is2D(&backgroundTransform) || + backgroundTransform.HasNonIntegerTranslation()) + return nsnull; + + nsIntPoint backgroundOffset(PRInt32(backgroundTransform.x0), PRInt32(backgroundTransform.y0)); + nsIntRect rectInBackground(transformOffset - backgroundOffset, aLayer->GetSize()); + const nsIntRegion& visibleRegion = l->GetEffectiveVisibleRegion(); + if (!visibleRegion.Intersects(rectInBackground)) + continue; + // Since l is present in the background, from here on we either choose l + // or nothing. + if (!visibleRegion.Contains(rectInBackground)) + return nsnull; + + if (l->GetEffectiveOpacity() != 1.0 || + !(l->GetContentFlags() & Layer::CONTENT_OPAQUE)) + return nsnull; + + // cliprects are post-transform + const nsIntRect* clipRect = l->GetEffectiveClipRect(); + if (clipRect && !clipRect->Contains(nsIntRect(transformOffset, aLayer->GetSize()))) + return nsnull; + + Layer::LayerType type = l->GetType(); + if (type != Layer::TYPE_COLOR && type != Layer::TYPE_THEBES) + return nsnull; + + *aOffset = backgroundOffset - transformOffset; + return l; + } + + return nsnull; +} + +void +ReadbackProcessor::BuildUpdatesForLayer(ReadbackLayer* aLayer) +{ + if (!aLayer->mSink) + return; + + nsIntPoint offset; + Layer* newBackground = FindBackgroundLayer(aLayer, &offset); + if (!newBackground) { + aLayer->SetUnknown(); + return; + } + + if (newBackground->GetType() == Layer::TYPE_COLOR) { + ColorLayer* colorLayer = static_cast(newBackground); + if (aLayer->mBackgroundColor != colorLayer->GetColor()) { + aLayer->mBackgroundLayer = nsnull; + aLayer->mBackgroundColor = colorLayer->GetColor(); + NS_ASSERTION(aLayer->mBackgroundColor.a == 1.0, + "Color layer said it was opaque!"); + nsRefPtr ctx = + aLayer->mSink->BeginUpdate(aLayer->GetRect(), + aLayer->AllocateSequenceNumber()); + if (ctx) { + ctx->SetColor(aLayer->mBackgroundColor); + nsIntSize size = aLayer->GetSize(); + ctx->Rectangle(gfxRect(0, 0, size.width, size.height)); + ctx->Fill(); + aLayer->mSink->EndUpdate(ctx, aLayer->GetRect()); + } + } + } else { + NS_ASSERTION(newBackground->AsThebesLayer(), "Must be ThebesLayer"); + ThebesLayer* thebesLayer = static_cast(newBackground); + // updateRect is relative to the ThebesLayer + nsIntRect updateRect = aLayer->GetRect() - offset; + if (thebesLayer != aLayer->mBackgroundLayer || + offset != aLayer->mBackgroundLayerOffset) { + aLayer->mBackgroundLayer = thebesLayer; + aLayer->mBackgroundLayerOffset = offset; + aLayer->mBackgroundColor = gfxRGBA(0,0,0,0); + thebesLayer->SetUsedForReadback(true); + } else { + nsIntRegion invalid; + invalid.Sub(updateRect, thebesLayer->GetValidRegion()); + updateRect = invalid.GetBounds(); + } + + Update update = { aLayer, updateRect, aLayer->AllocateSequenceNumber() }; + mAllUpdates.AppendElement(update); + } +} + +void +ReadbackProcessor::GetThebesLayerUpdates(ThebesLayer* aLayer, + nsTArray* aUpdates, + nsIntRegion* aUpdateRegion) +{ + // All ThebesLayers used for readback are in mAllUpdates (some possibly + // with an empty update rect). + aLayer->SetUsedForReadback(false); + if (aUpdateRegion) { + aUpdateRegion->SetEmpty(); + } + for (PRUint32 i = mAllUpdates.Length(); i > 0; --i) { + const Update& update = mAllUpdates[i - 1]; + if (update.mLayer->mBackgroundLayer == aLayer) { + aLayer->SetUsedForReadback(true); + // Don't bother asking for updates if we have an empty update rect. + if (!update.mUpdateRect.IsEmpty()) { + aUpdates->AppendElement(update); + if (aUpdateRegion) { + aUpdateRegion->Or(*aUpdateRegion, update.mUpdateRect); + } + } + mAllUpdates.RemoveElementAt(i - 1); + } + } +} + +ReadbackProcessor::~ReadbackProcessor() +{ + for (PRUint32 i = mAllUpdates.Length(); i > 0; --i) { + const Update& update = mAllUpdates[i - 1]; + // Unprocessed update. Notify the readback sink that this content is + // unknown. + update.mLayer->SetUnknown(); + } +} + +} +} diff --git a/gfx/layers/ReadbackProcessor.h b/gfx/layers/ReadbackProcessor.h new file mode 100644 index 00000000000..0f8bffb582e --- /dev/null +++ b/gfx/layers/ReadbackProcessor.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert O'Callahan + * + * 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 GFX_READBACKPROCESSOR_H +#define GFX_READBACKPROCESSOR_H + +#include "ReadbackLayer.h" +#include "ThebesLayerBuffer.h" + +namespace mozilla { +namespace layers { + +class ReadbackProcessor { +public: + /** + * Called by the container before processing any child layers. Call this + * if any child layer might have changed in any way (other than content-only + * changes to layers other than ColorLayers and ThebesLayers). + * + * This method recomputes the relationship between ReadbackLayers and + * sibling layers, and dispatches changes to ReadbackLayers. Except that + * if a ThebesLayer needs its contents sent to some ReadbackLayer, we'll + * just record that internally and later the ThebesLayer should call + * GetThebesLayerUpdates when it paints, to find out which rectangle needs + * to be sent, and the ReadbackLayer it needs to be sent to. + */ + void BuildUpdates(ContainerLayer* aContainer); + + struct Update { + /** + * The layer a ThebesLayer should send its contents to. + */ + ReadbackLayer* mLayer; + /** + * The rectangle of content that it should send, in the ThebesLayer's + * coordinate system. This rectangle is guaranteed to be in the ThebesLayer's + * visible region. Translate it to mLayer's coordinate system + * by adding mLayer->GetBackgroundLayerOffset(). + */ + nsIntRect mUpdateRect; + /** + * The sequence counter value to use when calling DoUpdate + */ + PRUint64 mSequenceCounter; + }; + /** + * Appends any ReadbackLayers that need to be updated, and the rects that + * need to be updated, to aUpdates. Only need to call this for ThebesLayers + * that have been marked UsedForReadback(). + * Each Update's mLayer's mBackgroundLayer will have been set to aLayer. + * If a ThebesLayer doesn't call GetThebesLayerUpdates, then all the + * ReadbackLayers that needed data from that ThebesLayer will be marked + * as having unknown backgrounds. + * @param aUpdateRegion if non-null, this region is set to the union + * of the mUpdateRects. + */ + void GetThebesLayerUpdates(ThebesLayer* aLayer, + nsTArray* aUpdates, + nsIntRegion* aUpdateRegion = nsnull); + + ~ReadbackProcessor(); + +protected: + void BuildUpdatesForLayer(ReadbackLayer* aLayer); + + nsTArray mAllUpdates; +}; + +} +} +#endif /* GFX_READBACKPROCESSOR_H */ diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index 6cb55b6a47c..11e0aec1bd6 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -58,6 +58,7 @@ #include "gfxUtils.h" #include "ThebesLayerBuffer.h" #include "nsIWidget.h" +#include "ReadbackProcessor.h" #include "GLContext.h" @@ -106,9 +107,16 @@ public: * set up to account for all the properties of the layer (transform, * opacity, etc). */ - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) {} + virtual void Paint(gfxContext* aContext) {} + + /** + * Like Paint() but called for ThebesLayers with the additional parameters + * they need. + */ + virtual void PaintThebes(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + ReadbackProcessor* aReadback) {} virtual ShadowableLayer* AsShadowableLayer() { return nsnull; } @@ -236,8 +244,6 @@ ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer) aAfter->GetParent() == aContainer), "aAfter is not our child"); - NS_ADDREF(aChild); - aChild->SetParent(aContainer); if (aAfter == aContainer->mLastChild) { aContainer->mLastChild = aChild; @@ -248,6 +254,8 @@ ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer) aContainer->mFirstChild->SetPrevSibling(aChild); } aContainer->mFirstChild = aChild; + NS_ADDREF(aChild); + aContainer->DidInsertChild(aChild); return; } @@ -258,6 +266,8 @@ ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer) next->SetPrevSibling(aChild); } aAfter->SetNextSibling(aChild); + NS_ADDREF(aChild); + aContainer->DidInsertChild(aChild); } template @@ -286,6 +296,7 @@ ContainerRemoveChild(Layer* aChild, Container* aContainer) aChild->SetPrevSibling(nsnull); aChild->SetParent(nsnull); + aContainer->DidRemoveChild(aChild); NS_RELEASE(aChild); } @@ -377,9 +388,10 @@ public: mValidRegion.Sub(mValidRegion, aRegion); } - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData); + virtual void PaintThebes(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + ReadbackProcessor* aReadback); virtual void ClearCachedResources() { mBuffer.Clear(); mValidRegion.SetEmpty(); } @@ -504,14 +516,20 @@ PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const nsIntRegion& aRegio } void -BasicThebesLayer::Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) +BasicThebesLayer::PaintThebes(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + ReadbackProcessor* aReadback) { NS_ASSERTION(BasicManager()->InDrawing(), "Can only draw in drawing phase"); nsRefPtr targetSurface = aContext->CurrentSurface(); + nsTArray readbackUpdates; + if (aReadback && UsedForReadback()) { + aReadback->GetThebesLayerUpdates(this, &readbackUpdates); + } + PRBool canUseOpaqueSurface = CanUseOpaqueSurface(); Buffer::ContentType contentType = canUseOpaqueSurface ? gfxASurface::CONTENT_COLOR : @@ -522,6 +540,8 @@ BasicThebesLayer::Paint(gfxContext* aContext, (!canUseOpaqueSurface && (mContentFlags & CONTENT_COMPONENT_ALPHA) && !MustRetainContent())) { + NS_ASSERTION(readbackUpdates.IsEmpty(), "Can't do readback for non-retained layer"); + mValidRegion.SetEmpty(); mBuffer.Clear(); @@ -584,6 +604,20 @@ BasicThebesLayer::Paint(gfxContext* aContext, } mBuffer.DrawTo(this, aContext, opacity); + + for (PRUint32 i = 0; i < readbackUpdates.Length(); ++i) { + ReadbackProcessor::Update& update = readbackUpdates[i]; + nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset(); + nsRefPtr ctx = + update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset, + update.mSequenceCounter); + if (ctx) { + NS_ASSERTION(opacity == 1.0, "Should only read back opaque layers"); + ctx->Translate(gfxPoint(offset.x, offset.y)); + mBuffer.DrawTo(this, ctx, 1.0); + update.mLayer->GetSink()->EndUpdate(ctx, update.mUpdateRect + offset); + } + } } static PRBool @@ -664,9 +698,7 @@ public: ImageLayer::SetVisibleRegion(aRegion); } - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData); + virtual void Paint(gfxContext* aContext); static void PaintContext(gfxPattern* aPattern, const nsIntRegion& aVisible, @@ -688,9 +720,7 @@ protected: }; void -BasicImageLayer::Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) +BasicImageLayer::Paint(gfxContext* aContext) { nsRefPtr dontcare = GetAndPaintCurrentImage(aContext, GetEffectiveOpacity()); @@ -796,9 +826,7 @@ public: ColorLayer::SetVisibleRegion(aRegion); } - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) + virtual void Paint(gfxContext* aContext) { PaintColorTo(mColor, GetEffectiveOpacity(), aContext); } @@ -844,9 +872,7 @@ public: virtual void Initialize(const Data& aData); virtual void Updated(const nsIntRect& aRect); - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData); + virtual void Paint(gfxContext* aContext); virtual void PaintWithOpacity(gfxContext* aContext, float aOpacity); @@ -956,9 +982,7 @@ BasicCanvasLayer::Updated(const nsIntRect& aRect) } void -BasicCanvasLayer::Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) +BasicCanvasLayer::Paint(gfxContext* aContext) { PaintWithOpacity(aContext, GetEffectiveOpacity()); } @@ -995,6 +1019,34 @@ BasicCanvasLayer::PaintWithOpacity(gfxContext* aContext, mUpdatedRect.Empty(); } +class BasicReadbackLayer : public ReadbackLayer, + BasicImplData +{ +public: + BasicReadbackLayer(BasicLayerManager* aLayerManager) : + ReadbackLayer(aLayerManager, static_cast(this)) + { + MOZ_COUNT_CTOR(BasicReadbackLayer); + } + virtual ~BasicReadbackLayer() + { + MOZ_COUNT_DTOR(BasicReadbackLayer); + } + + virtual void SetVisibleRegion(const nsIntRegion& aRegion) + { + NS_ASSERTION(BasicManager()->InConstruction(), + "Can only set properties in construction phase"); + ReadbackLayer::SetVisibleRegion(aRegion); + } + +protected: + BasicLayerManager* BasicManager() + { + return static_cast(mManager); + } +}; + static nsIntRect ToOutsideIntRect(const gfxRect &aRect) { @@ -1297,7 +1349,7 @@ BasicLayerManager::EndTransactionInternal(DrawThebesLayerCallback aCallback, MarkLeafLayersCoveredByOpaque(mRoot, mRoot->GetEffectiveVisibleRegion().GetBounds(), region); - PaintLayer(mRoot, aCallback, aCallbackData); + PaintLayer(mRoot, aCallback, aCallbackData, nsnull); // If we're doing manual double-buffering, we need to avoid drawing // the results of an incomplete transaction to the destination surface. @@ -1368,7 +1420,8 @@ BasicLayerManager::SetRoot(Layer* aLayer) void BasicLayerManager::PaintLayer(Layer* aLayer, DrawThebesLayerCallback aCallback, - void* aCallbackData) + void* aCallbackData, + ReadbackProcessor* aReadback) { const nsIntRect* clipRect = aLayer->GetEffectiveClipRect(); const gfx3DMatrix& effectiveTransform = aLayer->GetEffectiveTransform(); @@ -1430,11 +1483,21 @@ BasicLayerManager::PaintLayer(Layer* aLayer, (void*)aLayer, data->IsCoveredByOpaque())); #endif if (!data->IsCoveredByOpaque()) { - data->Paint(mTarget, aCallback, aCallbackData); + if (aLayer->GetType() == Layer::TYPE_THEBES) { + data->PaintThebes(mTarget, aCallback, aCallbackData, aReadback); + } else { + data->Paint(mTarget); + } } } else { + ReadbackProcessor readback; + if (IsRetained()) { + ContainerLayer* container = static_cast(aLayer); + readback.BuildUpdates(container); + } + for (; child; child = child->GetNextSibling()) { - PaintLayer(child, aCallback, aCallbackData); + PaintLayer(child, aCallback, aCallbackData, &readback); if (mTransactionIncomplete) break; } @@ -1518,6 +1581,13 @@ BasicLayerManager::CreateCanvasLayer() return layer.forget(); } +already_AddRefed +BasicLayerManager::CreateReadbackLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = new BasicReadbackLayer(this); + return layer.forget(); +} #ifdef MOZ_IPC @@ -1574,6 +1644,19 @@ ToShadowable(Layer* aLayer) return ToData(aLayer)->AsShadowableLayer(); } +// Some layers, like ReadbackLayers, can't be shadowed and shadowing +// them doesn't make sense anyway +static bool +ShouldShadow(Layer* aLayer) +{ + if (!ToShadowable(aLayer)) { + NS_ABORT_IF_FALSE(aLayer->GetType() == Layer::TYPE_READBACK, + "Only expect not to shadow ReadbackLayers"); + return false; + } + return true; +} + template static BasicShadowableLayer* GetBasicShadowable(const OpT& op) @@ -1621,7 +1704,10 @@ private: void BasicShadowableContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter) { - if (HasShadow()) { + if (HasShadow() && ShouldShadow(aChild)) { + while (aAfter && !ShouldShadow(aAfter)) { + aAfter = aAfter->GetPrevSibling(); + } ShadowManager()->InsertAfter(ShadowManager()->Hold(this), ShadowManager()->Hold(aChild), aAfter ? ShadowManager()->Hold(aAfter) : nsnull); @@ -1632,7 +1718,7 @@ BasicShadowableContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter) void BasicShadowableContainerLayer::RemoveChild(Layer* aChild) { - if (HasShadow()) { + if (HasShadow() && ShouldShadow(aChild)) { ShadowManager()->RemoveChild(ShadowManager()->Hold(this), ShadowManager()->Hold(aChild)); } @@ -1856,9 +1942,7 @@ public: MOZ_COUNT_DTOR(BasicShadowableImageLayer); } - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData); + virtual void Paint(gfxContext* aContext); virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { @@ -1889,9 +1973,7 @@ private: }; void -BasicShadowableImageLayer::Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) +BasicShadowableImageLayer::Paint(gfxContext* aContext) { gfxIntSize oldSize = mSize; nsRefPtr pat = GetAndPaintCurrentImage(aContext, GetEffectiveOpacity()); @@ -1976,9 +2058,7 @@ public: } virtual void Initialize(const Data& aData); - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData); + virtual void Paint(gfxContext* aContext); virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { @@ -2039,11 +2119,9 @@ BasicShadowableCanvasLayer::Initialize(const Data& aData) } void -BasicShadowableCanvasLayer::Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) +BasicShadowableCanvasLayer::Paint(gfxContext* aContext) { - BasicCanvasLayer::Paint(aContext, aCallback, aCallbackData); + BasicCanvasLayer::Paint(aContext); if (!HasShadow()) return; @@ -2166,9 +2244,10 @@ public: } } - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData); + virtual void PaintThebes(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + ReadbackProcessor* aReadback); private: BasicShadowLayerManager* BasicManager() @@ -2260,9 +2339,10 @@ BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront, } void -BasicShadowThebesLayer::Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) +BasicShadowThebesLayer::PaintThebes(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + ReadbackProcessor* aReadback) { NS_ASSERTION(BasicManager()->InDrawing(), "Can only draw in drawing phase"); @@ -2356,9 +2436,7 @@ public: mFrontSurface = nsnull; } - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData); + virtual void Paint(gfxContext* aContext); protected: BasicShadowLayerManager* BasicManager() @@ -2389,9 +2467,7 @@ BasicShadowImageLayer::Swap(gfxSharedImageSurface* newFront) } void -BasicShadowImageLayer::Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) +BasicShadowImageLayer::Paint(gfxContext* aContext) { if (!mFrontSurface) { return; @@ -2417,9 +2493,7 @@ public: MOZ_COUNT_DTOR(BasicShadowColorLayer); } - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) + virtual void Paint(gfxContext* aContext) { BasicColorLayer::PaintColorTo(mColor, GetEffectiveOpacity(), aContext); } @@ -2461,9 +2535,7 @@ public: mFrontSurface = nsnull; } - virtual void Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData); + virtual void Paint(gfxContext* aContext); private: BasicShadowLayerManager* BasicManager() @@ -2495,9 +2567,7 @@ BasicShadowCanvasLayer::Swap(gfxSharedImageSurface* newFront) } void -BasicShadowCanvasLayer::Paint(gfxContext* aContext, - LayerManager::DrawThebesLayerCallback aCallback, - void* aCallbackData) +BasicShadowCanvasLayer::Paint(gfxContext* aContext) { NS_ASSERTION(BasicManager()->InDrawing(), "Can only draw in drawing phase"); @@ -2668,7 +2738,7 @@ BasicShadowLayerManager::Mutated(Layer* aLayer) BasicLayerManager::Mutated(aLayer); NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase"); - if (HasShadowManager()) { + if (HasShadowManager() && ShouldShadow(aLayer)) { ShadowLayerForwarder::Mutated(Hold(aLayer)); } } diff --git a/gfx/layers/basic/BasicLayers.h b/gfx/layers/basic/BasicLayers.h index a2f6f03594c..6590f618001 100644 --- a/gfx/layers/basic/BasicLayers.h +++ b/gfx/layers/basic/BasicLayers.h @@ -60,6 +60,7 @@ class ShadowContainerLayer; class ShadowImageLayer; class ShadowCanvasLayer; class ShadowColorLayer; +class ReadbackProcessor; /** * This is a cairo/Thebes-only, main-thread-only implementation of layers. @@ -152,16 +153,17 @@ public: virtual already_AddRefed CreateCanvasLayer(); virtual already_AddRefed CreateImageContainer(); virtual already_AddRefed CreateColorLayer(); + virtual already_AddRefed CreateReadbackLayer(); virtual already_AddRefed CreateShadowThebesLayer() - { return NULL; } + { return nsnull; } virtual already_AddRefed CreateShadowContainerLayer() - { return NULL; } + { return nsnull; } virtual already_AddRefed CreateShadowImageLayer() - { return NULL; } + { return nsnull; } virtual already_AddRefed CreateShadowColorLayer() - { return NULL; } + { return nsnull; } virtual already_AddRefed CreateShadowCanvasLayer() - { return NULL; } + { return nsnull; } virtual LayersBackend GetBackendType() { return LAYERS_BASIC; } virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Basic"); } @@ -197,7 +199,8 @@ protected: // Paints aLayer to mTarget. void PaintLayer(Layer* aLayer, DrawThebesLayerCallback aCallback, - void* aCallbackData); + void* aCallbackData, + ReadbackProcessor* aReadback); // Clear the contents of a layer void ClearLayer(Layer* aLayer); diff --git a/gfx/layers/d3d10/CanvasLayerD3D10.h b/gfx/layers/d3d10/CanvasLayerD3D10.h index 426119a72e8..4fa8af816c8 100644 --- a/gfx/layers/d3d10/CanvasLayerD3D10.h +++ b/gfx/layers/d3d10/CanvasLayerD3D10.h @@ -36,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef GFX_CANVASLAYEROGL_H -#define GFX_CANVASLAYEROGL_H +#ifndef GFX_CANVASLAYERD3D10_H +#define GFX_CANVASLAYERD3D10_H #include "LayerManagerD3D10.h" #include "GLContext.h" diff --git a/gfx/layers/d3d10/ContainerLayerD3D10.cpp b/gfx/layers/d3d10/ContainerLayerD3D10.cpp index ef7dec7965f..6f4660ed482 100644 --- a/gfx/layers/d3d10/ContainerLayerD3D10.cpp +++ b/gfx/layers/d3d10/ContainerLayerD3D10.cpp @@ -40,6 +40,9 @@ #include "gfxUtils.h" #include "nsRect.h" +#include "ThebesLayerD3D10.h" +#include "ReadbackProcessor.h" + namespace mozilla { namespace layers { @@ -72,6 +75,7 @@ ContainerLayerD3D10::InsertAfter(Layer* aChild, Layer* aAfter) mLastChild = aChild; } NS_ADDREF(aChild); + DidInsertChild(aChild); return; } for (Layer *child = GetFirstChild(); @@ -87,6 +91,7 @@ ContainerLayerD3D10::InsertAfter(Layer* aChild, Layer* aAfter) } aChild->SetPrevSibling(child); NS_ADDREF(aChild); + DidInsertChild(aChild); return; } } @@ -106,6 +111,7 @@ ContainerLayerD3D10::RemoveChild(Layer *aChild) aChild->SetNextSibling(nsnull); aChild->SetPrevSibling(nsnull); aChild->SetParent(nsnull); + DidRemoveChild(aChild); NS_RELEASE(aChild); return; } @@ -123,6 +129,7 @@ ContainerLayerD3D10::RemoveChild(Layer *aChild) child->SetNextSibling(nsnull); child->SetPrevSibling(nsnull); child->SetParent(nsnull); + DidRemoveChild(aChild); NS_RELEASE(aChild); return; } @@ -398,9 +405,16 @@ ContainerLayerD3D10::Validate() (mParent && mParent->SupportsComponentAlphaChildren()); } + ReadbackProcessor readback; + readback.BuildUpdates(this); + Layer *layer = GetFirstChild(); while (layer) { - static_cast(layer->ImplData())->Validate(); + if (layer->GetType() == TYPE_THEBES) { + static_cast(layer)->Validate(&readback); + } else { + static_cast(layer->ImplData())->Validate(); + } layer = layer->GetNextSibling(); } } diff --git a/gfx/layers/d3d10/ImageLayerD3D10.cpp b/gfx/layers/d3d10/ImageLayerD3D10.cpp index 4623e62ded0..09e3ddc3aeb 100644 --- a/gfx/layers/d3d10/ImageLayerD3D10.cpp +++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp @@ -90,8 +90,11 @@ SurfaceToTexture(ID3D10Device *aDevice, nsRefPtr texture; HRESULT hr = aDevice->CreateTexture2D(&desc, &data, getter_AddRefs(texture)); - LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("Failed to create texture for image surface"), - hr); + if (FAILED(hr)) { + LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("Failed to create texture for image surface"), + hr); + } + return texture.forget(); } diff --git a/gfx/layers/d3d10/LayerManagerD3D10.cpp b/gfx/layers/d3d10/LayerManagerD3D10.cpp index 3e47e0ece5f..280052cfce0 100644 --- a/gfx/layers/d3d10/LayerManagerD3D10.cpp +++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp @@ -39,6 +39,7 @@ #include "LayerManagerD3D10Effect.h" #include "gfxWindowsPlatform.h" #include "gfxD2DSurface.h" +#include "gfxFailure.h" #include "cairo-win32.h" #include "dxgi.h" @@ -46,6 +47,7 @@ #include "ThebesLayerD3D10.h" #include "ColorLayerD3D10.h" #include "CanvasLayerD3D10.h" +#include "ReadbackLayerD3D10.h" #include "ImageLayerD3D10.h" #include "../d3d9/Nv3DVUtils.h" @@ -67,15 +69,12 @@ struct Vertex float position[2]; }; -// {17F88CCB-1F49-4c08-8002-ADA7BD44856D} -static const GUID sEffect = -{ 0x17f88ccb, 0x1f49, 0x4c08, { 0x80, 0x2, 0xad, 0xa7, 0xbd, 0x44, 0x85, 0x6d } }; -// {19599D91-912C-4C2F-A8C5-299DE85FBD34} -static const GUID sInputLayout = -{ 0x19599d91, 0x912c, 0x4c2f, { 0xa8, 0xc5, 0x29, 0x9d, 0xe8, 0x5f, 0xbd, 0x34 } }; -// {293157D2-09C7-4680-AE27-C28E370E418B} -static const GUID sVertexBuffer = -{ 0x293157d2, 0x9c7, 0x4680, { 0xae, 0x27, 0xc2, 0x8e, 0x37, 0xe, 0x41, 0x8b } }; +// {592BF306-0EED-4F76-9D03-A0846450F472} +static const GUID sDeviceAttachments = +{ 0x592bf306, 0xeed, 0x4f76, { 0x9d, 0x3, 0xa0, 0x84, 0x64, 0x50, 0xf4, 0x72 } }; +// {716AEDB1-C9C3-4B4D-8332-6F65D44AF6A8} +static const GUID sLayerManagerCount = +{ 0x716aedb1, 0xc9c3, 0x4b4d, { 0x83, 0x32, 0x6f, 0x65, 0xd4, 0x4a, 0xf6, 0xa8 } }; cairo_user_data_key_t gKeyD3D10Texture; @@ -84,8 +83,36 @@ LayerManagerD3D10::LayerManagerD3D10(nsIWidget *aWidget) { } +struct DeviceAttachments +{ + nsRefPtr mEffect; + nsRefPtr mInputLayout; + nsRefPtr mVertexBuffer; + nsRefPtr mReadbackManager; +}; + LayerManagerD3D10::~LayerManagerD3D10() { + if (mDevice) { + int referenceCount = 0; + UINT size = sizeof(referenceCount); + HRESULT hr = mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount); + NS_ASSERTION(SUCCEEDED(hr), "Reference count not found on device."); + referenceCount--; + mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount); + + if (!referenceCount) { + DeviceAttachments *attachments; + size = sizeof(attachments); + mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments); + // No LayerManagers left for this device. Clear out interfaces stored which + // hold a reference to the device. + mDevice->SetPrivateData(sDeviceAttachments, 0, NULL); + + delete attachments; + } + } + Destroy(); } @@ -129,8 +156,19 @@ LayerManagerD3D10::Initialize() mNv3DVUtils->SetDeviceInfo(devUnknown); } - UINT size = sizeof(ID3D10Effect*); - if (FAILED(mDevice->GetPrivateData(sEffect, &size, mEffect.StartAssignment()))) { + int referenceCount = 0; + UINT size = sizeof(referenceCount); + // If this isn't there yet it'll fail, count will remain 0, which is correct. + mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount); + referenceCount++; + mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount); + + DeviceAttachments *attachments; + size = sizeof(DeviceAttachments*); + if (FAILED(mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments))) { + attachments = new DeviceAttachments; + mDevice->SetPrivateData(sDeviceAttachments, sizeof(attachments), &attachments); + D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc) GetProcAddress(LoadLibraryA("d3d10_1.dll"), "D3D10CreateEffectFromMemory"); @@ -149,11 +187,8 @@ LayerManagerD3D10::Initialize() return false; } - mDevice->SetPrivateDataInterface(sEffect, mEffect); - } - - size = sizeof(ID3D10InputLayout*); - if (FAILED(mDevice->GetPrivateData(sInputLayout, &size, mInputLayout.StartAssignment()))) { + attachments->mEffect = mEffect; + D3D10_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, @@ -172,11 +207,8 @@ LayerManagerD3D10::Initialize() return false; } - mDevice->SetPrivateDataInterface(sInputLayout, mInputLayout); - } - - size = sizeof(ID3D10Buffer*); - if (FAILED(mDevice->GetPrivateData(sVertexBuffer, &size, mVertexBuffer.StartAssignment()))) { + attachments->mInputLayout = mInputLayout; + Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} }; CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); D3D10_SUBRESOURCE_DATA data; @@ -188,7 +220,11 @@ LayerManagerD3D10::Initialize() return false; } - mDevice->SetPrivateDataInterface(sVertexBuffer, mVertexBuffer); + attachments->mVertexBuffer = mVertexBuffer; + } else { + mEffect = attachments->mEffect; + mVertexBuffer = attachments->mVertexBuffer; + mInputLayout = attachments->mInputLayout; } nsRefPtr dxgiDevice; @@ -333,6 +369,13 @@ LayerManagerD3D10::CreateCanvasLayer() return layer.forget(); } +already_AddRefed +LayerManagerD3D10::CreateReadbackLayer() +{ + nsRefPtr layer = new ReadbackLayerD3D10(this); + return layer.forget(); +} + already_AddRefed LayerManagerD3D10::CreateImageContainer() { @@ -382,6 +425,13 @@ LayerManagerD3D10::CreateOptimalSurface(const gfxIntSize &aSize, return surface.forget(); } +ReadbackManagerD3D10* +LayerManagerD3D10::readbackManager() +{ + EnsureReadbackManager(); + return mReadbackManager; +} + void LayerManagerD3D10::SetViewport(const nsIntSize &aViewport) { @@ -495,6 +545,32 @@ LayerManagerD3D10::VerifyBufferSize() } +void +LayerManagerD3D10::EnsureReadbackManager() +{ + if (mReadbackManager) { + return; + } + + DeviceAttachments *attachments; + UINT size = sizeof(DeviceAttachments*); + if (FAILED(mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments))) { + // Strange! This shouldn't happen ... return a readback manager for this + // layer manager only. + mReadbackManager = new ReadbackManagerD3D10(); + gfx::LogFailure(NS_LITERAL_CSTRING("Couldn't get device attachments for device.")); + return; + } + + if (attachments->mReadbackManager) { + mReadbackManager = attachments->mReadbackManager; + return; + } + + mReadbackManager = new ReadbackManagerD3D10(); + attachments->mReadbackManager = mReadbackManager; +} + void LayerManagerD3D10::Render() { diff --git a/gfx/layers/d3d10/LayerManagerD3D10.h b/gfx/layers/d3d10/LayerManagerD3D10.h index 166c089d498..6c54e6624a9 100644 --- a/gfx/layers/d3d10/LayerManagerD3D10.h +++ b/gfx/layers/d3d10/LayerManagerD3D10.h @@ -46,6 +46,8 @@ #include "gfxContext.h" #include "nsIWidget.h" +#include "ReadbackManagerD3D10.h" + namespace mozilla { namespace layers { @@ -123,6 +125,8 @@ public: virtual already_AddRefed CreateCanvasLayer(); + virtual already_AddRefed CreateReadbackLayer(); + virtual already_AddRefed CreateImageContainer(); virtual already_AddRefed @@ -142,6 +146,8 @@ public: ID3D10Effect *effect() const { return mEffect; } + ReadbackManagerD3D10 *readbackManager(); + void SetViewport(const nsIntSize &aViewport); const nsIntSize &GetViewport() { return mViewport; } @@ -156,6 +162,7 @@ private: void SetupPipeline(); void UpdateRenderTarget(); void VerifyBufferSize(); + void EnsureReadbackManager(); void Render(); @@ -164,6 +171,7 @@ private: nsRefPtr mEffect; nsRefPtr mInputLayout; nsRefPtr mVertexBuffer; + nsRefPtr mReadbackManager; nsRefPtr mRTView; diff --git a/gfx/layers/d3d10/ReadbackLayerD3D10.h b/gfx/layers/d3d10/ReadbackLayerD3D10.h new file mode 100644 index 00000000000..4e539c89d37 --- /dev/null +++ b/gfx/layers/d3d10/ReadbackLayerD3D10.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * 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 GFX_READBACKLAYERD3D10_H +#define GFX_READBACKLAYERD3D10_H + +#include "LayerManagerD3D10.h" +#include "ReadbackLayer.h" + +namespace mozilla { +namespace layers { + +class THEBES_API ReadbackLayerD3D10 : + public ReadbackLayer, + public LayerD3D10 +{ +public: + ReadbackLayerD3D10(LayerManagerD3D10 *aManager) + : ReadbackLayer(aManager, NULL), + LayerD3D10(aManager) + { + mImplData = static_cast(this); + } + + virtual Layer* GetLayer() { return this; } + virtual void RenderLayer() {} +}; + +} /* layers */ +} /* mozilla */ +#endif /* GFX_READBACKLAYERD3D10_H */ diff --git a/gfx/layers/d3d10/ReadbackManagerD3D10.cpp b/gfx/layers/d3d10/ReadbackManagerD3D10.cpp new file mode 100644 index 00000000000..08748e9d347 --- /dev/null +++ b/gfx/layers/d3d10/ReadbackManagerD3D10.cpp @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * 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 "ReadbackManagerD3D10.h" +#include "ReadbackProcessor.h" + +#include "nsIThread.h" +#include "nsThreadUtils.h" +#include "gfxImageSurface.h" + +namespace mozilla { +namespace layers { + +// Structure that contains the information required to execute a readback task, +// the only member accessed off the main thread here is mReadbackTexture. Since +// mLayer may be released only on the main thread this object should always be +// destroyed on the main thread! +struct ReadbackTask { + // The texture that we copied the contents of the thebeslayer to. + nsRefPtr mReadbackTexture; + // This exists purely to keep the ReadbackLayer alive for the lifetime of + // mUpdate. Note that this addref and release should occur -solely- on the + // main thread. + nsRefPtr mLayer; + ReadbackProcessor::Update mUpdate; + // The origin in ThebesLayer coordinates of mReadbackTexture. + gfxPoint mOrigin; + // mLayer->GetBackgroundOffset() when the task is created. We have + // to save this in the ReadbackTask because it might change before + // the update is delivered to the readback sink. + nsIntPoint mBackgroundOffset; +}; + +// This class is created and dispatched from the Readback thread but it must be +// destroyed by the main thread. +class ReadbackResultWriter : public nsIRunnable +{ + NS_DECL_ISUPPORTS +public: + ReadbackResultWriter(ReadbackTask *aTask) : mTask(aTask) {} + + NS_IMETHODIMP Run() + { + ReadbackProcessor::Update *update = &mTask->mUpdate; + + if (!update->mLayer->GetSink()) { + // This can happen when a plugin is destroyed. + return NS_OK; + } + + nsIntPoint offset = mTask->mBackgroundOffset; + + D3D10_TEXTURE2D_DESC desc; + mTask->mReadbackTexture->GetDesc(&desc); + + D3D10_MAPPED_TEXTURE2D mappedTex; + // We know this map will immediately succeed, as we've already mapped this + // copied data on our task thread. + HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex); + + if (FAILED(hr)) { + // If this fails we're never going to get our ThebesLayer content. + update->mLayer->GetSink()->SetUnknown(update->mSequenceCounter); + return NS_OK; + } + + nsRefPtr sourceSurface = + new gfxImageSurface((unsigned char*)mappedTex.pData, + gfxIntSize(desc.Width, desc.Height), + mappedTex.RowPitch, + gfxASurface::ImageFormatRGB24); + + nsRefPtr ctx = + update->mLayer->GetSink()->BeginUpdate(update->mUpdateRect + offset, + update->mSequenceCounter); + + if (ctx) { + ctx->Translate(gfxPoint(offset.x, offset.y)); + ctx->SetSource(sourceSurface, gfxPoint(mTask->mOrigin.x, + mTask->mOrigin.y)); + ctx->Paint(); + + update->mLayer->GetSink()->EndUpdate(ctx, update->mUpdateRect + offset); + } + + mTask->mReadbackTexture->Unmap(0); + + return NS_OK; + } + +private: + nsAutoPtr mTask; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(ReadbackResultWriter, nsIRunnable) + +DWORD WINAPI StartTaskThread(void *aManager) +{ + static_cast(aManager)->ProcessTasks(); + + return 0; +} + +ReadbackManagerD3D10::ReadbackManagerD3D10() + : mRefCnt(0) +{ + ::InitializeCriticalSection(&mTaskMutex); + mShutdownEvent = ::CreateEventA(NULL, FALSE, FALSE, "ReadbackShutdownEvent"); + mTaskSemaphore = ::CreateSemaphoreA(NULL, 0, 1000000, "ReadbackTaskSemaphore"); + mTaskThread = ::CreateThread(NULL, 0, StartTaskThread, this, 0, 0); +} + +ReadbackManagerD3D10::~ReadbackManagerD3D10() +{ + ::SetEvent(mShutdownEvent); + + // This shouldn't take longer than 5 seconds, if it does we're going to choose + // to leak the thread and its synchronisation in favor of crashing or freezing + DWORD result = ::WaitForSingleObject(mTaskThread, 5000); + if (result != WAIT_TIMEOUT) { + ::DeleteCriticalSection(&mTaskMutex); + ::CloseHandle(mShutdownEvent); + ::CloseHandle(mTaskSemaphore); + ::CloseHandle(mTaskThread); + } else { + NS_WARNING("ReadbackManager: Task thread did not shutdown in 5 seconds. Leaking."); + } +} + +void +ReadbackManagerD3D10::PostTask(ID3D10Texture2D *aTexture, void *aUpdate, const gfxPoint &aOrigin) +{ + ReadbackTask *task = new ReadbackTask; + task->mReadbackTexture = aTexture; + task->mUpdate = *static_cast(aUpdate); + task->mOrigin = aOrigin; + task->mLayer = task->mUpdate.mLayer; + task->mBackgroundOffset = task->mLayer->GetBackgroundLayerOffset(); + + ::EnterCriticalSection(&mTaskMutex); + mPendingReadbackTasks.AppendElement(task); + ::LeaveCriticalSection(&mTaskMutex); + + ::ReleaseSemaphore(mTaskSemaphore, 1, NULL); +} + +HRESULT +ReadbackManagerD3D10::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) { + return E_POINTER; + } + + if (riid == IID_IUnknown) { + *ppvObject = this; + } else { + return E_NOINTERFACE; + } + + return S_OK; +} + +ULONG +ReadbackManagerD3D10::AddRef() +{ + NS_ASSERTION(NS_IsMainThread(), + "ReadbackManagerD3D10 should only be refcounted on main thread."); + return ++mRefCnt; +} + +ULONG +ReadbackManagerD3D10::Release() +{ + NS_ASSERTION(NS_IsMainThread(), + "ReadbackManagerD3D10 should only be refcounted on main thread."); + ULONG newRefCnt = --mRefCnt; + if (!newRefCnt) { + mRefCnt++; + delete this; + } + return newRefCnt; +} + +void +ReadbackManagerD3D10::ProcessTasks() +{ + HANDLE handles[] = { mTaskSemaphore, mShutdownEvent }; + + while (true) { + DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); + if (result != WAIT_OBJECT_0) { + return; + } + + ::EnterCriticalSection(&mTaskMutex); + ReadbackTask *nextReadbackTask = mPendingReadbackTasks[0].forget(); + mPendingReadbackTasks.RemoveElementAt(0); + ::LeaveCriticalSection(&mTaskMutex); + + // We want to block here until the texture contents are available, the + // easiest thing is to simply map and unmap. + D3D10_MAPPED_TEXTURE2D mappedTex; + nextReadbackTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex); + nextReadbackTask->mReadbackTexture->Unmap(0); + + // We can only send the update to the sink on the main thread, so post an + // event there to do so. Ownership of the task is passed from + // mPendingReadbackTasks to ReadbackResultWriter here. + nsCOMPtr thread = do_GetMainThread(); + thread->Dispatch(new ReadbackResultWriter(nextReadbackTask), + nsIEventTarget::DISPATCH_NORMAL); + } +} + +} +} diff --git a/gfx/layers/d3d10/ReadbackManagerD3D10.h b/gfx/layers/d3d10/ReadbackManagerD3D10.h new file mode 100644 index 00000000000..3a6a168be73 --- /dev/null +++ b/gfx/layers/d3d10/ReadbackManagerD3D10.h @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * 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 GFX_READBACKMANAGERD3D10_H +#define GFX_READBACKMANAGERD3D10_H + +#include +#include + +#include "nsTArray.h" +#include "nsAutoPtr.h" +#include "gfxPoint.h" + +namespace mozilla { +namespace layers { + +DWORD WINAPI StartTaskThread(void *aManager); + +struct ReadbackTask; + +class ReadbackManagerD3D10 : public IUnknown +{ +public: + ReadbackManagerD3D10(); + ~ReadbackManagerD3D10(); + + /** + * Tell the readback manager to post a readback task. + * + * @param aTexture D3D10_USAGE_STAGING texture that will contain the data that + * was readback. + * @param aUpdate ReadbackProcessor::Update object. This is a void pointer + * since we cannot forward declare a nested class, and do not + * export ReadbackProcessor.h + * @param aOrigin Origin of the aTexture surface in the ThebesLayer + * coordinate system. + */ + void PostTask(ID3D10Texture2D *aTexture, void *aUpdate, const gfxPoint &aOrigin); + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, + void **ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(void); + virtual ULONG STDMETHODCALLTYPE Release(void); + +private: + friend DWORD WINAPI StartTaskThread(void *aManager); + + void ProcessTasks(); + + // The invariant maintained by |mTaskSemaphore| is that the readback thread + // will awaken from WaitForMultipleObjects() at least once per readback + // task enqueued by the main thread. Since the readback thread processes + // exactly one task per wakeup (with one exception), no tasks are lost. The + // exception is when the readback thread is shut down, which orphans the + // remaining tasks, on purpose. + HANDLE mTaskSemaphore; + // Event signaled when the task thread should shutdown + HANDLE mShutdownEvent; + // Handle to the task thread + HANDLE mTaskThread; + + // FiFo list of readback tasks that are to be executed. Access is synchronized + // by mTaskMutex. + CRITICAL_SECTION mTaskMutex; + nsTArray> mPendingReadbackTasks; + + ULONG mRefCnt; +}; + +} +} + +#endif /* GFX_READBACKMANAGERD3D10_H */ \ No newline at end of file diff --git a/gfx/layers/d3d10/ThebesLayerD3D10.cpp b/gfx/layers/d3d10/ThebesLayerD3D10.cpp index 6ee6e05931a..5c2ddd67aa9 100644 --- a/gfx/layers/d3d10/ThebesLayerD3D10.cpp +++ b/gfx/layers/d3d10/ThebesLayerD3D10.cpp @@ -45,6 +45,8 @@ #include "gfxTeeSurface.h" #include "gfxUtils.h" +#include "ReadbackLayer.h" +#include "ReadbackProcessor.h" namespace mozilla { namespace layers { @@ -168,7 +170,7 @@ ThebesLayerD3D10::RenderLayer() } void -ThebesLayerD3D10::Validate() +ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback) { if (mVisibleRegion.IsEmpty()) { return; @@ -191,6 +193,12 @@ ThebesLayerD3D10::Validate() mTextureOnWhite = nsnull; } + nsTArray readbackUpdates; + nsIntRegion readbackRegion; + if (aReadback && UsedForReadback()) { + aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion); + } + nsIntRect visibleRect = mVisibleRegion.GetBounds(); if (mTexture) { @@ -264,6 +272,23 @@ ThebesLayerD3D10::Validate() DrawRegion(region, mode); + if (readbackUpdates.Length() > 0) { + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, + visibleRect.width, visibleRect.height, + 1, 1, 0, D3D10_USAGE_STAGING, + D3D10_CPU_ACCESS_READ); + + nsRefPtr readbackTexture; + device()->CreateTexture2D(&desc, NULL, getter_AddRefs(readbackTexture)); + device()->CopyResource(readbackTexture, mTexture); + + for (int i = 0; i < readbackUpdates.Length(); i++) { + mD3DManager->readbackManager()->PostTask(readbackTexture, + &readbackUpdates[i], + gfxPoint(visibleRect.x, visibleRect.y)); + } + } + mValidRegion = mVisibleRegion; } } diff --git a/gfx/layers/d3d10/ThebesLayerD3D10.h b/gfx/layers/d3d10/ThebesLayerD3D10.h index 15d1ebf367f..7fa2d6ea646 100644 --- a/gfx/layers/d3d10/ThebesLayerD3D10.h +++ b/gfx/layers/d3d10/ThebesLayerD3D10.h @@ -51,13 +51,15 @@ public: ThebesLayerD3D10(LayerManagerD3D10 *aManager); virtual ~ThebesLayerD3D10(); + void Validate(ReadbackProcessor *aReadback); + /* ThebesLayer implementation */ void InvalidateRegion(const nsIntRegion& aRegion); /* LayerD3D10 implementation */ virtual Layer* GetLayer(); virtual void RenderLayer(); - virtual void Validate(); + virtual void Validate() { Validate(nsnull); } virtual void LayerManagerDestroyed(); private: diff --git a/gfx/layers/d3d9/CanvasLayerD3D9.h b/gfx/layers/d3d9/CanvasLayerD3D9.h index 9434a39c884..194f6e14876 100644 --- a/gfx/layers/d3d9/CanvasLayerD3D9.h +++ b/gfx/layers/d3d9/CanvasLayerD3D9.h @@ -36,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef GFX_CANVASLAYEROGL_H -#define GFX_CANVASLAYEROGL_H +#ifndef GFX_CANVASLAYERD3D9_H +#define GFX_CANVASLAYERD3D9_H #include "LayerManagerD3D9.h" #include "GLContext.h" diff --git a/gfx/layers/d3d9/ContainerLayerD3D9.cpp b/gfx/layers/d3d9/ContainerLayerD3D9.cpp index f347170ce67..c98046b1e5d 100644 --- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp +++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp @@ -38,6 +38,8 @@ #include "ContainerLayerD3D9.h" #include "gfxUtils.h" #include "nsRect.h" +#include "ThebesLayerD3D9.h" +#include "ReadbackProcessor.h" namespace mozilla { namespace layers { @@ -71,6 +73,7 @@ ContainerLayerD3D9::InsertAfter(Layer* aChild, Layer* aAfter) mLastChild = aChild; } NS_ADDREF(aChild); + DidInsertChild(aChild); return; } for (Layer *child = GetFirstChild(); @@ -86,6 +89,7 @@ ContainerLayerD3D9::InsertAfter(Layer* aChild, Layer* aAfter) } aChild->SetPrevSibling(child); NS_ADDREF(aChild); + DidInsertChild(aChild); return; } } @@ -105,6 +109,7 @@ ContainerLayerD3D9::RemoveChild(Layer *aChild) aChild->SetNextSibling(nsnull); aChild->SetPrevSibling(nsnull); aChild->SetParent(nsnull); + DidRemoveChild(aChild); NS_RELEASE(aChild); return; } @@ -122,6 +127,7 @@ ContainerLayerD3D9::RemoveChild(Layer *aChild) child->SetNextSibling(nsnull); child->SetPrevSibling(nsnull); child->SetParent(nsnull); + DidRemoveChild(aChild); NS_RELEASE(aChild); return; } @@ -175,6 +181,9 @@ ContainerLayerD3D9::RenderLayer() device()->GetScissorRect(&containerClipRect); + ReadbackProcessor readback; + readback.BuildUpdates(this); + nsIntRect visibleRect = mVisibleRegion.GetBounds(); PRBool useIntermediate = UseIntermediateSurface(); @@ -315,7 +324,11 @@ ContainerLayerD3D9::RenderLayer() device()->SetScissorRect(&r); } - layerToRender->RenderLayer(); + if (layerToRender->GetLayer()->GetType() == TYPE_THEBES) { + static_cast(layerToRender)->RenderThebesLayer(&readback); + } else { + layerToRender->RenderLayer(); + } if (clipRect && !useIntermediate) { // In this situation we've set a new scissor rect and we will continue diff --git a/gfx/layers/d3d9/LayerManagerD3D9.cpp b/gfx/layers/d3d9/LayerManagerD3D9.cpp index 07a3abaab26..324693d556e 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.cpp +++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp @@ -42,6 +42,7 @@ #include "ImageLayerD3D9.h" #include "ColorLayerD3D9.h" #include "CanvasLayerD3D9.h" +#include "ReadbackLayerD3D9.h" #include "gfxWindowsPlatform.h" #include "nsIGfxInfo.h" #include "nsServiceManagerUtils.h" @@ -228,6 +229,13 @@ LayerManagerD3D9::CreateCanvasLayer() return layer.forget(); } +already_AddRefed +LayerManagerD3D9::CreateReadbackLayer() +{ + nsRefPtr layer = new ReadbackLayerD3D9(this); + return layer.forget(); +} + already_AddRefed LayerManagerD3D9::CreateImageContainer() { diff --git a/gfx/layers/d3d9/LayerManagerD3D9.h b/gfx/layers/d3d9/LayerManagerD3D9.h index b9374a68d61..7db39487518 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.h +++ b/gfx/layers/d3d9/LayerManagerD3D9.h @@ -149,6 +149,8 @@ public: virtual already_AddRefed CreateCanvasLayer(); + virtual already_AddRefed CreateReadbackLayer(); + virtual already_AddRefed CreateImageContainer(); virtual LayersBackend GetBackendType() { return LAYERS_D3D9; } diff --git a/gfx/layers/d3d9/ReadbackLayerD3D9.h b/gfx/layers/d3d9/ReadbackLayerD3D9.h new file mode 100644 index 00000000000..b345722dd5b --- /dev/null +++ b/gfx/layers/d3d9/ReadbackLayerD3D9.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Robert O'Callahan + * + * 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 GFX_READBACKLAYERD3D9_H +#define GFX_READBACKLAYERD3D9_H + +#include "LayerManagerD3D9.h" +#include "ReadbackLayer.h" + +namespace mozilla { +namespace layers { + +class THEBES_API ReadbackLayerD3D9 : + public ReadbackLayer, + public LayerD3D9 +{ +public: + ReadbackLayerD3D9(LayerManagerD3D9 *aManager) + : ReadbackLayer(aManager, NULL), + LayerD3D9(aManager) + { + mImplData = static_cast(this); + } + + virtual Layer* GetLayer() { return this; } + virtual void RenderLayer() {} +}; + +} /* layers */ +} /* mozilla */ +#endif /* GFX_READBACKLAYERD3D9_H */ diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.cpp b/gfx/layers/d3d9/ThebesLayerD3D9.cpp index 0cff97fc079..284bf4b1f05 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp +++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp @@ -41,6 +41,7 @@ #include "gfxWindowsPlatform.h" #include "gfxTeeSurface.h" #include "gfxUtils.h" +#include "ReadbackProcessor.h" namespace mozilla { namespace layers { @@ -201,7 +202,7 @@ ThebesLayerD3D9::RenderVisibleRegion() } void -ThebesLayerD3D9::RenderLayer() +ThebesLayerD3D9::RenderThebesLayer(ReadbackProcessor* aReadback) { if (mVisibleRegion.IsEmpty()) { return; @@ -219,22 +220,30 @@ ThebesLayerD3D9::RenderLayer() return; } - if (!mValidRegion.IsEqual(mVisibleRegion)) { + nsTArray readbackUpdates; + nsIntRegion readbackRegion; + if (aReadback && UsedForReadback()) { + aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion); + } + + // Because updates to D3D9 ThebesLayers are rendered with the CPU, we don't + // have to do readback from D3D9 surfaces. Instead we make sure that any area + // needed for readback is included in the drawRegion we ask layout to render. + // Then the readback areas we need can be copied out of the temporary + // destinationSurface in DrawRegion. + nsIntRegion drawRegion; + drawRegion.Sub(mVisibleRegion, mValidRegion); + drawRegion.Or(drawRegion, readbackRegion); + // NS_ASSERTION(mVisibleRegion.Contains(region), "Bad readback region!"); + + if (!drawRegion.IsEmpty()) { LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); if (!cbInfo.Callback) { NS_ERROR("D3D9 should never need to update ThebesLayers in an empty transaction"); return; } - /* We use the bounds of the visible region because we draw the bounds of - * this region when we draw this entire texture. We have to make sure that - * the areas that aren't filled with content get their background drawn. - * This is an issue for opaque surfaces, which otherwise won't get their - * background painted. - */ - nsIntRegion region; - region.Sub(mVisibleRegion, mValidRegion); - DrawRegion(region, mode); + DrawRegion(drawRegion, mode, readbackUpdates); mValidRegion = mVisibleRegion; } @@ -407,7 +416,8 @@ FillSurface(gfxASurface* aSurface, const nsIntRegion& aRegion, } void -ThebesLayerD3D9::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode) +ThebesLayerD3D9::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode, + const nsTArray& aReadbackUpdates) { HRESULT hr; nsIntRect visibleRect = mVisibleRegion.GetBounds(); @@ -480,6 +490,22 @@ ThebesLayerD3D9::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode) LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); + for (PRUint32 i = 0; i < aReadbackUpdates.Length(); ++i) { + NS_ASSERTION(aMode == SURFACE_OPAQUE, + "Transparent surfaces should not be used for readback"); + const ReadbackProcessor::Update& update = aReadbackUpdates[i]; + nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset(); + nsRefPtr ctx = + update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset, + update.mSequenceCounter); + if (ctx) { + ctx->Translate(gfxPoint(offset.x, offset.y)); + ctx->SetSource(destinationSurface, gfxPoint(bounds.x, bounds.y)); + ctx->Paint(); + update.mLayer->GetSink()->EndUpdate(ctx, update.mUpdateRect + offset); + } + } + nsAutoTArray srcTextures; nsAutoTArray destTextures; switch (aMode) diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.h b/gfx/layers/d3d9/ThebesLayerD3D9.h index fd11e8d0eaf..7146d7721db 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.h +++ b/gfx/layers/d3d9/ThebesLayerD3D9.h @@ -41,10 +41,13 @@ #include "Layers.h" #include "LayerManagerD3D9.h" #include "gfxImageSurface.h" +#include "ReadbackProcessor.h" namespace mozilla { namespace layers { +class ReadbackProcessor; + class ThebesLayerD3D9 : public ThebesLayer, public LayerD3D9 { @@ -58,10 +61,12 @@ public: /* LayerD3D9 implementation */ Layer* GetLayer(); virtual PRBool IsEmpty(); - virtual void RenderLayer(); + virtual void RenderLayer() { RenderThebesLayer(nsnull); } virtual void CleanResources(); virtual void LayerManagerDestroyed(); + void RenderThebesLayer(ReadbackProcessor* aReadback); + private: /* * D3D9 texture @@ -96,7 +101,8 @@ private: void RenderVisibleRegion(); /* Have a region of our layer drawn */ - void DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode); + void DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode, + const nsTArray& aReadbackUpdates); /* Create a new texture */ void CreateNewTextures(const gfxIntSize &aSize, SurfaceMode aMode); diff --git a/gfx/layers/opengl/ContainerLayerOGL.cpp b/gfx/layers/opengl/ContainerLayerOGL.cpp index b524340f856..1c311f1e5d2 100644 --- a/gfx/layers/opengl/ContainerLayerOGL.cpp +++ b/gfx/layers/opengl/ContainerLayerOGL.cpp @@ -57,6 +57,7 @@ ContainerInsertAfter(Container* aContainer, Layer* aChild, Layer* aAfter) aContainer->mLastChild = aChild; } NS_ADDREF(aChild); + aContainer->DidInsertChild(aChild); return; } for (Layer *child = aContainer->GetFirstChild(); @@ -72,6 +73,7 @@ ContainerInsertAfter(Container* aContainer, Layer* aChild, Layer* aAfter) } aChild->SetPrevSibling(child); NS_ADDREF(aChild); + aContainer->DidInsertChild(aChild); return; } } @@ -92,6 +94,7 @@ ContainerRemoveChild(Container* aContainer, Layer* aChild) aChild->SetNextSibling(nsnull); aChild->SetPrevSibling(nsnull); aChild->SetParent(nsnull); + aContainer->DidRemoveChild(aChild); NS_RELEASE(aChild); return; } @@ -109,6 +112,7 @@ ContainerRemoveChild(Container* aContainer, Layer* aChild) child->SetNextSibling(nsnull); child->SetPrevSibling(nsnull); child->SetParent(nsnull); + aContainer->DidRemoveChild(aChild); NS_RELEASE(aChild); return; } diff --git a/gfx/thebes/gfxAlphaRecovery.h b/gfx/thebes/gfxAlphaRecovery.h index 04e1c24f6a7..c17de1c23ee 100644 --- a/gfx/thebes/gfxAlphaRecovery.h +++ b/gfx/thebes/gfxAlphaRecovery.h @@ -41,6 +41,7 @@ #include "gfxContext.h" #include "gfxImageSurface.h" #include "mozilla/SSE.h" +#include "nsRect.h" class THEBES_API gfxAlphaRecovery { public: @@ -51,6 +52,16 @@ public: gfxFloat r, g, b; }; + /** + * Some SIMD fast-paths only can be taken if the relative + * byte-alignment of images' pointers and strides meets certain + * criteria. Aligning image pointers and strides by + * |GoodAlignmentLog2()| below will ensure that fast-paths aren't + * skipped because of misalignment. Fast-paths may still be taken + * even if GoodAlignmentLog2() is not met, in some conditions. + */ + static PRUint32 GoodAlignmentLog2() { return 4; /* for SSE2 */ } + /* Given two surfaces of equal size with the same rendering, one onto a * black background and the other onto white, recovers alpha values from * the difference and sets the alpha values on the black surface. @@ -62,12 +73,30 @@ public: Analysis *analysis = nsnull); #ifdef MOZILLA_MAY_SUPPORT_SSE2 - /* This does the save as the previous function, but uses SSE2 + /* This does the same as the previous function, but uses SSE2 * optimizations. Usually this should not be called directly. Be sure to * check mozilla::supports_sse2() before calling this function. */ static PRBool RecoverAlphaSSE2 (gfxImageSurface *blackSurface, const gfxImageSurface *whiteSurface); + + /** + * A common use-case for alpha recovery is to paint into a + * temporary "white image", then paint onto a subrect of the + * surface, the "black image", into which alpha-recovered pixels + * are eventually to be written. This function returns a rect + * aligned so that recovering alpha for that rect will hit SIMD + * fast-paths, if possible. It's not always possible to align + * |aRect| so that fast-paths will be taken. + * + * The returned rect is always a superset of |aRect|. + */ + static nsIntRect AlignRectForSubimageRecovery(const nsIntRect& aRect, + gfxImageSurface* aSurface); +#else + static nsIntRect AlignRectForSubimageRecovery(const nsIntRect& aRect, + gfxImageSurface*) + { return aRect; } #endif /** from cairo-xlib-utils.c, modified */ diff --git a/gfx/thebes/gfxAlphaRecoverySSE2.cpp b/gfx/thebes/gfxAlphaRecoverySSE2.cpp index a5fc19a0633..afc66fec6d9 100644 --- a/gfx/thebes/gfxAlphaRecoverySSE2.cpp +++ b/gfx/thebes/gfxAlphaRecoverySSE2.cpp @@ -160,3 +160,99 @@ gfxAlphaRecovery::RecoverAlphaSSE2(gfxImageSurface* blackSurf, return PR_TRUE; } + +static PRInt32 +ByteAlignment(PRInt32 aAlignToLog2, PRInt32 aX, PRInt32 aY=0, PRInt32 aStride=1) +{ + return (aX + aStride * aY) & ((1 << aAlignToLog2) - 1); +} + +/*static*/ nsIntRect +gfxAlphaRecovery::AlignRectForSubimageRecovery(const nsIntRect& aRect, + gfxImageSurface* aSurface) +{ + NS_ASSERTION(gfxASurface::ImageFormatARGB32 == aSurface->Format(), + "Thebes grew support for non-ARGB32 COLOR_ALPHA?"); + static const PRInt32 kByteAlignLog2 = GoodAlignmentLog2(); + static const PRInt32 bpp = 4; + static const PRInt32 pixPerAlign = (1 << kByteAlignLog2) / bpp; + // + // We're going to create a subimage of the surface with size + // for alpha recovery, and want a SIMD fast-path. The + // rect /needs/ to be redrawn, but it might not be + // properly aligned for SIMD. So we want to find a rect that's a superset of what needs to be redrawn but is + // properly aligned. Proper alignment is + // + // BPP * (x' + y' * sw) \cong 0 (mod ALIGN) + // BPP * w' \cong BPP * sw (mod ALIGN) + // + // (We assume the pixel at surface <0,0> is already ALIGN'd.) + // That rect (obviously) has to fit within the surface bounds, and + // we should also minimize the extra pixels redrawn only for + // alignment's sake. So we also want + // + // minimize + // 0 <= x' <= x + // 0 <= y' <= y + // w <= w' <= sw + // h <= h' <= sh + // + // This is a messy integer non-linear programming problem, except + // ... we can assume that ALIGN/BPP is a very small constant. So, + // brute force is viable. The algorithm below will find a + // solution if one exists, but isn't guaranteed to find the + // minimum solution. (For SSE2, ALIGN/BPP = 4, so it'll do at + // most 64 iterations below). In what's likely the common case, + // an already-aligned rectangle, it only needs 1 iteration. + // + // Is this alignment worth doing? Recovering alpha will take work + // proportional to w*h (assuming alpha recovery computation isn't + // memory bound). This analysis can lead to O(w+h) extra work + // (with small constants). In exchange, we expect to shave off a + // ALIGN/BPP constant by using SIMD-ized alpha recovery. So as + // w*h diverges from w+h, the win factor approaches ALIGN/BPP. We + // only really care about the w*h >> w+h case anyway; others + // should be fast enough even with the overhead. (Unless the cost + // of repainting the expanded rect is high, but in that case + // SIMD-ized alpha recovery won't make a difference so this code + // shouldn't be called.) + // + gfxIntSize surfaceSize = aSurface->GetSize(); + const PRInt32 stride = bpp * surfaceSize.width; + if (stride != aSurface->Stride()) { + NS_WARNING("Unexpected stride, falling back on slow alpha recovery"); + return aRect; + } + + const PRInt32 x = aRect.x, y = aRect.y, w = aRect.width, h = aRect.height; + const PRInt32 sw = surfaceSize.width, sh = surfaceSize.height; + const PRInt32 strideAlign = ByteAlignment(kByteAlignLog2, stride); + + PRInt32 dx, dy, dw; + // The outer two loops search for an aligned top-left pixel + for (dy = 0; (dy < pixPerAlign) && (y - dy >= 0) && (h + dy <= sh); ++dy) { + for (dx = 0; (dx < pixPerAlign) && (x - dx >= 0); ++dx) { + if (0 != ByteAlignment(kByteAlignLog2, + bpp * (x - dx), y - dy, stride)) { + continue; + } + // The inner searches for an aligned stride + for (dw = 0; (dw < pixPerAlign) && (w + dw + dx <= sw); ++dw) { + if (strideAlign == ByteAlignment(kByteAlignLog2, + bpp * (w + dw + dx))) { + goto FOUND_SOLUTION; + } + } + } + } + + // Didn't find a solution. + return aRect; + +FOUND_SOLUTION: + nsIntRect solution = nsIntRect(x - dx, y - dy, w + dw + dx, h + dy); + NS_ABORT_IF_FALSE(nsIntRect(0,0, sw,sh).Contains(solution), + "'Solution' extends outside surface bounds!"); + return solution; +} diff --git a/gfx/thebes/gfxColor.h b/gfx/thebes/gfxColor.h index 6dbe76fe469..be6fd186049 100644 --- a/gfx/thebes/gfxColor.h +++ b/gfx/thebes/gfxColor.h @@ -243,6 +243,10 @@ struct THEBES_API gfxRGBA { { return r == other.r && g == other.g && b == other.b && a == other.a; } + bool operator!=(const gfxRGBA& other) const + { + return !(*this == other); + } /** * Returns this color value as a packed 32-bit integer. This reconstructs diff --git a/gfx/thebes/gfxImageSurface.cpp b/gfx/thebes/gfxImageSurface.cpp index 66036fbc4a1..f24e6b6d09b 100644 --- a/gfx/thebes/gfxImageSurface.cpp +++ b/gfx/thebes/gfxImageSurface.cpp @@ -37,6 +37,7 @@ #include "prmem.h" +#include "gfxAlphaRecovery.h" #include "gfxImageSurface.h" #include "cairo.h" @@ -96,6 +97,24 @@ gfxImageSurface::InitWithData(unsigned char *aData, const gfxIntSize& aSize, Init(surface); } +static void* +TryAllocAlignedBytes(size_t aSize) +{ + // Use fallible allocators here +#if defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_JEMALLOC_POSIX_MEMALIGN) + void* ptr; + // Try to align for fast alpha recovery. This should only help + // cairo too, can't hurt. + return moz_posix_memalign(&ptr, + 1 << gfxAlphaRecovery::GoodAlignmentLog2(), + aSize) ? + nsnull : ptr; +#else + // Oh well, hope that luck is with us in the allocator + return moz_malloc(aSize); +#endif +} + gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format) : mSize(size), mOwnsData(PR_FALSE), mData(nsnull), mFormat(format) { @@ -107,8 +126,9 @@ gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format) // if we have a zero-sized surface, just leave mData nsnull if (mSize.height * mStride > 0) { - // Use the fallible allocator here - mData = (unsigned char *) moz_malloc(mSize.height * mStride); + // This can fail to allocate memory aligned as we requested, + // or it can fail to allocate any memory at all. + mData = (unsigned char *) TryAllocAlignedBytes(mSize.height * mStride); if (!mData) return; memset(mData, 0, mSize.height * mStride); diff --git a/gfx/thebes/gfxPoint.h b/gfx/thebes/gfxPoint.h index 93b585e9525..059e45a5c13 100644 --- a/gfx/thebes/gfxPoint.h +++ b/gfx/thebes/gfxPoint.h @@ -60,6 +60,13 @@ struct THEBES_API gfxIntSize { int operator!=(const gfxIntSize& s) const { return ((width != s.width) || (height != s.height)); } + bool operator<(const gfxIntSize& s) const { + return (operator<=(s) && + (width < s.width || height < s.height)); + } + bool operator<=(const gfxIntSize& s) const { + return (width <= s.width) && (height <= s.height); + } gfxIntSize operator+(const gfxIntSize& s) const { return gfxIntSize(width + s.width, height + s.height); } diff --git a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp index 10ccca21fb3..765b7b49306 100644 --- a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp +++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp @@ -323,11 +323,18 @@ mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL tmp.Append(uriStr); uriStr = tmp; - } - - rv = NS_OpenURI(getter_AddRefs(instream), uri, serv, - nsnull, nsnull, nsIRequest::LOAD_NORMAL, - getter_AddRefs(chan)); + } + + // Instead of calling NS_OpenURI, we create the channel ourselves and call + // SetContentType, to avoid expensive MIME type lookups (bug 632490). + rv = NS_NewChannel(getter_AddRefs(chan), uri, serv, + nsnull, nsnull, nsIRequest::LOAD_NORMAL); + if (NS_SUCCEEDED(rv)) + { + chan->SetContentType(NS_LITERAL_CSTRING("application/javascript")); + rv = chan->Open(getter_AddRefs(instream)); + } + if (NS_FAILED(rv)) { errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSTREAM); diff --git a/layout/base/nsDisplayItemTypes.h b/layout/base/nsDisplayItemTypes.h index cb9f46f0b91..ee1406b2d53 100644 --- a/layout/base/nsDisplayItemTypes.h +++ b/layout/base/nsDisplayItemTypes.h @@ -78,6 +78,7 @@ enum Type { TYPE_PAGE_CONTENT, TYPE_PAGE_SEQUENCE, TYPE_PLUGIN, + TYPE_PLUGIN_READBACK, TYPE_PRINT_PREVIEW_BACKGROUND, TYPE_PRINT_PLUGIN, TYPE_REMOTE, diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 789ad0e31e5..a62a4c1290d 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -375,7 +375,7 @@ nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder, r.And(*aVisibleRegion, GetBounds(aBuilder)); PRBool notUsed; return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, - r.GetBounds(), notUsed); + r.GetBounds(), r.GetBounds(), notUsed); } static nsRegion @@ -397,6 +397,7 @@ PRBool nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, const nsRect& aListVisibleBounds, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { #ifdef DEBUG nsRegion r; @@ -429,7 +430,7 @@ nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, item->mVisibleRect = itemVisible.GetBounds(); PRBool containsRootContentDocBG = PR_FALSE; - if (item->ComputeVisibility(aBuilder, aVisibleRegion, + if (item->ComputeVisibility(aBuilder, aVisibleRegion, aAllowVisibleRegionExpansion, containsRootContentDocBG)) { if (containsRootContentDocBG) { aContainsRootContentDocBG = PR_TRUE; @@ -744,8 +745,11 @@ PRBool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder, itemVisible.And(*aVisibleRegion, bounds); mVisibleRect = itemVisible.GetBounds(); + // When we recompute visibility within layers we don't need to + // expand the visible region for content behind plugins (the plugin + // is not in the layer). PRBool notUsed; - if (!ComputeVisibility(aBuilder, aVisibleRegion, notUsed)) + if (!ComputeVisibility(aBuilder, aVisibleRegion, nsRect(), notUsed)) return PR_FALSE; PRBool forceTransparentBackground; @@ -763,9 +767,11 @@ void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder, PRBool nsDisplaySolidColor::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { PRBool retval = nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG); if (retval && IsRootContentDocBackground()) { aContainsRootContentDocBG = PR_TRUE; @@ -923,9 +929,11 @@ nsDisplayBackground::HitTest(nsDisplayListBuilder* aBuilder, PRBool nsDisplayBackground::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG)) { return PR_FALSE; } @@ -1198,8 +1206,10 @@ nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, PRBool nsDisplayOutline::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG)) { return PR_FALSE; } @@ -1243,8 +1253,10 @@ nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, PRBool nsDisplayBorder::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG)) { return PR_FALSE; } @@ -1342,8 +1354,10 @@ nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder) { PRBool nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG)) { return PR_FALSE; } @@ -1388,8 +1402,10 @@ nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder, PRBool nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG)) { return PR_FALSE; } @@ -1429,9 +1445,11 @@ nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder) { PRBool nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { return mList.ComputeVisibilityForSublist(aBuilder, aVisibleRegion, mVisibleRect, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG); } @@ -1618,6 +1636,7 @@ nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder, PRBool nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { // Our children are translucent so we should not allow them to subtract // area from aVisibleRegion. We do need to find out what is visible under @@ -1630,9 +1649,11 @@ PRBool nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder, // do not pass up the aContainsRootContentDocBG value because anything under // us is not opaque PRBool notUsed; + nsRect allowExpansion; + allowExpansion.IntersectRect(bounds, aAllowVisibleRegionExpansion); return nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren, - notUsed); + allowExpansion, notUsed); } PRBool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { @@ -1708,6 +1729,7 @@ nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder, PRBool nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { nsPresContext* presContext = mFrame->PresContext(); @@ -1722,14 +1744,17 @@ nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRect boundedRect; boundedRect.IntersectRect(childVisibleRegion.GetBounds(), mList.GetBounds(aBuilder)); + nsRect allowExpansion; + allowExpansion.IntersectRect(allowExpansion, boundedRect); PRBool visible = mList.ComputeVisibilityForSublist( - aBuilder, &childVisibleRegion, boundedRect, aContainsRootContentDocBG); + aBuilder, &childVisibleRegion, boundedRect, allowExpansion, aContainsRootContentDocBG); mVisibleRect = boundedRect; return visible; } else { return nsDisplayOwnLayer::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG); } } @@ -1780,13 +1805,17 @@ void nsDisplayClip::Paint(nsDisplayListBuilder* aBuilder, PRBool nsDisplayClip::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { nsRegion clipped; clipped.And(*aVisibleRegion, mClip); nsRegion finalClipped(clipped); + nsRect allowExpansion; + allowExpansion.IntersectRect(mClip, aAllowVisibleRegionExpansion); PRBool anyVisible = nsDisplayWrapList::ComputeVisibility(aBuilder, &finalClipped, + allowExpansion, aContainsRootContentDocBG); nsRegion removed; @@ -1880,13 +1909,14 @@ nsDisplayClipRoundedRect::WrapWithClone(nsDisplayListBuilder* aBuilder, PRBool nsDisplayClipRoundedRect::ComputeVisibility( nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { nsRegion clipped; clipped.And(*aVisibleRegion, mClip); PRBool notUsed; - return nsDisplayWrapList::ComputeVisibility(aBuilder, &clipped, notUsed); + return nsDisplayWrapList::ComputeVisibility(aBuilder, &clipped, nsRect(), notUsed); // FIXME: Remove a *conservative* opaque region from aVisibleRegion // (like in nsDisplayClip::ComputeVisibility). } @@ -1949,6 +1979,7 @@ void nsDisplayZoom::Paint(nsDisplayListBuilder* aBuilder, PRBool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder, nsRegion *aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { // Convert the passed in visible region to our appunits. @@ -1958,9 +1989,12 @@ PRBool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder, nsRect transformedVisibleRect = mVisibleRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD); + nsRect allowExpansion = + aAllowVisibleRegionExpansion.ConvertAppUnitsRoundIn(mParentAPD, mAPD); PRBool retval = mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion, transformedVisibleRect, + allowExpansion, aContainsRootContentDocBG); nsRegion removed; @@ -2158,6 +2192,7 @@ nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder, PRBool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder, nsRegion *aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { /* As we do this, we need to be sure to @@ -2440,6 +2475,7 @@ void nsDisplaySVGEffects::Paint(nsDisplayListBuilder* aBuilder, PRBool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { nsPoint offset = aBuilder->ToReferenceFrame(mEffectsFrame); nsRect dirtyRect = @@ -2453,7 +2489,7 @@ PRBool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRect r; r.IntersectRect(dirtyRect, mList.GetBounds(aBuilder)); PRBool notUsed; - mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r, notUsed); + mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r, nsRect(), notUsed); return PR_TRUE; } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 3db58a21a78..c3ad363b71b 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -705,13 +705,17 @@ public: * this item to the intersection of *aVisibleRegion and this item's bounds. * We rely on that, so this should only be called by * nsDisplayList::ComputeVisibility or nsDisplayItem::RecomputeVisibility. - * + * aAllowVisibleRegionExpansion is a rect where we are allowed to + * expand the visible region and is only used for making sure the + * background behind a plugin is visible. + * * @return PR_TRUE if the item is visible, PR_FALSE if no part of the item * is visible. aContainsRootContentDocBG is set to true if this item contains * the background for the root content document. */ virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { return !mVisibleRect.IsEmpty(); } @@ -993,6 +997,7 @@ public: PRBool ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, const nsRect& aListVisibleBounds, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); /** @@ -1399,6 +1404,7 @@ public: virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); NS_DISPLAY_DECL_NAME("Border", TYPE_BORDER) @@ -1457,6 +1463,7 @@ public: virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); PRBool IsRootContentDocBackground() { @@ -1487,6 +1494,7 @@ public: HitTestState* aState, nsTArray *aOutFrames); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, PRBool* aForceTransparentSurface = nsnull); @@ -1526,6 +1534,7 @@ public: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); NS_DISPLAY_DECL_NAME("BoxShadowOuter", TYPE_BOX_SHADOW_OUTER) @@ -1551,6 +1560,7 @@ public: virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); NS_DISPLAY_DECL_NAME("BoxShadowInner", TYPE_BOX_SHADOW_INNER) @@ -1577,6 +1587,7 @@ public: virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); NS_DISPLAY_DECL_NAME("Outline", TYPE_OUTLINE) }; @@ -1639,6 +1650,7 @@ public: virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { NS_WARNING("This list should already have been flattened!!!"); @@ -1724,6 +1736,7 @@ public: LayerManager* aManager); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem); NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY) @@ -1791,6 +1804,7 @@ public: virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); private: @@ -1822,6 +1836,7 @@ public: virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem); NS_DISPLAY_DECL_NAME("Clip", TYPE_CLIP) @@ -1864,6 +1879,7 @@ public: HitTestState* aState, nsTArray *aOutFrames); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem); NS_DISPLAY_DECL_NAME("ClipRoundedRect", TYPE_CLIP_ROUNDED_RECT) @@ -1905,6 +1921,7 @@ public: HitTestState* aState, nsTArray *aOutFrames); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); NS_DISPLAY_DECL_NAME("Zoom", TYPE_ZOOM) @@ -1940,6 +1957,7 @@ public: virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem); NS_DISPLAY_DECL_NAME("SVGEffects", TYPE_SVG_EFFECTS) @@ -2004,6 +2022,7 @@ public: LayerManager* aManager); virtual PRBool ComputeVisibility(nsDisplayListBuilder *aBuilder, nsRegion *aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); virtual PRBool TryMerge(nsDisplayListBuilder *aBuilder, nsDisplayItem *aItem); diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h index b644cf30ebd..0e921638c48 100644 --- a/layout/generic/nsCanvasFrame.h +++ b/layout/generic/nsCanvasFrame.h @@ -173,10 +173,12 @@ public: virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { PRBool retval = NS_GET_A(mExtraBackgroundColor) > 0 || nsDisplayBackground::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG); if (retval && mFrame->PresContext()->IsRootContentDocument()) { aContainsRootContentDocBG = PR_TRUE; diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 63aeaa78ea4..6ed82ade008 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -163,6 +163,7 @@ enum { XKeyPress = KeyPress }; #include "gfxImageSurface.h" #include "gfxUtils.h" #include "Layers.h" +#include "ReadbackLayer.h" // accessibility support #ifdef ACCESSIBILITY @@ -237,6 +238,8 @@ static PRLogModuleInfo *nsObjectFrameLM = PR_NewLogModule("nsObjectFrame"); #define MAC_CARBON_PLUGINS #endif +using namespace mozilla; +using namespace mozilla::plugins; using namespace mozilla::layers; // special class for handeling DOM context menu events because for @@ -464,6 +467,13 @@ public: // Return true if we set image with valid surface PRBool SetCurrentImage(ImageContainer* aContainer); + // Methods to update the background image we send to async plugins. + // The eventual target of these operations is PluginInstanceParent, + // but it takes several hops to get there. + void SetBackgroundUnknown(); + already_AddRefed BeginUpdateBackground(const nsIntRect& aRect); + void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect); + PRBool UseLayers() { PRBool useAsyncRendering; @@ -474,6 +484,7 @@ public: mPluginWindow->type == NPWindowTypeDrawable)); } + private: // return FALSE if LayerSurface dirty (newly created and don't have valid plugin content yet) PRBool IsUpToDate() @@ -484,6 +495,13 @@ private: mPluginWindow->height); } + already_AddRefed + GetInstance() + { + nsCOMPtr inst = do_QueryInterface(mInstance); + return inst.forget(); + } + void FixUpURLS(const nsString &name, nsAString &value); nsPluginNativeWindow *mPluginWindow; @@ -606,6 +624,53 @@ private: PRPackedBool mWaitingForPaint; }; +class PluginBackgroundSink : public ReadbackSink { +public: + PluginBackgroundSink(nsObjectFrame* aFrame, PRUint64 aStartSequenceNumber) + : mLastSequenceNumber(aStartSequenceNumber), mFrame(aFrame) {} + ~PluginBackgroundSink() + { + if (mFrame) { + mFrame->mBackgroundSink = nsnull; + } + } + + virtual void SetUnknown(PRUint64 aSequenceNumber) + { + if (!AcceptUpdate(aSequenceNumber)) + return; + mFrame->mInstanceOwner->SetBackgroundUnknown(); + } + + virtual already_AddRefed + BeginUpdate(const nsIntRect& aRect, PRUint64 aSequenceNumber) + { + if (!AcceptUpdate(aSequenceNumber)) + return nsnull; + return mFrame->mInstanceOwner->BeginUpdateBackground(aRect); + } + + virtual void EndUpdate(gfxContext* aContext, const nsIntRect& aRect) + { + return mFrame->mInstanceOwner->EndUpdateBackground(aContext, aRect); + } + + void Destroy() { mFrame = nsnull; } + +protected: + PRBool AcceptUpdate(PRUint64 aSequenceNumber) { + if (aSequenceNumber > mLastSequenceNumber && mFrame && + mFrame->mInstanceOwner) { + mLastSequenceNumber = aSequenceNumber; + return PR_TRUE; + } + return PR_FALSE; + } + + PRUint64 mLastSequenceNumber; + nsObjectFrame* mFrame; +}; + // Mac specific code to fix up port position and clip #ifdef XP_MACOSX @@ -651,7 +716,6 @@ NS_IMETHODIMP nsObjectFrame::GetPluginPort(HWND *aPort) #endif #endif - static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID); NS_IMETHODIMP @@ -689,6 +753,10 @@ nsObjectFrame::DestroyFrom(nsIFrame* aDestructRoot) mWidget->Destroy(); } + if (mBackgroundSink) { + mBackgroundSink->Destroy(); + } + nsObjectFrameSuper::DestroyFrom(aDestructRoot); } @@ -1253,19 +1321,48 @@ nsObjectFrame::PaintPrintPlugin(nsIFrame* aFrame, nsIRenderingContext* aCtx, static_cast(aFrame)->PrintPlugin(*aCtx, aDirtyRect); } -nsRect -nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder) -{ - nsRect r = mFrame->GetContentRect() - mFrame->GetPosition() + - ToReferenceFrame(); - if (aBuilder->IsForPluginGeometry()) { - // Return the geometry we want, not the geometry we have (which is based - // on the surface the plugin last gave us) - return r; +class nsDisplayPluginReadback : public nsDisplayItem { +public: + nsDisplayPluginReadback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) + : nsDisplayItem(aBuilder, aFrame) + { + MOZ_COUNT_CTOR(nsDisplayPluginReadback); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayPluginReadback() { + MOZ_COUNT_DTOR(nsDisplayPluginReadback); + } +#endif + + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); + virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, + PRBool& aContainsRootContentDocBG); + + NS_DISPLAY_DECL_NAME("PluginReadback", TYPE_PLUGIN_READBACK) + + virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager) + { + return static_cast(mFrame)->BuildLayer(aBuilder, aManager, this); } - nsObjectFrame* f = static_cast(mFrame); - if (mozilla::LAYER_ACTIVE == f->GetLayerState(aBuilder, nsnull)) { + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager) + { + return LAYER_ACTIVE; + } +}; + +static nsRect +GetDisplayItemBounds(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, nsIFrame* aFrame) +{ + nsRect r = aFrame->GetContentRect() - aFrame->GetPosition() + + aItem->ToReferenceFrame(); + + nsObjectFrame* f = static_cast(aFrame); + if (LAYER_ACTIVE == f->GetLayerState(aBuilder)) { ImageContainer* c = f->GetImageContainer(); if (c) { gfxIntSize size = c->GetCurrentSize(); @@ -1279,6 +1376,38 @@ nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder) return r; } +nsRect +nsDisplayPluginReadback::GetBounds(nsDisplayListBuilder* aBuilder) +{ + return GetDisplayItemBounds(aBuilder, this, mFrame); +} + +PRBool +nsDisplayPluginReadback::ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, + PRBool& aContainsRootContentDocBG) +{ + if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, + aContainsRootContentDocBG)) + return PR_FALSE; + + nsRect expand; + expand.IntersectRect(aAllowVisibleRegionExpansion, GetBounds(aBuilder)); + // *Add* our bounds to the visible region so that stuff underneath us is + // likely to be made visible, so we can use it for a background! This is + // a bit crazy since we normally only subtract from the visible region. + aVisibleRegion->Or(*aVisibleRegion, expand); + return PR_TRUE; +} + +nsRect +nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder) +{ + return GetDisplayItemBounds(aBuilder, this, mFrame); +} + void nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx) @@ -1290,10 +1419,12 @@ nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder, PRBool nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG) { mVisibleRegion.And(*aVisibleRegion, GetBounds(aBuilder)); return nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, + aAllowVisibleRegionExpansion, aContainsRootContentDocBG); } @@ -1456,10 +1587,12 @@ nsObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) { + AddStateBits(NS_OBJECT_NEEDS_SET_IMAGE); + // XXX why are we painting collapsed object frames? if (!IsVisibleOrCollapsedForPainting(aBuilder)) return NS_OK; - + nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists); NS_ENSURE_SUCCESS(rv, rv); @@ -1504,6 +1637,12 @@ nsObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayGeneric(aBuilder, this, PaintPrintPlugin, "PrintPlugin", nsDisplayItem::TYPE_PRINT_PLUGIN)); } else { + if (aBuilder->IsPaintingToWindow() && GetLayerState(aBuilder) == LAYER_ACTIVE) { + rv = replacedContent.AppendNewToTop(new (aBuilder) + nsDisplayPluginReadback(aBuilder, this)); + NS_ENSURE_SUCCESS(rv, rv); + } + rv = replacedContent.AppendNewToTop(new (aBuilder) nsDisplayPlugin(aBuilder, this)); } @@ -1807,14 +1946,44 @@ nsPluginInstanceOwner::SetCurrentImage(ImageContainer* aContainer) return PR_TRUE; } -mozilla::LayerState -nsObjectFrame::GetLayerState(nsDisplayListBuilder* aBuilder, - LayerManager* aManager) +void +nsPluginInstanceOwner::SetBackgroundUnknown() { - if (!mInstanceOwner || !mInstanceOwner->UseLayers()) - return mozilla::LAYER_NONE; + nsCOMPtr inst = GetInstance(); + if (inst) { + inst->SetBackgroundUnknown(); + } +} - return mozilla::LAYER_ACTIVE; +already_AddRefed +nsPluginInstanceOwner::BeginUpdateBackground(const nsIntRect& aRect) +{ + nsIntRect rect = aRect; + nsCOMPtr inst = GetInstance(); + nsRefPtr ctx; + if (inst && + NS_SUCCEEDED(inst->BeginUpdateBackground(&rect, getter_AddRefs(ctx)))) { + return ctx.forget(); + } + return nsnull; +} + +void +nsPluginInstanceOwner::EndUpdateBackground(gfxContext* aContext, + const nsIntRect& aRect) +{ + nsIntRect rect = aRect; + nsCOMPtr inst = GetInstance(); + if (inst) { + inst->EndUpdateBackground(aContext, &rect); + } +} + +LayerState +nsObjectFrame::GetLayerState(nsDisplayListBuilder* aBuilder) +{ + return mInstanceOwner && mInstanceOwner->UseLayers() ? + LAYER_ACTIVE : LAYER_NONE; } already_AddRefed @@ -1833,6 +2002,18 @@ nsObjectFrame::BuildLayer(nsDisplayListBuilder* aBuilder, if (window->width <= 0 || window->height <= 0) return nsnull; + // Create image + nsRefPtr container = GetImageContainer(aManager); + if (!container) + return nsnull; + if (GetStateBits() & NS_OBJECT_NEEDS_SET_IMAGE) { + RemoveStateBits(NS_OBJECT_NEEDS_SET_IMAGE); + if (!mInstanceOwner->SetCurrentImage(container)) { + return nsnull; + } + } + gfxIntSize size = container->GetCurrentSize(); + nsRect area = GetContentRect() + aBuilder->ToReferenceFrame(GetParent()); gfxRect r = nsLayoutUtils::RectToGfxRect(area, PresContext()->AppUnitsPerDevPixel()); // to provide crisper and faster drawing. @@ -1840,41 +2021,55 @@ nsObjectFrame::BuildLayer(nsDisplayListBuilder* aBuilder, nsRefPtr layer = (aBuilder->LayerBuilder()->GetLeafLayerFor(aBuilder, aManager, aItem)); - if (!layer) { - mInstanceOwner->NotifyPaintWaiter(aBuilder); - // Initialize ImageLayer - layer = aManager->CreateImageLayer(); + if (aItem->GetType() == nsDisplayItem::TYPE_PLUGIN) { + if (!layer) { + mInstanceOwner->NotifyPaintWaiter(aBuilder); + // Initialize ImageLayer + layer = aManager->CreateImageLayer(); + if (!layer) + return nsnull; + } + NS_ASSERTION(layer->GetType() == Layer::TYPE_IMAGE, "Bad layer type"); + + ImageLayer* imglayer = static_cast(layer.get()); + imglayer->SetContainer(container); + imglayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this)); + + layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0); + } else { + NS_ASSERTION(aItem->GetType() == nsDisplayItem::TYPE_PLUGIN_READBACK, + "Unknown item type"); + if (!layer) { + layer = aManager->CreateReadbackLayer(); + if (!layer) + return nsnull; + } + NS_ASSERTION(layer->GetType() == Layer::TYPE_READBACK, "Bad layer type"); + + ReadbackLayer* readback = static_cast(layer.get()); + if (readback->GetSize() != nsIntSize(size.width, size.height)) { + // This will destroy any old background sink and notify us that the + // background is now unknown + readback->SetSink(nsnull); + NS_ASSERTION(!mBackgroundSink, "Should have been cleared"); + + readback->SetSize(nsIntSize(size.width, size.height)); + + mBackgroundSink = + new PluginBackgroundSink(this, + readback->AllocateSequenceNumber()); + readback->SetSink(mBackgroundSink); + // The layer has taken ownership of our sink. When either the sink dies + // or the frame dies, the connection from the surviving object is nulled out. + } } - if (!layer) - return nsnull; - - NS_ASSERTION(layer->GetType() == Layer::TYPE_IMAGE, "ObjectFrame works only with ImageLayer"); - // Create image - nsRefPtr container = GetImageContainer(aManager); - if (!container) - return nsnull; - - if (!mInstanceOwner->SetCurrentImage(container)) { - return nsnull; - } - - ImageLayer* imglayer = static_cast(layer.get()); - imglayer->SetContainer(container); - imglayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this)); - - layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0); - // Set a transform on the layer to draw the plugin in the right place gfxMatrix transform; - // Center plugin if layer size != frame rect - r.pos.x += (r.Width() - container->GetCurrentSize().width) / 2; - r.pos.y += (r.Height() - container->GetCurrentSize().height) / 2; transform.Translate(r.pos); layer->SetTransform(gfx3DMatrix::From2D(transform)); - nsRefPtr result = layer.forget(); - return result.forget(); + return layer.forget(); } void @@ -2039,7 +2234,7 @@ nsObjectFrame::PaintPlugin(nsDisplayListBuilder* aBuilder, // double pass render. If this plugin isn't oop, the register window message // will be ignored. NPEvent pluginEvent; - pluginEvent.event = mozilla::plugins::DoublePassRenderingEvent(); + pluginEvent.event = DoublePassRenderingEvent(); pluginEvent.wParam = 0; pluginEvent.lParam = 0; if (pluginEvent.event) diff --git a/layout/generic/nsObjectFrame.h b/layout/generic/nsObjectFrame.h index 05d579d54aa..be4243530fb 100644 --- a/layout/generic/nsObjectFrame.h +++ b/layout/generic/nsObjectFrame.h @@ -62,9 +62,12 @@ class nsIPluginInstance; class nsPresContext; class nsDisplayPlugin; class nsIDOMElement; +class PluginBackgroundSink; #define nsObjectFrameSuper nsFrame +#define NS_OBJECT_NEEDS_SET_IMAGE NS_FRAME_STATE_BIT(31) + class nsObjectFrame : public nsObjectFrameSuper, public nsIObjectFrame, public nsIReflowCallback { @@ -176,12 +179,15 @@ public: virtual PRBool ReflowFinished(); virtual void ReflowCallbackCanceled(); + /** + * Builds either an ImageLayer or a ReadbackLayer, depending on the type + * of aItem (TYPE_PLUGIN or TYPE_PLUGIN_READBACK respectively). + */ already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager, nsDisplayItem* aItem); - virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, - LayerManager* aManager); + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder); ImageContainer* GetImageContainer(LayerManager* aManager = nsnull); @@ -272,6 +278,7 @@ protected: friend class nsPluginInstanceOwner; friend class nsDisplayPlugin; + friend class PluginBackgroundSink; private: @@ -289,6 +296,11 @@ private: nsIView* mInnerView; nsCOMPtr mWidget; nsIntRect mWindowlessRect; + /** + * This is owned by the ReadbackLayer for this nsObjectFrame. It is + * automatically cleared if the PluginBackgroundSink is destroyed. + */ + PluginBackgroundSink* mBackgroundSink; // For assertions that make it easier to determine if a crash is due // to the underlying problem described in bug 136927, and to prevent @@ -322,6 +334,7 @@ public: nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion, + const nsRect& aAllowVisibleRegionExpansion, PRBool& aContainsRootContentDocBG); NS_DISPLAY_DECL_NAME("Plugin", TYPE_PLUGIN) @@ -345,7 +358,7 @@ public: virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager) { - return static_cast(mFrame)->GetLayerState(aBuilder, aManager); + return static_cast(mFrame)->GetLayerState(aBuilder); } private: diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 21bd061ce4d..c4b82f0196f 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1558,7 +1558,7 @@ asserts(0-1) == 582146-1.html about:blank == 584699-1.html 584699-1-ref.html == 585598-2.xhtml 585598-2-ref.xhtml == 586400-1.html 586400-1-ref.html -== 586683-1.html 586683-1-ref.html +fails-if(cocoaWidget) == 586683-1.html 586683-1-ref.html == 589615-1a.xhtml 589615-1-ref.html == 589615-1b.html 589615-1-ref.html == 589672-1.html 589672-1-ref.html diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index d9464f64dbd..95708ccc7f3 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -571,6 +571,7 @@ function ReadManifest(aURL) cond = true; } else if (item == "needs-focus") { needs_focus = true; + cond = false; } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) { cond = false; minAsserts = Number(m[1]); diff --git a/modules/plugin/base/public/nsIPluginInstance.idl b/modules/plugin/base/public/nsIPluginInstance.idl index 26a95036da2..a4a413f2428 100644 --- a/modules/plugin/base/public/nsIPluginInstance.idl +++ b/modules/plugin/base/public/nsIPluginInstance.idl @@ -48,6 +48,8 @@ interface nsIOutputStream; struct JSContext; struct JSObject; class gfxASurface; +class gfxContext; +struct nsIntRect; #define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class" %} @@ -55,6 +57,8 @@ class gfxASurface; [ptr] native JSContextPtr(JSContext); [ptr] native JSObjectPtr(JSObject); [ptr] native gfxASurfacePtr(gfxASurface); +[ptr] native gfxContextPtr(gfxContext); +[ptr] native nsIntRectPtr(nsIntRect); [uuid(84994340-E120-4051-824F-D4EE8AEF1A3E)] interface nsIPluginInstance : nsISupports @@ -247,3 +251,18 @@ interface nsIPluginInstance : nsISupports */ PRBool useAsyncPainting(); }; + + +// XXX kill me after branching +[noscript, uuid(324f3c02-4fbd-430b-8afa-db083d3867fc)] +interface nsIPluginInstance_MOZILLA_2_0_BRANCH : nsIPluginInstance +{ + /** + * This is the second leg in the trip to PluginInstanceParent. It + * approximately follows the ReadbackSink API. + */ + + void setBackgroundUnknown(); + void beginUpdateBackground(in nsIntRectPtr rect, out gfxContextPtr ctx); + void endUpdateBackground(in gfxContextPtr ctx, in nsIntRectPtr rect); +}; diff --git a/modules/plugin/base/src/PluginPRLibrary.cpp b/modules/plugin/base/src/PluginPRLibrary.cpp index 925af1eae59..49d39e16a76 100644 --- a/modules/plugin/base/src/PluginPRLibrary.cpp +++ b/modules/plugin/base/src/PluginPRLibrary.cpp @@ -255,4 +255,32 @@ PluginPRLibrary::GetSurface(NPP instance, gfxASurface** aSurface) return NS_OK; } +nsresult +PluginPRLibrary::SetBackgroundUnknown(NPP instance) +{ + nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata; + NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER); + NS_ERROR("Unexpected use of async APIs for in-process plugin."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +PluginPRLibrary::BeginUpdateBackground(NPP instance, + const nsIntRect&, gfxContext** aCtx) +{ + nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata; + NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER); + NS_ERROR("Unexpected use of async APIs for in-process plugin."); + *aCtx = nsnull; + return NS_OK; +} + +nsresult +PluginPRLibrary::EndUpdateBackground(NPP instance, + gfxContext*, const nsIntRect&) +{ + NS_RUNTIMEABORT("This should never be called"); + return NS_ERROR_NOT_AVAILABLE; +} + } // namespace mozilla diff --git a/modules/plugin/base/src/PluginPRLibrary.h b/modules/plugin/base/src/PluginPRLibrary.h index 70d18bf9d85..d392d40aba4 100644 --- a/modules/plugin/base/src/PluginPRLibrary.h +++ b/modules/plugin/base/src/PluginPRLibrary.h @@ -142,6 +142,14 @@ public: virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window); virtual nsresult GetSurface(NPP instance, gfxASurface** aSurface); NS_OVERRIDE virtual bool UseAsyncPainting() { return false; } + NS_OVERRIDE + virtual nsresult SetBackgroundUnknown(NPP instance); + NS_OVERRIDE + virtual nsresult BeginUpdateBackground(NPP instance, + const nsIntRect&, gfxContext** aCtx); + NS_OVERRIDE + virtual nsresult EndUpdateBackground(NPP instance, + gfxContext* aCtx, const nsIntRect&); private: NP_InitializeFunc mNP_Initialize; diff --git a/modules/plugin/base/src/nsNPAPIPluginInstance.cpp b/modules/plugin/base/src/nsNPAPIPluginInstance.cpp index 4a7beb65b8a..2473c19281b 100644 --- a/modules/plugin/base/src/nsNPAPIPluginInstance.cpp +++ b/modules/plugin/base/src/nsNPAPIPluginInstance.cpp @@ -59,13 +59,13 @@ #include "nsJSNPRuntime.h" #include "nsPluginStreamListenerPeer.h" +using namespace mozilla; using namespace mozilla::plugins::parent; -using mozilla::TimeStamp; static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID); static NS_DEFINE_IID(kIPluginStreamListenerIID, NS_IPLUGINSTREAMLISTENER_IID); -NS_IMPL_ISUPPORTS1(nsNPAPIPluginInstance, nsIPluginInstance) +NS_IMPL_ISUPPORTS2(nsNPAPIPluginInstance, nsIPluginInstance, nsIPluginInstance_MOZILLA_2_0_BRANCH) nsNPAPIPluginInstance::nsNPAPIPluginInstance(nsNPAPIPlugin* plugin) : @@ -809,18 +809,32 @@ nsNPAPIPluginInstance::IsWindowless(PRBool* isWindowless) return NS_OK; } +class NS_STACK_CLASS AutoPluginLibraryCall +{ +public: + AutoPluginLibraryCall(nsNPAPIPluginInstance* aThis) + : mThis(aThis), mGuard(aThis), mLibrary(nsnull) + { + nsNPAPIPlugin* plugin = mThis->GetPlugin(); + if (plugin) + mLibrary = plugin->GetLibrary(); + } + operator bool() { return !!mLibrary; } + PluginLibrary* operator->() { return mLibrary; } + +private: + nsNPAPIPluginInstance* mThis; + PluginDestructionGuard mGuard; + PluginLibrary* mLibrary; +}; + NS_IMETHODIMP nsNPAPIPluginInstance::AsyncSetWindow(NPWindow* window) { if (RUNNING != mRunning) return NS_OK; - PluginDestructionGuard guard(this); - - if (!mPlugin) - return NS_ERROR_FAILURE; - - PluginLibrary* library = mPlugin->GetLibrary(); + AutoPluginLibraryCall library(this); if (!library) return NS_ERROR_FAILURE; @@ -833,12 +847,7 @@ nsNPAPIPluginInstance::GetSurface(gfxASurface** aSurface) if (RUNNING != mRunning) return NS_OK; - PluginDestructionGuard guard(this); - - if (!mPlugin) - return NS_ERROR_FAILURE; - - PluginLibrary* library = mPlugin->GetLibrary(); + AutoPluginLibraryCall library(this); if (!library) return NS_ERROR_FAILURE; @@ -861,12 +870,7 @@ nsNPAPIPluginInstance::UseAsyncPainting(PRBool* aIsAsync) return NS_OK; } - PluginDestructionGuard guard(this); - - if (!mPlugin) - return NS_ERROR_FAILURE; - - PluginLibrary* library = mPlugin->GetLibrary(); + AutoPluginLibraryCall library(this); if (!library) return NS_ERROR_FAILURE; @@ -874,6 +878,47 @@ nsNPAPIPluginInstance::UseAsyncPainting(PRBool* aIsAsync) return NS_OK; } +NS_IMETHODIMP +nsNPAPIPluginInstance::SetBackgroundUnknown() +{ + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) + return NS_ERROR_FAILURE; + + return library->SetBackgroundUnknown(&mNPP); +} + +NS_IMETHODIMP +nsNPAPIPluginInstance::BeginUpdateBackground(nsIntRect* aRect, + gfxContext** aContext) +{ + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) + return NS_ERROR_FAILURE; + + return library->BeginUpdateBackground(&mNPP, *aRect, aContext); +} + +NS_IMETHODIMP +nsNPAPIPluginInstance::EndUpdateBackground(gfxContext* aContext, + nsIntRect* aRect) +{ + if (RUNNING != mRunning) + return NS_OK; + + AutoPluginLibraryCall library(this); + if (!library) + return NS_ERROR_FAILURE; + + return library->EndUpdateBackground(&mNPP, aContext, *aRect); +} + NS_IMETHODIMP nsNPAPIPluginInstance::IsTransparent(PRBool* isTransparent) { diff --git a/modules/plugin/base/src/nsNPAPIPluginInstance.h b/modules/plugin/base/src/nsNPAPIPluginInstance.h index 61912fdb9cd..ba062a228b7 100644 --- a/modules/plugin/base/src/nsNPAPIPluginInstance.h +++ b/modules/plugin/base/src/nsNPAPIPluginInstance.h @@ -67,7 +67,7 @@ public: void (*callback)(NPP npp, uint32_t timerID); }; -class nsNPAPIPluginInstance : public nsIPluginInstance +class nsNPAPIPluginInstance : public nsIPluginInstance_MOZILLA_2_0_BRANCH { private: typedef mozilla::PluginLibrary PluginLibrary; @@ -75,6 +75,7 @@ private: public: NS_DECL_ISUPPORTS NS_DECL_NSIPLUGININSTANCE + NS_DECL_NSIPLUGININSTANCE_MOZILLA_2_0_BRANCH nsNPAPIPlugin* GetPlugin(); diff --git a/modules/plugin/test/crashtests/626602-1.html b/modules/plugin/test/crashtests/626602-1.html new file mode 100644 index 00000000000..1802cf77f8f --- /dev/null +++ b/modules/plugin/test/crashtests/626602-1.html @@ -0,0 +1,109 @@ + + + + + + + + + +
+ + + + + diff --git a/modules/plugin/test/crashtests/crashtests.list b/modules/plugin/test/crashtests/crashtests.list index acd3186f3ed..9e271b16554 100644 --- a/modules/plugin/test/crashtests/crashtests.list +++ b/modules/plugin/test/crashtests/crashtests.list @@ -9,3 +9,4 @@ load 570884.html # Plugin arch is going to change anyway with OOP content so skipping # this test for now is OK. skip-if(browserIsRemote||!haveTestPlugin||http.platform!="X11"||!testPluginIsOOP()) load 598862.html +load 626602-1.html diff --git a/modules/plugin/test/reftest/plugin-background-1-step.html b/modules/plugin/test/reftest/plugin-background-1-step.html new file mode 100644 index 00000000000..9498633b416 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-background-1-step.html @@ -0,0 +1,22 @@ + + + + + + + + +
Test some plugin stuff.
+
+ + + +
+
+
+
+ + diff --git a/modules/plugin/test/reftest/plugin-background-10-step.html b/modules/plugin/test/reftest/plugin-background-10-step.html new file mode 100644 index 00000000000..7a0824a5657 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-background-10-step.html @@ -0,0 +1,22 @@ + + + + + + + + +
Test some plugin stuff.
+
+ + + +
+
+
+
+ + diff --git a/modules/plugin/test/reftest/plugin-background-2-step.html b/modules/plugin/test/reftest/plugin-background-2-step.html new file mode 100644 index 00000000000..cc186a5f296 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-background-2-step.html @@ -0,0 +1,22 @@ + + + + + + + + +
Test some plugin stuff.
+
+ + + +
+
+
+
+ + diff --git a/modules/plugin/test/reftest/plugin-background-5-step.html b/modules/plugin/test/reftest/plugin-background-5-step.html new file mode 100644 index 00000000000..2630719c880 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-background-5-step.html @@ -0,0 +1,22 @@ + + + + + + + + +
Test some plugin stuff.
+
+ + + +
+
+
+
+ + diff --git a/modules/plugin/test/reftest/plugin-background-ref.html b/modules/plugin/test/reftest/plugin-background-ref.html new file mode 100644 index 00000000000..651fdecef56 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-background-ref.html @@ -0,0 +1,17 @@ + + + + + + +
Test some plugin stuff.
+
+ +
+ +
+
+
+
+ + diff --git a/modules/plugin/test/reftest/plugin-background.css b/modules/plugin/test/reftest/plugin-background.css new file mode 100644 index 00000000000..14e37f185a2 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-background.css @@ -0,0 +1,58 @@ +div { + position: absolute; +} +#bad { + left:220px; top:0px; + z-index: 0; +} +#good { + left:0px; top:0px; + width:220px; height:220px; + background-color: rgba(0,255,0, 0.6); + z-index: 0; +} + +#topbar { + left:0px; top:0px; + width:220px; height:20px; + background-color: rgb(0,0,0); + z-index: 2; +} +#topbar { + left:0px; top:0px; + width:220px; height:20px; + background-color: rgb(0,0,0); + z-index: 2; +} +#leftbar { + left:0px; top:0px; + width:20px; height:220px; + background-color: rgb(0,0,0); + z-index: 2; +} +#rightbar { + left:200px; top:0px; + width:20px; height:220px; + background-color: rgb(0,0,0); + z-index: 2; +} +#bottombar { + left:0px; top:200px; + width:220px; height:20px; + background-color: rgb(0,0,0); + z-index: 2; +} + +div#plugin { + position: absolute; + left:1px; top:1px; + width:199px; height:199px; + background-color: rgba(0,0,255, 0.2); + z-index: 1; +} + +embed#plugin { + position: absolute; + left:1px; top:1px; + z-index: 1; +} \ No newline at end of file diff --git a/modules/plugin/test/reftest/plugin-background.html b/modules/plugin/test/reftest/plugin-background.html new file mode 100644 index 00000000000..bffa4de49a9 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-background.html @@ -0,0 +1,18 @@ + + + + + + +
Test some plugin stuff.
+
+ + + +
+
+
+
+ + diff --git a/modules/plugin/test/reftest/plugin-background.js b/modules/plugin/test/reftest/plugin-background.js new file mode 100644 index 00000000000..5d24562cff5 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-background.js @@ -0,0 +1,75 @@ +// The including script sets this for us +//var NUM_STEPS; + +var plugin; +var left = 1, top = 1, width = 199, height = 199; +function movePluginTo(x, y, w, h) { + left = x; top = y; width = w; height = h; + plugin.width = w; + plugin.height = h; + plugin.style.left = left + "px"; + plugin.style.top = top + "px"; +} +function deltaInBounds(dx,dy, dw,dh) { + var l = dx + left; + var r = l + width + dw; + var t = dy + top; + var b = t + height + dh; + return (0 <= l && l <= 20 && + 0 <= t && t <= 20 && + 200 <= r && r <= 220 && + 200 <= b && b <= 220); +} + +var initialFrame; +function start() { + window.removeEventListener("MozReftestInvalidate", start, false); + + window.addEventListener("MozAfterPaint", step, false); + window.addEventListener("MozPaintWaitFinished", step, false); + + initialFrame = window.mozPaintCount; + plugin = document.getElementById("plugin"); + + movePluginTo(0,0, 200,200); +} + +var steps = 0; +var which = "move"; // or "grow" +var dx = 1, dy = 1, dw = 1, dh = 1; +function step() { + if (++steps >= NUM_STEPS) { + window.removeEventListener("MozAfterPaint", step, false); + window.removeEventListener("MozPaintWaitFinished", step, false); + return finish(); + } + + var didSomething = false; + if (which == "grow") { + if (deltaInBounds(0,0, dw,dh)) { + movePluginTo(left,top, width+dw, height+dh); + didSomething = true; + } else { + dw = -dw; dh = -dh; + } + } else { + // "move" + if (deltaInBounds(dx,dy, 0,0)) { + movePluginTo(left+dx,top+dy, width, height); + didSomething = true; + } else { + dx = -dx; dy = -dy; + } + } + which = (which == "grow") ? "move" : "grow"; + + if (!didSomething) { + step(); + } +} + +function finish() { + document.documentElement.removeAttribute("class"); +} + +window.addEventListener("MozReftestInvalidate", start, false); diff --git a/modules/plugin/test/reftest/plugin-busy-alpha-zindex.html b/modules/plugin/test/reftest/plugin-busy-alpha-zindex.html new file mode 100644 index 00000000000..5108c09b4c5 --- /dev/null +++ b/modules/plugin/test/reftest/plugin-busy-alpha-zindex.html @@ -0,0 +1,49 @@ + + + + + + + + + +
+ + + + + diff --git a/modules/plugin/test/reftest/plugin-canvas-alpha-zindex.html b/modules/plugin/test/reftest/plugin-canvas-alpha-zindex.html new file mode 100644 index 00000000000..21a846f0f1b --- /dev/null +++ b/modules/plugin/test/reftest/plugin-canvas-alpha-zindex.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + + diff --git a/modules/plugin/test/reftest/plugin-transform-alpha-zindex.html b/modules/plugin/test/reftest/plugin-transform-alpha-zindex.html new file mode 100644 index 00000000000..a53552dcdaa --- /dev/null +++ b/modules/plugin/test/reftest/plugin-transform-alpha-zindex.html @@ -0,0 +1,26 @@ + + + + + + +
+ + + + diff --git a/modules/plugin/test/reftest/reftest.list b/modules/plugin/test/reftest/reftest.list index 6fff9538f7f..7b36aaecafe 100644 --- a/modules/plugin/test/reftest/reftest.list +++ b/modules/plugin/test/reftest/reftest.list @@ -9,3 +9,11 @@ random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) = random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-3.html border-padding-3-ref.html # bug 629430 random-if(cocoaWidget||d2d) fails-if(!haveTestPlugin) skip-if(!testPluginIsOOP()) == pluginproblemui-direction-1.html pluginproblemui-direction-1-ref.html fails-if(!haveTestPlugin) skip-if(!testPluginIsOOP()) == pluginproblemui-direction-2.html pluginproblemui-direction-2-ref.html +fails-if(!haveTestPlugin) == plugin-canvas-alpha-zindex.html div-alpha-zindex.html +fails-if(!haveTestPlugin) == plugin-transform-alpha-zindex.html div-alpha-zindex.html +random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-busy-alpha-zindex.html div-alpha-zindex.html +fails-if(!haveTestPlugin) == plugin-background.html plugin-background-ref.html +random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-background-1-step.html plugin-background-ref.html +random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-background-2-step.html plugin-background-ref.html +fails-if(!haveTestPlugin) == plugin-background-5-step.html plugin-background-ref.html +fails-if(!haveTestPlugin) == plugin-background-10-step.html plugin-background-ref.html 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; } diff --git a/netwerk/base/public/Makefile.in b/netwerk/base/public/Makefile.in index 633c096a98f..3bf85069870 100644 --- a/netwerk/base/public/Makefile.in +++ b/netwerk/base/public/Makefile.in @@ -59,6 +59,7 @@ SDK_XPIDLSRCS = \ nsIUploadChannel.idl \ nsIUnicharStreamListener.idl \ nsITraceableChannel.idl \ + nsIX509Cert.idl \ $(NULL) XPIDLSRCS = \ @@ -143,6 +144,8 @@ XPIDLSRCS = \ nsIRedirectResultListener.idl \ mozIThirdPartyUtil.idl \ nsISerializationHelper.idl \ + nsISSLStatus.idl \ + nsISSLStatusProvider.idl \ $(NULL) ifdef MOZ_IPC diff --git a/security/manager/ssl/public/nsISSLStatus.idl b/netwerk/base/public/nsISSLStatus.idl similarity index 100% rename from security/manager/ssl/public/nsISSLStatus.idl rename to netwerk/base/public/nsISSLStatus.idl diff --git a/security/manager/boot/public/nsISSLStatusProvider.idl b/netwerk/base/public/nsISSLStatusProvider.idl similarity index 100% rename from security/manager/boot/public/nsISSLStatusProvider.idl rename to netwerk/base/public/nsISSLStatusProvider.idl diff --git a/security/manager/ssl/public/nsIX509Cert.idl b/netwerk/base/public/nsIX509Cert.idl similarity index 100% rename from security/manager/ssl/public/nsIX509Cert.idl rename to netwerk/base/public/nsIX509Cert.idl diff --git a/netwerk/protocol/http/nsHttpNTLMAuth.cpp b/netwerk/protocol/http/nsHttpNTLMAuth.cpp index 2fb16ae1789..3cc0de159bf 100644 --- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp +++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp @@ -22,6 +22,7 @@ * Contributor(s): * Darin Fisher * Jim Mathies + * Guillermo Robla Vicario * * 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 @@ -52,6 +53,9 @@ #include "nsIServiceManager.h" #include "nsIHttpAuthenticableChannel.h" #include "nsIURI.h" +#include "nsIX509Cert.h" +#include "nsISSLStatus.h" +#include "nsISSLStatusProvider.h" static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies"; static const char kTrustedURIs[] = "network.automatic-ntlm-auth.trusted-uris"; @@ -235,6 +239,9 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel, LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n", *sessionState, *continuationState)); + // Use the native NTLM if available + mUseNative = PR_TRUE; + // NOTE: we don't define any session state, but we do use the pointer. *identityInvalid = PR_FALSE; @@ -298,6 +305,8 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel, // see bug 520607 for details. LOG(("Trying to fall back on internal ntlm auth.\n")); module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm"); + + mUseNative = PR_FALSE; // Prompt user for domain, username, and password. *identityInvalid = PR_TRUE; @@ -366,8 +375,65 @@ nsHttpNTLMAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel, if (NS_FAILED(rv)) return rv; +// This update enables updated Windows machines (Win7 or patched previous +// versions) and Linux machines running Samba (updated for Channel +// Binding), to perform Channel Binding when authenticating using NTLMv2 +// and an outer secure channel. +// +// Currently only implemented for Windows, linux support will be landing in +// a separate patch, update this #ifdef accordingly then. +#if defined (XP_WIN) /* || defined (LINUX) */ + PRBool isHttps; + rv = uri->SchemeIs("https", &isHttps); + if (NS_FAILED(rv)) + return rv; + + // When the url starts with https, we should retrieve the server + // certificate and compute the CBT, but only when we are using + // the native NTLM implementation and not the internal one. + if (isHttps && mUseNative) { + nsCOMPtr channel = do_QueryInterface(authChannel, &rv); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr security; + rv = channel->GetSecurityInfo(getter_AddRefs(security)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr + statusProvider(do_QueryInterface(security)); + NS_ENSURE_TRUE(statusProvider, NS_ERROR_FAILURE); + + rv = statusProvider->GetSSLStatus(getter_AddRefs(security)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr status(do_QueryInterface(security)); + NS_ENSURE_TRUE(status, NS_ERROR_FAILURE); + + nsCOMPtr cert; + rv = status->GetServerCert(getter_AddRefs(cert)); + if (NS_FAILED(rv)) + return rv; + + PRUint32 length; + PRUint8* certArray; + cert->GetRawDER(&length, &certArray); + + // If there is a server certificate, we pass it along the + // first time we call GetNextToken(). + inBufLen = length; + inBuf = certArray; + } else { + // If there is no server certificate, we don't pass anything. + inBufLen = 0; + inBuf = nsnull; + } +#else // Extended protection update is just for Linux and Windows machines. inBufLen = 0; inBuf = nsnull; +#endif } else { // decode challenge; skip past "NTLM " to the start of the base64 diff --git a/netwerk/protocol/http/nsHttpNTLMAuth.h b/netwerk/protocol/http/nsHttpNTLMAuth.h index ed22a3f5939..485605fb5e4 100644 --- a/netwerk/protocol/http/nsHttpNTLMAuth.h +++ b/netwerk/protocol/http/nsHttpNTLMAuth.h @@ -20,6 +20,7 @@ * * Contributor(s): * Darin Fisher + * Guillermo Robla Vicario * * 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 @@ -48,6 +49,11 @@ public: nsHttpNTLMAuth() {} virtual ~nsHttpNTLMAuth() {} + +private: + // This flag indicates whether we are using the native NTLM implementation + // or the internal one. + PRBool mUseNative; }; #endif // !nsHttpNTLMAuth_h__ diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index 33ae7052742..f286db3d340 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -687,6 +687,9 @@ nsHttpTransaction::LocateHttpStart(char *buf, PRUint32 len, static const char HTTPHeader[] = "HTTP/1."; static const PRInt32 HTTPHeaderLen = sizeof(HTTPHeader) - 1; + + if (aAllowPartialMatch && (len < HTTPHeaderLen)) + return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nsnull; // mLineBuf can contain partial match from previous search if (!mLineBuf.IsEmpty()) { @@ -714,13 +717,9 @@ nsHttpTransaction::LocateHttpStart(char *buf, PRUint32 len, if (PL_strncasecmp(buf, HTTPHeader, PR_MIN(len, HTTPHeaderLen)) == 0) { if (len < HTTPHeaderLen) { // partial HTTPHeader sequence found - if (aAllowPartialMatch) { - return buf; - } else { - // save partial match to mLineBuf - mLineBuf.Assign(buf, len); - return 0; - } + // save partial match to mLineBuf + mLineBuf.Assign(buf, len); + return 0; } // whole HTTPHeader sequence found @@ -828,7 +827,7 @@ nsHttpTransaction::ParseHead(char *buf, if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) { // tolerate only minor junk before the status line mHttpResponseMatched = PR_TRUE; - char *p = LocateHttpStart(buf, PR_MIN(count, 8), PR_TRUE); + char *p = LocateHttpStart(buf, PR_MIN(count, 11), PR_TRUE); if (!p) { // Treat any 0.9 style response of a put as a failure. if (mRequestHead->Method() == nsHttp::Put) diff --git a/security/manager/boot/public/Makefile.in b/security/manager/boot/public/Makefile.in index 82d90cecaf2..70b8f30c3be 100644 --- a/security/manager/boot/public/Makefile.in +++ b/security/manager/boot/public/Makefile.in @@ -50,7 +50,6 @@ SDK_XPIDLSRCS = \ $(NULL) XPIDLSRCS = \ - nsISSLStatusProvider.idl \ nsIBufEntropyCollector.idl \ $(NULL) diff --git a/security/manager/ssl/public/Makefile.in b/security/manager/ssl/public/Makefile.in index fa84d3ac04e..27829846378 100644 --- a/security/manager/ssl/public/Makefile.in +++ b/security/manager/ssl/public/Makefile.in @@ -53,7 +53,6 @@ SDK_XPIDLSRCS = \ nsIASN1Sequence.idl \ nsICertificateDialogs.idl \ nsICRLInfo.idl \ - nsIX509Cert.idl \ nsIX509CertDB.idl \ nsIX509CertValidity.idl \ $(NULL) @@ -80,7 +79,6 @@ XPIDLSRCS = \ nsIGenKeypairInfoDlg.idl \ nsITokenDialogs.idl \ nsITokenPasswordDialogs.idl \ - nsISSLStatus.idl \ nsIKeygenThread.idl \ nsICMSSecureMessage.idl \ nsIUserCertPicker.idl \ diff --git a/toolkit/components/console/hudservice/HUDService.jsm b/toolkit/components/console/hudservice/HUDService.jsm index ffd94a3dd1f..eb230fd5140 100644 --- a/toolkit/components/console/hudservice/HUDService.jsm +++ b/toolkit/components/console/hudservice/HUDService.jsm @@ -2696,20 +2696,9 @@ HUD_SERVICE.prototype = } } - // Need to detect that the console component has been paved over. Do this by - // checking whether its global object is equal to that of an object - // returned by our native ConsoleAPI nsIDOMGlobalPropertyInitializer. + // Need to detect that the console component has been paved over. let consoleObject = unwrap(aContentWindow).console; - let consoleGlobal = Cu.getGlobalForObject(consoleObject); - - let nativeConsoleObj = Cc["@mozilla.org/console-api;1"]. - createInstance(Ci.nsIDOMGlobalPropertyInitializer). - init(aContentWindow); - let nativeConsoleGlobal = Cu.getGlobalForObject(nativeConsoleObj); - - // Need a "===" comparison because backstagepass objects have strange - // behavior with == - if (consoleGlobal !== nativeConsoleGlobal) + if (!("__mozillaConsole__" in consoleObject)) this.logWarningAboutReplacedAPI(hudId); // register the controller to handle "select all" properly diff --git a/toolkit/mozapps/extensions/content/extensions-content.js b/toolkit/mozapps/extensions/content/extensions-content.js index bc065d323dc..04dcb5a8663 100644 --- a/toolkit/mozapps/extensions/content/extensions-content.js +++ b/toolkit/mozapps/extensions/content/extensions-content.js @@ -49,7 +49,7 @@ var gIoService = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); function createInstallTrigger(window) { - return { + let chromeObject = { window: window, __exposedProps__: { @@ -61,9 +61,7 @@ function createInstallTrigger(window) { updateEnabled: "r", install: "r", installChrome: "r", - startSoftwareUpdate: "r", - toString: "r", - toSource: "r", // XXX workaround for bug 582100 + startSoftwareUpdate: "r" }, // == Public interface == @@ -188,6 +186,26 @@ function createInstallTrigger(window) { } } }; + + let sandbox = Cu.Sandbox(window); + let obj = Cu.evalInSandbox( + "(function (x) {\ + var bind = Function.bind;\ + return {\ + enabled: bind.call(x.enabled, x),\ + updateEnabled: bind.call(x.updateEnabled, x),\ + install: bind.call(x.install, x),\ + installChrome: bind.call(x.installChrome, x),\ + startSoftwareUpdate: bind.call(x.startSoftwareUpdate, x)\ + };\ + })", sandbox)(chromeObject); + + obj.SKIN = chromeObject.SKIN; + obj.LOCALE = chromeObject.LOCALE; + obj.CONTENT = chromeObject.CONTENT; + obj.PACKAGE = chromeObject.PACKAGE; + + return obj; }; /** diff --git a/toolkit/system/gnome/nsAlertsIconListener.cpp b/toolkit/system/gnome/nsAlertsIconListener.cpp index 3ad4e7c8976..2793ead6b52 100644 --- a/toolkit/system/gnome/nsAlertsIconListener.cpp +++ b/toolkit/system/gnome/nsAlertsIconListener.cpp @@ -46,6 +46,11 @@ #include +// Compatibility macro for =libnotify-0.7.0 has no support for attaching to widgets +#if !NOTIFY_CHECK_VERSION(0,7,0) + , NULL +#endif + ); + if (!mNotification) return NS_ERROR_OUT_OF_MEMORY;