From 2ac3c549db205bd9e74d452d00bf37e7a450eb18 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 16 Jul 2010 09:07:53 +1200 Subject: [PATCH] Bug 572613. Avoid creating a SolidColor display item when possible by poking the color into the canvas background display item. r=tnikkel --- layout/base/nsCSSRendering.cpp | 7 +++-- layout/base/nsLayoutUtils.cpp | 4 ++- layout/base/nsPresShell.cpp | 35 ++++++++++++++++++++++ layout/generic/nsCanvasFrame.cpp | 47 +++++++++++------------------ layout/generic/nsCanvasFrame.h | 51 ++++++++++++++++++++++++++++++++ layout/generic/nsFrameFrame.cpp | 12 ++++---- 6 files changed, 118 insertions(+), 38 deletions(-) diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 941d12de22dc..2e464b9f5bed 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -2160,8 +2160,11 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, // For canvas frames (in the CSS sense) we draw the background color using // a solid color item that gets added in nsLayoutUtils::PaintFrame, - // PresShell::RenderDocument, or nsSubDocumentFrame::BuildDisplayList - // (bug 488242). + // or nsSubDocumentFrame::BuildDisplayList (bug 488242). (The solid + // color may be moved into nsDisplayCanvasBackground by + // nsPresShell::AddCanvasBackgroundColorItem, and painted by + // nsDisplayCanvasBackground directly.) Either way we don't need to + // paint the background color here. PRBool isCanvasFrame = IsCanvasFrame(aForFrame); // Determine whether we are drawing background images and/or diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 4e2b2e368468..9c95a85444b1 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1275,7 +1275,9 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra // an nsPageContentFrame. We only want to add the canvas background color // item once, for the nsPageContentFrame. - // Add the canvas background color. + // Add the canvas background color to the bottom of the list. This + // happens after we've built the list so that AddCanvasBackgroundColorItem + // can monkey with the contents if necessary. rv = presShell->AddCanvasBackgroundColorItem( builder, list, aFrame, canvasArea, aBackstop); } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 1a8ea4b7b5d6..89fd0dbb5211 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5713,6 +5713,24 @@ PresShell::RenderSelection(nsISelection* aSelection, aScreenRect); } +static PRBool +AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame, + nscolor aColor) +{ + for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) { + if (i->GetUnderlyingFrame() == aCanvasFrame && + i->GetType() == nsDisplayItem::TYPE_CANVAS_BACKGROUND) { + nsDisplayCanvasBackground* bg = static_cast(i); + bg->SetExtraBackgroundColor(aColor); + return PR_TRUE; + } + nsDisplayList* sublist = i->GetList(); + if (sublist && AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor)) + return PR_TRUE; + } + return PR_FALSE; +} + nsresult PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder, nsDisplayList& aList, nsIFrame* aFrame, @@ -5730,6 +5748,23 @@ nsresult PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder, return NS_OK; nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor); + + // To make layers work better, we want to avoid having a big non-scrolled + // color background behind a scrolled transparent background. Instead, + // we'll try to move the color background into the scrolled content + // by making nsDisplayCanvasBackground paint it. + if (!aFrame->GetParent()) { + nsIScrollableFrame* sf = + aFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable(); + if (sf) { + nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame()); + if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) { + if (AddCanvasBackgroundColor(aList, canvasFrame, bgcolor)) + return NS_OK; + } + } + } + return aList.AppendNewToBottom( new (&aBuilder) nsDisplaySolidColor(aFrame, aBounds, bgcolor)); } diff --git a/layout/generic/nsCanvasFrame.cpp b/layout/generic/nsCanvasFrame.cpp index 1486b1107372..7304565535a9 100644 --- a/layout/generic/nsCanvasFrame.cpp +++ b/layout/generic/nsCanvasFrame.cpp @@ -249,38 +249,25 @@ nsRect nsCanvasFrame::CanvasArea() const return result; } -/* - * Override nsDisplayBackground methods so that we pass aBGClipRect to - * PaintBackground, covering the whole overflow area. - */ -class nsDisplayCanvasBackground : public nsDisplayBackground { -public: - nsDisplayCanvasBackground(nsIFrame *aFrame) - : nsDisplayBackground(aFrame) - { +void +nsDisplayCanvasBackground::Paint(nsDisplayListBuilder* aBuilder, + nsIRenderingContext* aCtx) +{ + nsCanvasFrame* frame = static_cast(mFrame); + nsPoint offset = aBuilder->ToReferenceFrame(mFrame); + nsRect bgClipRect = frame->CanvasArea() + offset; + + if (NS_GET_A(mExtraBackgroundColor) > 0) { + aCtx->SetColor(mExtraBackgroundColor); + aCtx->FillRect(bgClipRect); } - virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) - { - nsCanvasFrame* frame = static_cast(mFrame); - return frame->CanvasArea() + aBuilder->ToReferenceFrame(mFrame); - } - - virtual void Paint(nsDisplayListBuilder* aBuilder, - nsIRenderingContext* aCtx) - { - nsCanvasFrame* frame = static_cast(mFrame); - nsPoint offset = aBuilder->ToReferenceFrame(mFrame); - nsRect bgClipRect = frame->CanvasArea() + offset; - nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame, - mVisibleRect, - nsRect(offset, mFrame->GetSize()), - aBuilder->GetBackgroundPaintFlags(), - &bgClipRect); - } - - NS_DISPLAY_DECL_NAME("CanvasBackground", TYPE_CANVAS_BACKGROUND) -}; + nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame, + mVisibleRect, + nsRect(offset, mFrame->GetSize()), + aBuilder->GetBackgroundPaintFlags(), + &bgClipRect); +} /** * A display item to paint the focus ring for the document. diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h index 6cc148c8d194..bcb2c4ecf2fd 100644 --- a/layout/generic/nsCanvasFrame.h +++ b/layout/generic/nsCanvasFrame.h @@ -157,4 +157,55 @@ protected: nsAbsoluteContainingBlock mAbsoluteContainer; }; +/** + * Override nsDisplayBackground methods so that we pass aBGClipRect to + * PaintBackground, covering the whole overflow area. + * We can also paint an "extra background color" behind the normal + * background. + */ +class nsDisplayCanvasBackground : public nsDisplayBackground { +public: + nsDisplayCanvasBackground(nsIFrame *aFrame) + : nsDisplayBackground(aFrame) + { + mExtraBackgroundColor = NS_RGBA(0,0,0,0); + } + + virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder) + { + return NS_GET_A(mExtraBackgroundColor) == 255 || + nsDisplayBackground::IsOpaque(aBuilder); + } + virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) + { + nscolor background; + if (!nsDisplayBackground::IsUniform(aBuilder, &background)) + return PR_FALSE; + NS_ASSERTION(background == NS_RGBA(0,0,0,0), + "The nsDisplayBackground for a canvas frame doesn't paint " + "its background color normally"); + *aColor = mExtraBackgroundColor; + return PR_TRUE; + } + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) + { + nsCanvasFrame* frame = static_cast(mFrame); + return frame->CanvasArea() + aBuilder->ToReferenceFrame(mFrame); + } + + virtual void Paint(nsDisplayListBuilder* aBuilder, + nsIRenderingContext* aCtx); + + void SetExtraBackgroundColor(nscolor aColor) + { + mExtraBackgroundColor = aColor; + } + + NS_DISPLAY_DECL_NAME("CanvasBackground", TYPE_CANVAS_BACKGROUND) + +private: + nscolor mExtraBackgroundColor; +}; + + #endif /* nsCanvasFrame_h___ */ diff --git a/layout/generic/nsFrameFrame.cpp b/layout/generic/nsFrameFrame.cpp index 3b08f9e931d7..3b8c9ec1b422 100644 --- a/layout/generic/nsFrameFrame.cpp +++ b/layout/generic/nsFrameFrame.cpp @@ -427,17 +427,19 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, mInnerView->GetPosition() + GetOffsetTo(aBuilder->ReferenceFrame()); + if (f && NS_SUCCEEDED(rv)) { + rv = f->BuildDisplayListForStackingContext(aBuilder, dirty, &childItems); + } + if (!aBuilder->IsForEventDelivery()) { - // Add the canvas background color. + // Add the canvas background color to the bottom of the list. This + // happens after we've built the list so that AddCanvasBackgroundColorItem + // can monkey with the contents if necessary. rv = presShell->AddCanvasBackgroundColorItem( *aBuilder, childItems, f ? f : this, shellBounds, NS_RGBA(0,0,0,0), PR_TRUE); } - if (f && NS_SUCCEEDED(rv)) { - rv = f->BuildDisplayListForStackingContext(aBuilder, dirty, &childItems); - } - if (NS_SUCCEEDED(rv)) { // Clip children to the child root frame's rectangle rv = aLists.Content()->AppendNewToTop(