michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* rendering object that goes directly inside the document's scrollbars */ michael@0: michael@0: #include "nsCanvasFrame.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "nsCSSRendering.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsCSSFrameConstructor.h" michael@0: #include "nsFrameManager.h" michael@0: #include "gfxPlatform.h" michael@0: michael@0: // for focus michael@0: #include "nsIScrollableFrame.h" michael@0: #ifdef DEBUG_CANVAS_FOCUS michael@0: #include "nsIDocShell.h" michael@0: #endif michael@0: michael@0: //#define DEBUG_CANVAS_FOCUS michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::layout; michael@0: using namespace mozilla::gfx; michael@0: michael@0: nsIFrame* michael@0: NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsCanvasFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame) michael@0: michael@0: NS_QUERYFRAME_HEAD(nsCanvasFrame) michael@0: NS_QUERYFRAME_ENTRY(nsCanvasFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) michael@0: michael@0: void michael@0: nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: nsIScrollableFrame* sf = michael@0: PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable(); michael@0: if (sf) { michael@0: sf->RemoveScrollPositionListener(this); michael@0: } michael@0: michael@0: nsContainerFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: void michael@0: nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY) michael@0: { michael@0: if (mDoPaintFocus) { michael@0: mDoPaintFocus = false; michael@0: PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCanvasFrame::SetHasFocus(bool aHasFocus) michael@0: { michael@0: if (mDoPaintFocus != aHasFocus) { michael@0: mDoPaintFocus = aHasFocus; michael@0: PresContext()->FrameManager()->GetRootFrame()->InvalidateFrameSubtree(); michael@0: michael@0: if (!mAddedScrollPositionListener) { michael@0: nsIScrollableFrame* sf = michael@0: PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable(); michael@0: if (sf) { michael@0: sf->AddScrollPositionListener(this); michael@0: mAddedScrollPositionListener = true; michael@0: } michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsCanvasFrame::SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: NS_ASSERTION(aListID != kPrincipalList || michael@0: aChildList.IsEmpty() || aChildList.OnlyChild(), michael@0: "Primary child list can have at most one frame in it"); michael@0: return nsContainerFrame::SetInitialChildList(aListID, aChildList); michael@0: } michael@0: michael@0: nsresult michael@0: nsCanvasFrame::AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: NS_ASSERTION(aListID == kPrincipalList || michael@0: aListID == kAbsoluteList, "unexpected child list ID"); michael@0: NS_PRECONDITION(aListID != kAbsoluteList || michael@0: mFrames.IsEmpty(), "already have a child frame"); michael@0: if (aListID != kPrincipalList) { michael@0: // We only support the Principal and Absolute child lists. michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (!mFrames.IsEmpty()) { michael@0: // We only allow a single principal child frame. michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // Insert the new frames michael@0: NS_ASSERTION(aFrameList.FirstChild() == aFrameList.LastChild(), michael@0: "Only one principal child frame allowed"); michael@0: #ifdef DEBUG michael@0: nsFrame::VerifyDirtyBitSet(aFrameList); michael@0: #endif michael@0: mFrames.AppendFrames(nullptr, aFrameList); michael@0: michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsCanvasFrame::InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: // Because we only support a single child frame inserting is the same michael@0: // as appending michael@0: NS_PRECONDITION(!aPrevFrame, "unexpected previous sibling frame"); michael@0: if (aPrevFrame) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: return AppendFrames(aListID, aFrameList); michael@0: } michael@0: michael@0: nsresult michael@0: nsCanvasFrame::RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) michael@0: { michael@0: NS_ASSERTION(aListID == kPrincipalList || michael@0: aListID == kAbsoluteList, "unexpected child list ID"); michael@0: if (aListID != kPrincipalList && aListID != kAbsoluteList) { michael@0: // We only support the Principal and Absolute child lists. michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (aOldFrame != mFrames.FirstChild()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Remove the frame and destroy it michael@0: mFrames.DestroyFrame(aOldFrame); michael@0: michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRect nsCanvasFrame::CanvasArea() const michael@0: { michael@0: // Not clear which overflow rect we want here, but it probably doesn't michael@0: // matter. michael@0: nsRect result(GetVisualOverflowRect()); michael@0: michael@0: nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent()); michael@0: if (scrollableFrame) { michael@0: nsRect portRect = scrollableFrame->GetScrollPortRect(); michael@0: result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size())); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: nsCanvasFrame* frame = static_cast(mFrame); michael@0: nsPoint offset = ToReferenceFrame(); michael@0: nsRect bgClipRect = frame->CanvasArea() + offset; michael@0: if (NS_GET_A(mColor) > 0) { michael@0: aCtx->SetColor(mColor); michael@0: aCtx->FillRect(bgClipRect); michael@0: } michael@0: } michael@0: michael@0: static void BlitSurface(gfxContext* aDest, const gfxRect& aRect, gfxASurface* aSource) michael@0: { michael@0: aDest->Translate(gfxPoint(aRect.x, aRect.y)); michael@0: aDest->SetSource(aSource); michael@0: aDest->NewPath(); michael@0: aDest->Rectangle(gfxRect(0, 0, aRect.width, aRect.height)); michael@0: aDest->Fill(); michael@0: aDest->Translate(-gfxPoint(aRect.x, aRect.y)); michael@0: } michael@0: michael@0: static void BlitSurface(DrawTarget* aDest, const gfxRect& aRect, DrawTarget* aSource) michael@0: { michael@0: RefPtr source = aSource->Snapshot(); michael@0: aDest->DrawSurface(source, michael@0: Rect(aRect.x, aRect.y, aRect.width, aRect.height), michael@0: Rect(0, 0, aRect.width, aRect.height)); michael@0: } michael@0: michael@0: void michael@0: nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: nsCanvasFrame* frame = static_cast(mFrame); michael@0: nsPoint offset = ToReferenceFrame(); michael@0: nsRect bgClipRect = frame->CanvasArea() + offset; michael@0: michael@0: nsRefPtr context; michael@0: nsRefPtr dest = aCtx->ThebesContext(); michael@0: nsRefPtr surf; michael@0: RefPtr dt; michael@0: nsRefPtr ctx; michael@0: gfxRect destRect; michael@0: #ifndef MOZ_GFX_OPTIMIZE_MOBILE michael@0: if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) && michael@0: aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() && michael@0: !dest->CurrentMatrix().HasNonIntegerTranslation()) { michael@0: // Snap image rectangle to nearest pixel boundaries. This is the right way michael@0: // to snap for this context, because we checked HasNonIntegerTranslation above. michael@0: destRect.Round(); michael@0: if (dest->IsCairo()) { michael@0: surf = static_cast(Frame()->Properties().Get(nsIFrame::CachedBackgroundImage())); michael@0: nsRefPtr destSurf = dest->CurrentSurface(); michael@0: if (surf && surf->GetType() == destSurf->GetType()) { michael@0: BlitSurface(dest, destRect, surf); michael@0: return; michael@0: } michael@0: surf = destSurf->CreateSimilarSurface( michael@0: gfxContentType::COLOR_ALPHA, michael@0: gfxIntSize(ceil(destRect.width), ceil(destRect.height))); michael@0: } else { michael@0: dt = static_cast(Frame()->Properties().Get(nsIFrame::CachedBackgroundImageDT())); michael@0: DrawTarget* destDT = dest->GetDrawTarget(); michael@0: if (dt) { michael@0: BlitSurface(destDT, destRect, dt); michael@0: return; michael@0: } michael@0: dt = destDT->CreateSimilarDrawTarget(IntSize(ceil(destRect.width), ceil(destRect.height)), SurfaceFormat::B8G8R8A8); michael@0: } michael@0: if (surf || dt) { michael@0: if (surf) { michael@0: ctx = new gfxContext(surf); michael@0: } else { michael@0: ctx = new gfxContext(dt); michael@0: } michael@0: ctx->Translate(-gfxPoint(destRect.x, destRect.y)); michael@0: context = new nsRenderingContext(); michael@0: context->Init(aCtx->DeviceContext(), ctx); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: PaintInternal(aBuilder, michael@0: (surf || dt) ? context.get() : aCtx, michael@0: (surf || dt) ? bgClipRect: mVisibleRect, michael@0: &bgClipRect); michael@0: michael@0: if (surf) { michael@0: BlitSurface(dest, destRect, surf); michael@0: frame->Properties().Set(nsIFrame::CachedBackgroundImage(), michael@0: surf.forget().take()); michael@0: } michael@0: if (dt) { michael@0: BlitSurface(dest->GetDrawTarget(), destRect, dt); michael@0: frame->Properties().Set(nsIFrame::CachedBackgroundImageDT(), dt.forget().drop()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDisplayCanvasThemedBackground::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: nsCanvasFrame* frame = static_cast(mFrame); michael@0: nsPoint offset = ToReferenceFrame(); michael@0: nsRect bgClipRect = frame->CanvasArea() + offset; michael@0: michael@0: PaintInternal(aBuilder, aCtx, mVisibleRect, &bgClipRect); michael@0: } michael@0: michael@0: /** michael@0: * A display item to paint the focus ring for the document. michael@0: * michael@0: * The only reason this can't use nsDisplayGeneric is overriding GetBounds. michael@0: */ michael@0: class nsDisplayCanvasFocus : public nsDisplayItem { michael@0: public: michael@0: nsDisplayCanvasFocus(nsDisplayListBuilder* aBuilder, nsCanvasFrame *aFrame) michael@0: : nsDisplayItem(aBuilder, aFrame) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplayCanvasFocus); michael@0: } michael@0: virtual ~nsDisplayCanvasFocus() { michael@0: MOZ_COUNT_DTOR(nsDisplayCanvasFocus); michael@0: } michael@0: michael@0: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) MOZ_OVERRIDE michael@0: { michael@0: *aSnap = false; michael@0: // This is an overestimate, but that's not a problem. michael@0: nsCanvasFrame* frame = static_cast(mFrame); michael@0: return frame->CanvasArea() + ToReferenceFrame(); michael@0: } michael@0: michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE michael@0: { michael@0: nsCanvasFrame* frame = static_cast(mFrame); michael@0: frame->PaintFocus(*aCtx, ToReferenceFrame()); michael@0: } michael@0: michael@0: NS_DISPLAY_DECL_NAME("CanvasFocus", TYPE_CANVAS_FOCUS) michael@0: }; michael@0: michael@0: void michael@0: nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: if (GetPrevInFlow()) { michael@0: DisplayOverflowContainers(aBuilder, aDirtyRect, aLists); michael@0: } michael@0: michael@0: // Force a background to be shown. We may have a background propagated to us, michael@0: // in which case StyleBackground wouldn't have the right background michael@0: // and the code in nsFrame::DisplayBorderBackgroundOutline might not give us michael@0: // a background. michael@0: // We don't have any border or outline, and our background draws over michael@0: // the overflow area, so just add nsDisplayCanvasBackground instead of michael@0: // calling DisplayBorderBackgroundOutline. michael@0: if (IsVisibleForPainting(aBuilder)) { michael@0: nsStyleContext* bgSC; michael@0: const nsStyleBackground* bg = nullptr; michael@0: bool isThemed = IsThemed(); michael@0: if (!isThemed && nsCSSRendering::FindBackground(this, &bgSC)) { michael@0: bg = bgSC->StyleBackground(); michael@0: } michael@0: aLists.BorderBackground()->AppendNewToTop( michael@0: new (aBuilder) nsDisplayCanvasBackgroundColor(aBuilder, this)); michael@0: michael@0: if (isThemed) { michael@0: aLists.BorderBackground()->AppendNewToTop( michael@0: new (aBuilder) nsDisplayCanvasThemedBackground(aBuilder, this)); michael@0: return; michael@0: } michael@0: michael@0: if (!bg) { michael@0: return; michael@0: } michael@0: michael@0: // Create separate items for each background layer. michael@0: NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) { michael@0: if (bg->mLayers[i].mImage.IsEmpty()) { michael@0: continue; michael@0: } michael@0: aLists.BorderBackground()->AppendNewToTop( michael@0: new (aBuilder) nsDisplayCanvasBackgroundImage(aBuilder, this, i, bg)); michael@0: } michael@0: } michael@0: michael@0: nsIFrame* kid; michael@0: for (kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { michael@0: // Put our child into its own pseudo-stack. michael@0: BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); michael@0: } michael@0: michael@0: #ifdef DEBUG_CANVAS_FOCUS michael@0: nsCOMPtr focusContent; michael@0: aPresContext->EventStateManager()-> michael@0: GetFocusedContent(getter_AddRefs(focusContent)); michael@0: michael@0: bool hasFocus = false; michael@0: nsCOMPtr container; michael@0: aPresContext->GetContainer(getter_AddRefs(container)); michael@0: nsCOMPtr docShell(do_QueryInterface(container)); michael@0: if (docShell) { michael@0: docShell->GetHasFocus(&hasFocus); michael@0: printf("%p - nsCanvasFrame::Paint R:%d,%d,%d,%d DR: %d,%d,%d,%d\n", this, michael@0: mRect.x, mRect.y, mRect.width, mRect.height, michael@0: aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); michael@0: } michael@0: printf("%p - Focus: %s c: %p DoPaint:%s\n", docShell.get(), hasFocus?"Y":"N", michael@0: focusContent.get(), mDoPaintFocus?"Y":"N"); michael@0: #endif michael@0: michael@0: if (!mDoPaintFocus) michael@0: return; michael@0: // Only paint the focus if we're visible michael@0: if (!StyleVisibility()->IsVisible()) michael@0: return; michael@0: michael@0: aLists.Outlines()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayCanvasFocus(aBuilder, this)); michael@0: } michael@0: michael@0: void michael@0: nsCanvasFrame::PaintFocus(nsRenderingContext& aRenderingContext, nsPoint aPt) michael@0: { michael@0: nsRect focusRect(aPt, GetSize()); michael@0: michael@0: nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent()); michael@0: if (scrollableFrame) { michael@0: nsRect portRect = scrollableFrame->GetScrollPortRect(); michael@0: focusRect.width = portRect.width; michael@0: focusRect.height = portRect.height; michael@0: focusRect.MoveBy(scrollableFrame->GetScrollPosition()); michael@0: } michael@0: michael@0: // XXX use the root frame foreground color, but should we find BODY frame michael@0: // for HTML documents? michael@0: nsIFrame* root = mFrames.FirstChild(); michael@0: const nsStyleColor* color = root ? root->StyleColor() : StyleColor(); michael@0: if (!color) { michael@0: NS_ERROR("current color cannot be found"); michael@0: return; michael@0: } michael@0: michael@0: nsCSSRendering::PaintFocus(PresContext(), aRenderingContext, michael@0: focusRect, color->mColor); michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsCanvasFrame::GetMinWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord result; michael@0: DISPLAY_MIN_WIDTH(this, result); michael@0: if (mFrames.IsEmpty()) michael@0: result = 0; michael@0: else michael@0: result = mFrames.FirstChild()->GetMinWidth(aRenderingContext); michael@0: return result; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsCanvasFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord result; michael@0: DISPLAY_PREF_WIDTH(this, result); michael@0: if (mFrames.IsEmpty()) michael@0: result = 0; michael@0: else michael@0: result = mFrames.FirstChild()->GetPrefWidth(aRenderingContext); michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsCanvasFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow"); michael@0: michael@0: // Initialize OUT parameter michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: michael@0: nsCanvasFrame* prevCanvasFrame = static_cast michael@0: (GetPrevInFlow()); michael@0: if (prevCanvasFrame) { michael@0: AutoFrameListPtr overflow(aPresContext, michael@0: prevCanvasFrame->StealOverflowFrames()); michael@0: if (overflow) { michael@0: NS_ASSERTION(overflow->OnlyChild(), michael@0: "must have doc root as canvas frame's only child"); michael@0: nsContainerFrame::ReparentFrameViewList(*overflow, prevCanvasFrame, this); michael@0: // Prepend overflow to the our child list. There may already be michael@0: // children placeholders for fixed-pos elements, which don't get michael@0: // reflowed but must not be lost until the canvas frame is destroyed. michael@0: mFrames.InsertFrames(this, nullptr, *overflow); michael@0: } michael@0: } michael@0: michael@0: // Set our size up front, since some parts of reflow depend on it michael@0: // being already set. Note that the computed height may be michael@0: // unconstrained; that's ok. Consumers should watch out for that. michael@0: SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); michael@0: michael@0: // Reflow our one and only normal child frame. It's either the root michael@0: // element's frame or a placeholder for that frame, if the root element michael@0: // is abs-pos or fixed-pos. We may have additional children which michael@0: // are placeholders for continuations of fixed-pos content, but those michael@0: // don't need to be reflowed. The normal child is always comes before michael@0: // the fixed-pos placeholders, because we insert it at the start michael@0: // of the child list, above. michael@0: nsHTMLReflowMetrics kidDesiredSize(aReflowState); michael@0: if (mFrames.IsEmpty()) { michael@0: // We have no child frame, so return an empty size michael@0: aDesiredSize.Width() = aDesiredSize.Height() = 0; michael@0: } else { michael@0: nsIFrame* kidFrame = mFrames.FirstChild(); michael@0: bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0; michael@0: michael@0: nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, michael@0: nsSize(aReflowState.AvailableWidth(), michael@0: aReflowState.AvailableHeight())); michael@0: michael@0: if (aReflowState.mFlags.mVResize && michael@0: (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { michael@0: // Tell our kid it's being vertically resized too. Bit of a michael@0: // hack for framesets. michael@0: kidReflowState.mFlags.mVResize = true; michael@0: } michael@0: michael@0: nsPoint kidPt(kidReflowState.ComputedPhysicalMargin().left, michael@0: kidReflowState.ComputedPhysicalMargin().top); michael@0: michael@0: kidReflowState.ApplyRelativePositioning(&kidPt); michael@0: michael@0: // Reflow the frame michael@0: ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, michael@0: kidPt.x, kidPt.y, 0, aStatus); michael@0: michael@0: // Complete the reflow and position and size the child frame michael@0: FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowState, michael@0: kidPt.x, kidPt.y, 0); michael@0: michael@0: if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { michael@0: nsIFrame* nextFrame = kidFrame->GetNextInFlow(); michael@0: NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW, michael@0: "If it's incomplete and has no nif yet, it must flag a nif reflow."); michael@0: if (!nextFrame) { michael@0: nextFrame = aPresContext->PresShell()->FrameConstructor()-> michael@0: CreateContinuingFrame(aPresContext, kidFrame, this); michael@0: SetOverflowFrames(nsFrameList(nextFrame, nextFrame)); michael@0: // Root overflow containers will be normal children of michael@0: // the canvas frame, but that's ok because there michael@0: // aren't any other frames we need to isolate them from michael@0: // during reflow. michael@0: } michael@0: if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { michael@0: nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); michael@0: } michael@0: } michael@0: michael@0: // If the child frame was just inserted, then we're responsible for making sure michael@0: // it repaints michael@0: if (kidDirty) { michael@0: // But we have a new child, which will affect our background, so michael@0: // invalidate our whole rect. michael@0: // Note: Even though we request to be sized to our child's size, our michael@0: // scroll frame ensures that we are always the size of the viewport. michael@0: // Also note: GetPosition() on a CanvasFrame is always going to return michael@0: // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect() michael@0: // could also include overflow to our top and left (out of the viewport) michael@0: // which doesn't need to be painted. michael@0: nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame(); michael@0: viewport->InvalidateFrame(); michael@0: } michael@0: michael@0: // Return our desired size. Normally it's what we're told, but michael@0: // sometimes we can be given an unconstrained height (when a window michael@0: // is sizing-to-content), and we should compute our desired height. michael@0: aDesiredSize.Width() = aReflowState.ComputedWidth(); michael@0: if (aReflowState.ComputedHeight() == NS_UNCONSTRAINEDSIZE) { michael@0: aDesiredSize.Height() = kidFrame->GetRect().height + michael@0: kidReflowState.ComputedPhysicalMargin().TopBottom(); michael@0: } else { michael@0: aDesiredSize.Height() = aReflowState.ComputedHeight(); michael@0: } michael@0: michael@0: aDesiredSize.SetOverflowAreasToDesiredBounds(); michael@0: aDesiredSize.mOverflowAreas.UnionWith( michael@0: kidDesiredSize.mOverflowAreas + kidPt); michael@0: } michael@0: michael@0: if (prevCanvasFrame) { michael@0: ReflowOverflowContainerChildren(aPresContext, aReflowState, michael@0: aDesiredSize.mOverflowAreas, 0, michael@0: aStatus); michael@0: } michael@0: michael@0: FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); michael@0: michael@0: NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus); michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsCanvasFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::canvasFrame; michael@0: } michael@0: michael@0: nsresult michael@0: nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent, michael@0: nsIContent** aContent) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aContent); michael@0: nsresult rv = nsFrame::GetContentForEvent(aEvent, michael@0: aContent); michael@0: if (NS_FAILED(rv) || !*aContent) { michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: if (kid) { michael@0: rv = kid->GetContentForEvent(aEvent, michael@0: aContent); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsCanvasFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("Canvas"), aResult); michael@0: } michael@0: #endif