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: #include "nsPageFrame.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsPageContentFrame.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsLayoutUtils.h" // for function BinarySearchForPosition michael@0: #include "nsSimplePageSequenceFrame.h" // for nsSharedPageData michael@0: #include "nsTextFormatter.h" // for page number localization formatting michael@0: #include "nsBidiUtils.h" michael@0: #include "nsIPrintSettings.h" michael@0: michael@0: #include "prlog.h" michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo *GetLayoutPrintingLog(); michael@0: #define PR_PL(_p1) PR_LOG(GetLayoutPrintingLog(), PR_LOG_DEBUG, _p1) michael@0: #else michael@0: #define PR_PL(_p1) michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: nsIFrame* michael@0: NS_NewPageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsPageFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsPageFrame) michael@0: michael@0: nsPageFrame::nsPageFrame(nsStyleContext* aContext) michael@0: : nsContainerFrame(aContext) michael@0: { michael@0: } michael@0: michael@0: nsPageFrame::~nsPageFrame() michael@0: { michael@0: } michael@0: michael@0: nsresult nsPageFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsPageFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: aStatus = NS_FRAME_COMPLETE; // initialize out parameter michael@0: michael@0: NS_ASSERTION(mFrames.FirstChild() && michael@0: nsGkAtoms::pageContentFrame == mFrames.FirstChild()->GetType(), michael@0: "pageFrame must have a pageContentFrame child"); michael@0: michael@0: // Resize our frame allowing it only to be as big as we are michael@0: // XXX Pay attention to the page's border and padding... michael@0: if (mFrames.NotEmpty()) { michael@0: nsIFrame* frame = mFrames.FirstChild(); michael@0: // When the reflow size is NS_UNCONSTRAINEDSIZE it means we are reflowing michael@0: // a single page to print selection. So this means we want to use michael@0: // NS_UNCONSTRAINEDSIZE without altering it michael@0: nscoord avHeight; michael@0: if (mPD->mReflowSize.height == NS_UNCONSTRAINEDSIZE) { michael@0: avHeight = NS_UNCONSTRAINEDSIZE; michael@0: } else { michael@0: avHeight = mPD->mReflowSize.height; michael@0: } michael@0: nsSize maxSize(mPD->mReflowSize.width, avHeight); michael@0: float scale = aPresContext->GetPageScale(); michael@0: maxSize.width = NSToCoordCeil(maxSize.width / scale); michael@0: if (maxSize.height != NS_UNCONSTRAINEDSIZE) { michael@0: maxSize.height = NSToCoordCeil(maxSize.height / scale); michael@0: } michael@0: // Get the number of Twips per pixel from the PresContext michael@0: nscoord onePixelInTwips = nsPresContext::CSSPixelsToAppUnits(1); michael@0: // insurance against infinite reflow, when reflowing less than a pixel michael@0: // XXX Shouldn't we do something more friendly when invalid margins michael@0: // are set? michael@0: if (maxSize.width < onePixelInTwips || maxSize.height < onePixelInTwips) { michael@0: aDesiredSize.Width() = 0; michael@0: aDesiredSize.Height() = 0; michael@0: NS_WARNING("Reflow aborted; no space for content"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsHTMLReflowState kidReflowState(aPresContext, aReflowState, frame, maxSize); michael@0: kidReflowState.mFlags.mIsTopOfPage = true; michael@0: kidReflowState.mFlags.mTableIsSplittable = true; michael@0: michael@0: // Use the margins given in the @page rule. michael@0: // If a margin is 'auto', use the margin from the print settings for that side. michael@0: nsMargin pageContentMargin; michael@0: const nsStyleSides& marginStyle = kidReflowState.mStyleMargin->mMargin; michael@0: NS_FOR_CSS_SIDES(side) { michael@0: if (marginStyle.GetUnit(side) == eStyleUnit_Auto) { michael@0: pageContentMargin.Side(side) = mPD->mReflowMargin.Side(side); michael@0: } else { michael@0: pageContentMargin.Side(side) = kidReflowState.ComputedPhysicalMargin().Side(side); michael@0: } michael@0: } michael@0: michael@0: michael@0: nscoord maxWidth = maxSize.width - pageContentMargin.LeftRight() / scale; michael@0: nscoord maxHeight; michael@0: if (maxSize.height == NS_UNCONSTRAINEDSIZE) { michael@0: maxHeight = NS_UNCONSTRAINEDSIZE; michael@0: } else { michael@0: maxHeight = maxSize.height - pageContentMargin.TopBottom() / scale; michael@0: } michael@0: michael@0: // Check the width and height, if they're too small we reset the margins michael@0: // back to the default. michael@0: if (maxWidth < onePixelInTwips || michael@0: (maxHeight != NS_UNCONSTRAINEDSIZE && maxHeight < onePixelInTwips)) { michael@0: NS_FOR_CSS_SIDES(side) { michael@0: pageContentMargin.Side(side) = mPD->mReflowMargin.Side(side); michael@0: } michael@0: maxWidth = maxSize.width - pageContentMargin.LeftRight() / scale; michael@0: if (maxHeight != NS_UNCONSTRAINEDSIZE) { michael@0: maxHeight = maxSize.height - pageContentMargin.TopBottom() / scale; michael@0: } michael@0: } michael@0: michael@0: kidReflowState.SetComputedWidth(maxWidth); michael@0: kidReflowState.SetComputedHeight(maxHeight); michael@0: michael@0: // calc location of frame michael@0: nscoord xc = pageContentMargin.left; michael@0: nscoord yc = pageContentMargin.top; michael@0: michael@0: // Get the child's desired size michael@0: ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, xc, yc, 0, aStatus); michael@0: michael@0: // Place and size the child michael@0: FinishReflowChild(frame, aPresContext, aDesiredSize, &kidReflowState, xc, yc, 0); michael@0: michael@0: NS_ASSERTION(!NS_FRAME_IS_FULLY_COMPLETE(aStatus) || michael@0: !frame->GetNextInFlow(), "bad child flow list"); michael@0: } michael@0: PR_PL(("PageFrame::Reflow %p ", this)); michael@0: PR_PL(("[%d,%d][%d,%d]\n", aDesiredSize.Width(), aDesiredSize.Height(), aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); michael@0: michael@0: // Return our desired size michael@0: aDesiredSize.Width() = aReflowState.AvailableWidth(); michael@0: if (aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE) { michael@0: aDesiredSize.Height() = aReflowState.AvailableHeight(); michael@0: } michael@0: michael@0: aDesiredSize.SetOverflowAreasToDesiredBounds(); michael@0: FinishAndStoreOverflow(&aDesiredSize); michael@0: michael@0: PR_PL(("PageFrame::Reflow %p ", this)); michael@0: PR_PL(("[%d,%d]\n", aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); michael@0: michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsPageFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::pageFrame; michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsPageFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("Page"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsPageFrame::ProcessSpecialCodes(const nsString& aStr, nsString& aNewStr) michael@0: { michael@0: michael@0: aNewStr = aStr; michael@0: michael@0: // Search to see if the &D code is in the string michael@0: // then subst in the current date/time michael@0: NS_NAMED_LITERAL_STRING(kDate, "&D"); michael@0: if (aStr.Find(kDate) != kNotFound) { michael@0: aNewStr.ReplaceSubstring(kDate.get(), mPD->mDateTimeStr.get()); michael@0: } michael@0: michael@0: // NOTE: Must search for &PT before searching for &P michael@0: // michael@0: // Search to see if the "page number and page" total code are in the string michael@0: // and replace the page number and page total code with the actual michael@0: // values michael@0: NS_NAMED_LITERAL_STRING(kPageAndTotal, "&PT"); michael@0: if (aStr.Find(kPageAndTotal) != kNotFound) { michael@0: char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumAndTotalsFormat.get(), mPageNum, mTotNumPages); michael@0: aNewStr.ReplaceSubstring(kPageAndTotal.get(), uStr); michael@0: nsMemory::Free(uStr); michael@0: } michael@0: michael@0: // Search to see if the page number code is in the string michael@0: // and replace the page number code with the actual value michael@0: NS_NAMED_LITERAL_STRING(kPage, "&P"); michael@0: if (aStr.Find(kPage) != kNotFound) { michael@0: char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumFormat.get(), mPageNum); michael@0: aNewStr.ReplaceSubstring(kPage.get(), uStr); michael@0: nsMemory::Free(uStr); michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_STRING(kTitle, "&T"); michael@0: if (aStr.Find(kTitle) != kNotFound) { michael@0: aNewStr.ReplaceSubstring(kTitle.get(), mPD->mDocTitle.get()); michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_STRING(kDocURL, "&U"); michael@0: if (aStr.Find(kDocURL) != kNotFound) { michael@0: aNewStr.ReplaceSubstring(kDocURL.get(), mPD->mDocURL.get()); michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_STRING(kPageTotal, "&L"); michael@0: if (aStr.Find(kPageTotal) != kNotFound) { michael@0: char16_t * uStr = nsTextFormatter::smprintf(mPD->mPageNumFormat.get(), mTotNumPages); michael@0: aNewStr.ReplaceSubstring(kPageTotal.get(), uStr); michael@0: nsMemory::Free(uStr); michael@0: } michael@0: } michael@0: michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: nscoord nsPageFrame::GetXPosition(nsRenderingContext& aRenderingContext, michael@0: const nsRect& aRect, michael@0: int32_t aJust, michael@0: const nsString& aStr) michael@0: { michael@0: nscoord width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext, michael@0: aStr.get(), aStr.Length()); michael@0: michael@0: nscoord x = aRect.x; michael@0: switch (aJust) { michael@0: case nsIPrintSettings::kJustLeft: michael@0: x += mPD->mEdgePaperMargin.left; michael@0: break; michael@0: michael@0: case nsIPrintSettings::kJustCenter: michael@0: x += (aRect.width - width) / 2; michael@0: break; michael@0: michael@0: case nsIPrintSettings::kJustRight: michael@0: x += aRect.width - width - mPD->mEdgePaperMargin.right; michael@0: break; michael@0: } // switch michael@0: michael@0: return x; michael@0: } michael@0: michael@0: // Draw a header or footer michael@0: // @param aRenderingContext - rendering content ot draw into michael@0: // @param aHeaderFooter - indicates whether it is a header or footer michael@0: // @param aStrLeft - string for the left header or footer; can be empty michael@0: // @param aStrCenter - string for the center header or footer; can be empty michael@0: // @param aStrRight - string for the right header or footer; can be empty michael@0: // @param aRect - the rect of the page michael@0: // @param aAscent - the ascent of the font michael@0: // @param aHeight - the height of the font michael@0: void michael@0: nsPageFrame::DrawHeaderFooter(nsRenderingContext& aRenderingContext, michael@0: nsHeaderFooterEnum aHeaderFooter, michael@0: const nsString& aStrLeft, michael@0: const nsString& aStrCenter, michael@0: const nsString& aStrRight, michael@0: const nsRect& aRect, michael@0: nscoord aAscent, michael@0: nscoord aHeight) michael@0: { michael@0: int32_t numStrs = 0; michael@0: if (!aStrLeft.IsEmpty()) numStrs++; michael@0: if (!aStrCenter.IsEmpty()) numStrs++; michael@0: if (!aStrRight.IsEmpty()) numStrs++; michael@0: michael@0: if (numStrs == 0) return; michael@0: nscoord strSpace = aRect.width / numStrs; michael@0: michael@0: if (!aStrLeft.IsEmpty()) { michael@0: DrawHeaderFooter(aRenderingContext, aHeaderFooter, michael@0: nsIPrintSettings::kJustLeft, aStrLeft, aRect, aAscent, michael@0: aHeight, strSpace); michael@0: } michael@0: if (!aStrCenter.IsEmpty()) { michael@0: DrawHeaderFooter(aRenderingContext, aHeaderFooter, michael@0: nsIPrintSettings::kJustCenter, aStrCenter, aRect, aAscent, michael@0: aHeight, strSpace); michael@0: } michael@0: if (!aStrRight.IsEmpty()) { michael@0: DrawHeaderFooter(aRenderingContext, aHeaderFooter, michael@0: nsIPrintSettings::kJustRight, aStrRight, aRect, aAscent, michael@0: aHeight, strSpace); michael@0: } michael@0: } michael@0: michael@0: // Draw a header or footer string michael@0: // @param aRenderingContext - rendering context to draw into michael@0: // @param aHeaderFooter - indicates whether it is a header or footer michael@0: // @param aJust - indicates where the string is located within the header/footer michael@0: // @param aStr - the string to be drawn michael@0: // @param aRect - the rect of the page michael@0: // @param aHeight - the height of the font michael@0: // @param aAscent - the ascent of the font michael@0: // @param aWidth - available width for the string michael@0: void michael@0: nsPageFrame::DrawHeaderFooter(nsRenderingContext& aRenderingContext, michael@0: nsHeaderFooterEnum aHeaderFooter, michael@0: int32_t aJust, michael@0: const nsString& aStr, michael@0: const nsRect& aRect, michael@0: nscoord aAscent, michael@0: nscoord aHeight, michael@0: nscoord aWidth) michael@0: { michael@0: michael@0: nscoord contentWidth = aWidth - (mPD->mEdgePaperMargin.left + mPD->mEdgePaperMargin.right); michael@0: michael@0: if ((aHeaderFooter == eHeader && aHeight < mPD->mReflowMargin.top) || michael@0: (aHeaderFooter == eFooter && aHeight < mPD->mReflowMargin.bottom)) { michael@0: nsAutoString str; michael@0: ProcessSpecialCodes(aStr, str); michael@0: michael@0: int32_t indx; michael@0: int32_t textWidth = 0; michael@0: const char16_t* text = str.get(); michael@0: michael@0: int32_t len = (int32_t)str.Length(); michael@0: if (len == 0) { michael@0: return; // bail is empty string michael@0: } michael@0: // find how much text fits, the "position" is the size of the available area michael@0: if (nsLayoutUtils::BinarySearchForPosition(&aRenderingContext, text, 0, 0, 0, len, michael@0: int32_t(contentWidth), indx, textWidth)) { michael@0: if (indx < len-1 ) { michael@0: // we can't fit in all the text michael@0: if (indx > 3) { michael@0: // But we can fit in at least 4 chars. Show all but 3 of them, then michael@0: // an ellipsis. michael@0: // XXXbz for non-plane0 text, this may be cutting things in the michael@0: // middle of a codepoint! Also, we have no guarantees that the three michael@0: // dots will fit in the space the three chars we removed took up with michael@0: // these font metrics! michael@0: str.Truncate(indx-3); michael@0: str.AppendLiteral("..."); michael@0: } else { michael@0: // We can only fit 3 or fewer chars. Just show nothing michael@0: str.Truncate(); michael@0: } michael@0: } michael@0: } else { michael@0: return; // bail if couldn't find the correct length michael@0: } michael@0: michael@0: if (HasRTLChars(str)) { michael@0: PresContext()->SetBidiEnabled(); michael@0: } michael@0: michael@0: // cacl the x and y positions of the text michael@0: nscoord x = GetXPosition(aRenderingContext, aRect, aJust, str); michael@0: nscoord y; michael@0: if (aHeaderFooter == eHeader) { michael@0: y = aRect.y + mPD->mEdgePaperMargin.top; michael@0: } else { michael@0: y = aRect.YMost() - aHeight - mPD->mEdgePaperMargin.bottom; michael@0: } michael@0: michael@0: // set up new clip and draw the text michael@0: aRenderingContext.PushState(); michael@0: aRenderingContext.SetColor(NS_RGB(0,0,0)); michael@0: aRenderingContext.IntersectClip(aRect); michael@0: nsLayoutUtils::DrawString(this, &aRenderingContext, str.get(), str.Length(), nsPoint(x, y + aAscent)); michael@0: aRenderingContext.PopState(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Remove all leaf display items that are not for descendants of michael@0: * aBuilder->GetReferenceFrame() from aList. michael@0: * @param aPage the page we're constructing the display list for michael@0: * @param aExtraPage the page we constructed aList for michael@0: * @param aList the list that is modified in-place michael@0: */ michael@0: static void michael@0: PruneDisplayListForExtraPage(nsDisplayListBuilder* aBuilder, michael@0: nsPageFrame* aPage, nsIFrame* aExtraPage, michael@0: nsDisplayList* aList) michael@0: { michael@0: nsDisplayList newList; michael@0: michael@0: while (true) { michael@0: nsDisplayItem* i = aList->RemoveBottom(); michael@0: if (!i) michael@0: break; michael@0: nsDisplayList* subList = i->GetSameCoordinateSystemChildren(); michael@0: if (subList) { michael@0: PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, subList); michael@0: i->UpdateBounds(aBuilder); michael@0: } else { michael@0: nsIFrame* f = i->Frame(); michael@0: if (!nsLayoutUtils::IsProperAncestorFrameCrossDoc(aPage, f)) { michael@0: // We're throwing this away so call its destructor now. The memory michael@0: // is owned by aBuilder which destroys all items at once. michael@0: i->~nsDisplayItem(); michael@0: continue; michael@0: } michael@0: } michael@0: newList.AppendToTop(i); michael@0: } michael@0: aList->AppendToTop(&newList); michael@0: } michael@0: michael@0: static nsresult michael@0: BuildDisplayListForExtraPage(nsDisplayListBuilder* aBuilder, michael@0: nsPageFrame* aPage, nsIFrame* aExtraPage, michael@0: nsDisplayList* aList) michael@0: { michael@0: nsDisplayList list; michael@0: // Pass an empty dirty rect since we're only interested in finding michael@0: // placeholders whose out-of-flows are in the page michael@0: // aBuilder->GetReferenceFrame(), and the paths to those placeholders michael@0: // have already been marked as NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO. michael@0: // Note that we should still do a prune step since we don't want to michael@0: // rely on dirty-rect checking for correctness. michael@0: aExtraPage->BuildDisplayListForStackingContext(aBuilder, nsRect(), &list); michael@0: PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, &list); michael@0: aList->AppendToTop(&list); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsIFrame* michael@0: GetNextPage(nsIFrame* aPageContentFrame) michael@0: { michael@0: // XXX ugh michael@0: nsIFrame* pageFrame = aPageContentFrame->GetParent(); michael@0: NS_ASSERTION(pageFrame->GetType() == nsGkAtoms::pageFrame, michael@0: "pageContentFrame has unexpected parent"); michael@0: nsIFrame* nextPageFrame = pageFrame->GetNextSibling(); michael@0: if (!nextPageFrame) michael@0: return nullptr; michael@0: NS_ASSERTION(nextPageFrame->GetType() == nsGkAtoms::pageFrame, michael@0: "pageFrame's sibling is not a page frame..."); michael@0: nsIFrame* f = nextPageFrame->GetFirstPrincipalChild(); michael@0: NS_ASSERTION(f, "pageFrame has no page content frame!"); michael@0: NS_ASSERTION(f->GetType() == nsGkAtoms::pageContentFrame, michael@0: "pageFrame's child is not page content!"); michael@0: return f; michael@0: } michael@0: michael@0: static void PaintHeaderFooter(nsIFrame* aFrame, nsRenderingContext* aCtx, michael@0: const nsRect& aDirtyRect, nsPoint aPt) michael@0: { michael@0: static_cast(aFrame)->PaintHeaderFooter(*aCtx, aPt); michael@0: } michael@0: michael@0: static gfx3DMatrix ComputePageTransform(nsIFrame* aFrame, float aAppUnitsPerPixel) michael@0: { michael@0: float scale = aFrame->PresContext()->GetPageScale(); michael@0: return gfx3DMatrix::ScalingMatrix(scale, scale, 1); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: void michael@0: nsPageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: nsDisplayListCollection set; michael@0: michael@0: if (PresContext()->IsScreen()) { michael@0: DisplayBorderBackgroundOutline(aBuilder, aLists); michael@0: } michael@0: michael@0: nsIFrame *child = mFrames.FirstChild(); michael@0: float scale = PresContext()->GetPageScale(); michael@0: nsRect clipRect(nsPoint(0, 0), child->GetSize()); michael@0: // Note: this computation matches how we compute maxSize.height michael@0: // in nsPageFrame::Reflow michael@0: nscoord expectedPageContentHeight = NSToCoordCeil(GetSize().height / scale); michael@0: if (clipRect.height > expectedPageContentHeight) { michael@0: // We're doing print-selection, with one long page-content frame. michael@0: // Clip to the appropriate page-content slice for the current page. michael@0: NS_ASSERTION(mPageNum > 0, "page num should be positive"); michael@0: // Note: The pageContentFrame's y-position has been set such that a zero michael@0: // y-value matches the top edge of the current page. So, to clip to the michael@0: // current page's content (in coordinates *relative* to the page content michael@0: // frame), we just negate its y-position and add the top margin. michael@0: clipRect.y = NSToCoordCeil((-child->GetRect().y + michael@0: mPD->mReflowMargin.top) / scale); michael@0: clipRect.height = expectedPageContentHeight; michael@0: NS_ASSERTION(clipRect.y < child->GetSize().height, michael@0: "Should be clipping to region inside the page content bounds"); michael@0: } michael@0: clipRect += aBuilder->ToReferenceFrame(child); michael@0: michael@0: nsDisplayList content; michael@0: { michael@0: DisplayListClipState::AutoSaveRestore clipState(aBuilder); michael@0: michael@0: // Overwrite current clip, since we're going to wrap in a transform michael@0: // and the current clip is no longer meaningful. michael@0: clipState.Clear(); michael@0: clipState.ClipContainingBlockDescendants(clipRect, nullptr); michael@0: michael@0: child->BuildDisplayListForStackingContext(aBuilder, michael@0: child->GetVisualOverflowRectRelativeToSelf(), &content); michael@0: michael@0: // We may need to paint out-of-flow frames whose placeholders are michael@0: // on other pages. Add those pages to our display list. Note that michael@0: // out-of-flow frames can't be placed after their placeholders so michael@0: // we don't have to process earlier pages. The display lists for michael@0: // these extra pages are pruned so that only display items for the michael@0: // page we currently care about (which we would have reached by michael@0: // following placeholders to their out-of-flows) end up on the list. michael@0: nsIFrame* page = child; michael@0: while ((page = GetNextPage(page)) != nullptr) { michael@0: BuildDisplayListForExtraPage(aBuilder, this, page, &content); michael@0: } michael@0: michael@0: // Add the canvas background color to the bottom of the list. This michael@0: // happens after we've built the list so that AddCanvasBackgroundColorItem michael@0: // can monkey with the contents if necessary. michael@0: nsRect backgroundRect = michael@0: nsRect(aBuilder->ToReferenceFrame(child), child->GetSize()); michael@0: PresContext()->GetPresShell()->AddCanvasBackgroundColorItem( michael@0: *aBuilder, content, child, backgroundRect, NS_RGBA(0,0,0,0)); michael@0: } michael@0: michael@0: content.AppendNewToTop(new (aBuilder) nsDisplayTransform(aBuilder, child, &content, ::ComputePageTransform)); michael@0: michael@0: set.Content()->AppendToTop(&content); michael@0: michael@0: if (PresContext()->IsRootPaginatedDocument()) { michael@0: set.Content()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayGeneric(aBuilder, this, ::PaintHeaderFooter, michael@0: "HeaderFooter", michael@0: nsDisplayItem::TYPE_HEADER_FOOTER)); michael@0: } michael@0: michael@0: set.MoveTo(aLists); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: void michael@0: nsPageFrame::SetPageNumInfo(int32_t aPageNumber, int32_t aTotalPages) michael@0: { michael@0: mPageNum = aPageNumber; michael@0: mTotNumPages = aTotalPages; michael@0: } michael@0: michael@0: michael@0: void michael@0: nsPageFrame::PaintHeaderFooter(nsRenderingContext& aRenderingContext, michael@0: nsPoint aPt) michael@0: { michael@0: nsPresContext* pc = PresContext(); michael@0: michael@0: if (!mPD->mPrintSettings) { michael@0: if (pc->Type() == nsPresContext::eContext_PrintPreview || pc->IsDynamic()) michael@0: mPD->mPrintSettings = pc->GetPrintSettings(); michael@0: if (!mPD->mPrintSettings) michael@0: return; michael@0: } michael@0: michael@0: nsRect rect(aPt, mRect.Size()); michael@0: aRenderingContext.SetColor(NS_RGB(0,0,0)); michael@0: michael@0: // Get the FontMetrics to determine width.height of strings michael@0: nsRefPtr fontMet; michael@0: pc->DeviceContext()->GetMetricsFor(mPD->mHeadFootFont, nullptr, michael@0: pc->GetUserFontSet(), michael@0: pc->GetTextPerfMetrics(), michael@0: *getter_AddRefs(fontMet)); michael@0: michael@0: aRenderingContext.SetFont(fontMet); michael@0: michael@0: nscoord ascent = 0; michael@0: nscoord visibleHeight = 0; michael@0: if (fontMet) { michael@0: visibleHeight = fontMet->MaxHeight(); michael@0: ascent = fontMet->MaxAscent(); michael@0: } michael@0: michael@0: // print document headers and footers michael@0: nsXPIDLString headerLeft, headerCenter, headerRight; michael@0: mPD->mPrintSettings->GetHeaderStrLeft(getter_Copies(headerLeft)); michael@0: mPD->mPrintSettings->GetHeaderStrCenter(getter_Copies(headerCenter)); michael@0: mPD->mPrintSettings->GetHeaderStrRight(getter_Copies(headerRight)); michael@0: DrawHeaderFooter(aRenderingContext, eHeader, michael@0: headerLeft, headerCenter, headerRight, michael@0: rect, ascent, visibleHeight); michael@0: michael@0: nsXPIDLString footerLeft, footerCenter, footerRight; michael@0: mPD->mPrintSettings->GetFooterStrLeft(getter_Copies(footerLeft)); michael@0: mPD->mPrintSettings->GetFooterStrCenter(getter_Copies(footerCenter)); michael@0: mPD->mPrintSettings->GetFooterStrRight(getter_Copies(footerRight)); michael@0: DrawHeaderFooter(aRenderingContext, eFooter, michael@0: footerLeft, footerCenter, footerRight, michael@0: rect, ascent, visibleHeight); michael@0: } michael@0: michael@0: void michael@0: nsPageFrame::SetSharedPageData(nsSharedPageData* aPD) michael@0: { michael@0: mPD = aPD; michael@0: // Set the shared data into the page frame before reflow michael@0: nsPageContentFrame * pcf = static_cast(mFrames.FirstChild()); michael@0: if (pcf) { michael@0: pcf->SetSharedPageData(mPD); michael@0: } michael@0: michael@0: } michael@0: michael@0: nsIFrame* michael@0: NS_NewPageBreakFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: NS_PRECONDITION(aPresShell, "null PresShell"); michael@0: //check that we are only creating page break frames when printing michael@0: NS_ASSERTION(aPresShell->GetPresContext()->IsPaginated(), "created a page break frame while not printing"); michael@0: michael@0: return new (aPresShell) nsPageBreakFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsPageBreakFrame) michael@0: michael@0: nsPageBreakFrame::nsPageBreakFrame(nsStyleContext* aContext) : michael@0: nsLeafFrame(aContext), mHaveReflowed(false) michael@0: { michael@0: } michael@0: michael@0: nsPageBreakFrame::~nsPageBreakFrame() michael@0: { michael@0: } michael@0: michael@0: nscoord michael@0: nsPageBreakFrame::GetIntrinsicWidth() michael@0: { michael@0: return nsPresContext::CSSPixelsToAppUnits(1); michael@0: } michael@0: michael@0: nscoord michael@0: nsPageBreakFrame::GetIntrinsicHeight() michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: nsresult michael@0: nsPageBreakFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsPageBreakFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: michael@0: // Override reflow, since we don't want to deal with what our michael@0: // computed values are. michael@0: aDesiredSize.Width() = GetIntrinsicWidth(); michael@0: aDesiredSize.Height() = (aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE ? michael@0: 0 : aReflowState.AvailableHeight()); michael@0: // round the height down to the nearest pixel michael@0: aDesiredSize.Height() -= michael@0: aDesiredSize.Height() % nsPresContext::CSSPixelsToAppUnits(1); michael@0: michael@0: // Note: not using NS_FRAME_FIRST_REFLOW here, since it's not clear whether michael@0: // DidReflow will always get called before the next Reflow() call. michael@0: mHaveReflowed = true; michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsPageBreakFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::pageBreakFrame; michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsPageBreakFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("PageBreak"), aResult); michael@0: } michael@0: #endif