diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index e7d945c4d6bc..e8c3667691c8 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -29,6 +29,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/PresShell.h" #include "mozilla/PresShellInlines.h" +#include "mozilla/PrintedSheetFrame.h" #include "mozilla/ServoBindings.h" #include "mozilla/ServoStyleSetInlines.h" #include "mozilla/StaticPrefs_layout.h" @@ -2467,22 +2468,24 @@ void nsCSSFrameConstructor::SetUpDocElementContainingBlock( ViewportFrame nsPageSequenceFrame - nsPageFrame - nsPageContentFrame [fixed-cb] - nsCanvasFrame [abs-cb] - root element frame (nsBlockFrame, SVGOuterSVGFrame, - nsTableWrapperFrame, nsPlaceholderFrame) + PrintedSheetFrame + nsPageFrame + nsPageContentFrame [fixed-cb] + nsCanvasFrame [abs-cb] + root element frame (nsBlockFrame, SVGOuterSVGFrame, + nsTableWrapperFrame, nsPlaceholderFrame) Print-preview presentation, non-XUL ViewportFrame nsHTMLScrollFrame nsPageSequenceFrame - nsPageFrame - nsPageContentFrame [fixed-cb] - nsCanvasFrame [abs-cb] - root element frame (nsBlockFrame, SVGOuterSVGFrame, - nsTableWrapperFrame, nsPlaceholderFrame) + PrintedSheetFrame + nsPageFrame + nsPageContentFrame [fixed-cb] + nsCanvasFrame [abs-cb] + root element frame (nsBlockFrame, SVGOuterSVGFrame, + nsTableWrapperFrame, nsPlaceholderFrame) Print/print preview of XUL is not supported. [fixed-cb]: the default containing block for fixed-pos content @@ -2628,13 +2631,20 @@ void nsCSSFrameConstructor::SetUpDocElementContainingBlock( } if (isPaginated) { - // Create the first page - // Set the initial child lists + // Create the first printed sheet frame, as the sole child (for now) of our + // page sequence frame (rootFrame). + auto* printedSheetFrame = + ConstructPrintedSheetFrame(mPresShell, rootFrame, nullptr); + printedSheetFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); + SetInitialSingleChild(rootFrame, printedSheetFrame); + + // Create the first page, as the sole child (for now) of the printed sheet + // frame that we just created. nsContainerFrame* canvasFrame; nsContainerFrame* pageFrame = - ConstructPageFrame(mPresShell, rootFrame, nullptr, canvasFrame); + ConstructPageFrame(mPresShell, printedSheetFrame, nullptr, canvasFrame); pageFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); - SetInitialSingleChild(rootFrame, pageFrame); + SetInitialSingleChild(printedSheetFrame, pageFrame); // The eventual parent of the document element frame. // XXX should this be set for every new page (in ConstructPageFrame)? @@ -2671,15 +2681,33 @@ void nsCSSFrameConstructor::ConstructAnonymousContentForCanvas( aFrameList); } +PrintedSheetFrame* nsCSSFrameConstructor::ConstructPrintedSheetFrame( + PresShell* aPresShell, nsContainerFrame* aParentFrame, + nsIFrame* aPrevSheetFrame) { + ComputedStyle* parentComputedStyle = aParentFrame->Style(); + ServoStyleSet* styleSet = aPresShell->StyleSet(); + + RefPtr printedSheetPseudoStyle = + styleSet->ResolveInheritingAnonymousBoxStyle( + PseudoStyleType::printedSheet, parentComputedStyle); + + auto* printedSheetFrame = + NS_NewPrintedSheetFrame(aPresShell, printedSheetPseudoStyle); + + printedSheetFrame->Init(nullptr, aParentFrame, aPrevSheetFrame); + + return printedSheetFrame; +} + nsContainerFrame* nsCSSFrameConstructor::ConstructPageFrame( PresShell* aPresShell, nsContainerFrame* aParentFrame, nsIFrame* aPrevPageFrame, nsContainerFrame*& aCanvasFrame) { ComputedStyle* parentComputedStyle = aParentFrame->Style(); ServoStyleSet* styleSet = aPresShell->StyleSet(); - RefPtr pagePseudoStyle; - pagePseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle( - PseudoStyleType::page, parentComputedStyle); + RefPtr pagePseudoStyle = + styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::page, + parentComputedStyle); nsContainerFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle); @@ -7931,8 +7959,10 @@ nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame( "no support for fragmenting table captions yet"); newFrame = NS_NewColumnSetFrame(mPresShell, computedStyle, nsFrameState(0)); newFrame->Init(content, aParentFrame, aFrame); + } else if (LayoutFrameType::PrintedSheet == frameType) { + newFrame = ConstructPrintedSheetFrame(mPresShell, aParentFrame, aFrame); } else if (LayoutFrameType::Page == frameType) { - nsContainerFrame* canvasFrame; + nsContainerFrame* canvasFrame; // (unused outparam for ConstructPageFrame) newFrame = ConstructPageFrame(mPresShell, aParentFrame, aFrame, canvasFrame); } else if (LayoutFrameType::TableWrapper == frameType) { diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 3482cc23a0ae..ae8829d8fe63 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -44,6 +44,7 @@ namespace mozilla { class ComputedStyle; class PresShell; +class PrintedSheetFrame; namespace dom { @@ -348,6 +349,10 @@ class nsCSSFrameConstructor final : public nsFrameManager { nsContainerFrame* aRootElementFrame, nsFrameConstructorState&, nsFrameList&); + mozilla::PrintedSheetFrame* ConstructPrintedSheetFrame( + PresShell* aPresShell, nsContainerFrame* aParentFrame, + nsIFrame* aPrevSheetFrame); + nsContainerFrame* ConstructPageFrame(PresShell* aPresShell, nsContainerFrame* aParentFrame, nsIFrame* aPrevPageFrame, diff --git a/layout/generic/PrintedSheetFrame.cpp b/layout/generic/PrintedSheetFrame.cpp index 54ca9f77f06d..6131af4abb00 100644 --- a/layout/generic/PrintedSheetFrame.cpp +++ b/layout/generic/PrintedSheetFrame.cpp @@ -8,6 +8,9 @@ #include "PrintedSheetFrame.h" +#include "nsCSSFrameConstructor.h" +#include "nsPageFrame.h" + using namespace mozilla; PrintedSheetFrame* NS_NewPrintedSheetFrame(PresShell* aPresShell, @@ -26,14 +29,116 @@ NS_IMPL_FRAMEARENA_HELPERS(PrintedSheetFrame) void PrintedSheetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { - // TODO: Implement me + if (PresContext()->IsScreen()) { + // Draw the background/shadow/etc. of a blank sheet of paper, for + // print-preview. + DisplayBorderBackgroundOutline(aBuilder, aLists); + } + + // Let each of our children (pages) draw itself: + for (auto* frame : mFrames) { + BuildDisplayListForChild(aBuilder, frame, aLists); + } } void PrintedSheetFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aReflowOutput, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { - // TODO: Implement me + MarkInReflow(); + DO_GLOBAL_REFLOW_COUNT("PrintedSheetFrame"); + DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); + MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); + + // If we have a prev-in-flow, take its overflowing content: + MoveOverflowToChildList(); + + const WritingMode wm = aReflowInput.GetWritingMode(); + + // Both the sheet and the pages will use this size: + const nsSize physPageSize = aPresContext->GetPageSize(); + const LogicalSize pageSize(wm, physPageSize); + + // Until bug 1631452, we should only have one child (which we might've just + // pulled from our prev-in-flow's overflow list). That means the loop below + // is trivial & will will run exactly once, for now. + MOZ_ASSERT(mFrames.GetLength() == 1, + "how did we get more than one page per sheet"); + for (auto* pageFrame : mFrames) { + MOZ_ASSERT(pageFrame->IsPageFrame(), + "we're only expecting page frames as children"); + // Be sure our child has a pointer to the nsSharedPageData: + static_cast(pageFrame)->SetSharedPageData(mPD); + + ReflowInput pageReflowInput(aPresContext, aReflowInput, pageFrame, + pageSize); + // For now, we'll always position the pageFrame at our origin. (This will + // change in bug 1631452 when we support more than 1 page per sheet.) + LogicalPoint pagePos(wm); + + // Outparams for reflow: + ReflowOutput pageReflowOutput(pageReflowInput); + nsReflowStatus status; + + ReflowChild(pageFrame, aPresContext, pageReflowOutput, pageReflowInput, wm, + pagePos, physPageSize, ReflowChildFlags::Default, status); + + FinishReflowChild(pageFrame, aPresContext, pageReflowOutput, + &pageReflowInput, wm, pagePos, physPageSize, + ReflowChildFlags::Default); + + // Did this page complete the document, or do we need to generate + // another page frame? + nsIFrame* pageNextInFlow = pageFrame->GetNextInFlow(); + if (status.IsFullyComplete()) { + // Normally, we (the parent frame) would be responsible for deleting the + // next-in-flow of our fully-complete children. But since we don't + // support dynamic changes / incremental reflow for printing (and since + // the reflow operation is single-pass at this level of granularity), our + // child *shouldn't have had any opportunities* to end up with a + // next-in-flow that would need cleaning up here. + NS_ASSERTION(!pageNextInFlow, "bad child flow list"); + } else { + // Our page frame child needs a continuation. For now, that makes us need + // one as well. (This will change in bug 1631452 when we support more + // than 1 page per sheet; at that point, we'll only want to do this when + // we max out the number of pages that are allowed on our sheet.) + aStatus.SetIncomplete(); + + if (!pageNextInFlow) { + // We need to create a continuation for our page frame. We add the + // continuation to our child list, and then push it to our overflow + // list so that our own (perhaps yet-to-be-created) continuation can + // pull it forward. + nsIFrame* continuingPage = + PresShell()->FrameConstructor()->CreateContinuingFrame(pageFrame, + this); + mFrames.InsertFrame(nullptr, pageFrame, continuingPage); + PushChildrenToOverflow(continuingPage, pageFrame); + } + } + } + + // Populate our ReflowOutput outparam -- just use up all the + // available space, for both our desired size & overflow areas. + aReflowOutput.ISize(wm) = aReflowInput.AvailableISize(); + if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) { + aReflowOutput.BSize(wm) = aReflowInput.AvailableBSize(); + } + aReflowOutput.SetOverflowAreasToDesiredBounds(); + + FinishAndStoreOverflow(&aReflowOutput); + NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aReflowOutput); +} + +void PrintedSheetFrame::AppendDirectlyOwnedAnonBoxes( + nsTArray& aResult) { + MOZ_ASSERT(mFrames.FirstChild() && mFrames.FirstChild()->IsPageFrame(), + "PrintedSheetFrame must have a nsPageFrame child"); + // Only append the first child; all our children are expected to be + // continuations of each other, and our anon box handling always walks + // continuations. + aResult.AppendElement(mFrames.FirstChild()); } #ifdef DEBUG_FRAME_DUMP diff --git a/layout/generic/PrintedSheetFrame.h b/layout/generic/PrintedSheetFrame.h index de9c6a9906a5..4c4ce4113714 100644 --- a/layout/generic/PrintedSheetFrame.h +++ b/layout/generic/PrintedSheetFrame.h @@ -12,6 +12,8 @@ #include "nsContainerFrame.h" #include "nsHTMLParts.h" +class nsSharedPageData; + namespace mozilla { class PrintedSheetFrame final : public nsContainerFrame { @@ -22,6 +24,8 @@ class PrintedSheetFrame final : public nsContainerFrame { friend PrintedSheetFrame* ::NS_NewPrintedSheetFrame( mozilla::PresShell* aPresShell, ComputedStyle* aStyle); + void SetSharedPageData(nsSharedPageData* aPD) { mPD = aPD; } + // nsIFrame overrides void Reflow(nsPresContext* aPresContext, ReflowOutput& aReflowOutput, const ReflowInput& aReflowInput, @@ -30,6 +34,9 @@ class PrintedSheetFrame final : public nsContainerFrame { void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; + // Return our first page frame. + void AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) override; + #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const override; #endif @@ -40,6 +47,10 @@ class PrintedSheetFrame final : public nsContainerFrame { PrintedSheetFrame(ComputedStyle* aStyle, nsPresContext* aPresContext) : nsContainerFrame(aStyle, aPresContext, kClassID) {} ~PrintedSheetFrame() = default; + + // Note: this will be set before reflow, and it's strongly owned by our + // nsPageSequenceFrame, which outlives us. + nsSharedPageData* mPD = nullptr; }; } // namespace mozilla diff --git a/layout/generic/nsPageFrame.cpp b/layout/generic/nsPageFrame.cpp index ac24da538142..5e019822f2cd 100644 --- a/layout/generic/nsPageFrame.cpp +++ b/layout/generic/nsPageFrame.cpp @@ -526,9 +526,6 @@ void nsPageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayListCollection set(aBuilder); nsPresContext* pc = PresContext(); - if (pc->IsScreen()) { - DisplayBorderBackgroundOutline(aBuilder, aLists); - } nsIFrame* child = mFrames.FirstChild(); float scale = pc->GetPageScale(); diff --git a/layout/generic/nsPageSequenceFrame.cpp b/layout/generic/nsPageSequenceFrame.cpp index bae5638a1097..60c426fdc9d3 100644 --- a/layout/generic/nsPageSequenceFrame.cpp +++ b/layout/generic/nsPageSequenceFrame.cpp @@ -8,6 +8,7 @@ #include "mozilla/Logging.h" #include "mozilla/PresShell.h" +#include "mozilla/PrintedSheetFrame.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/StaticPresData.h" @@ -238,19 +239,21 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext, mPageData->mReflowSize = pageSize; mPageData->mReflowMargin = mMargin; - // We use the CSS "margin" property on the -moz-page pseudoelement - // to determine the space between each page in print preview. - // Keep a running y-offset for each page. + // We use the CSS "margin" property on the -moz-printed-sheet pseudoelement + // to determine the space between each printed sheet in print preview. + // Keep a running y-offset for each printed sheet. nscoord y = 0; nscoord maxXMost = 0; - // Tile the pages vertically + // Tile the sheets vertically for (nsIFrame* kidFrame : mFrames) { // Set the shared data into the page frame before reflow - auto* pf = static_cast(kidFrame); - pf->SetSharedPageData(mPageData.get()); + MOZ_ASSERT(kidFrame->IsPrintedSheetFrame(), + "we're only expecting PrintedSheetFrame as children"); + auto* sheet = static_cast(kidFrame); + sheet->SetSharedPageData(mPageData.get()); - // Reflow the page + // Reflow the sheet ReflowInput kidReflowInput( aPresContext, aReflowInput, kidFrame, LogicalSize(kidFrame->GetWritingMode(), pageSize)); @@ -267,7 +270,7 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext, nscoord x = pageCSSMargin.left; - // Place and size the page. + // Place and size the sheet. ReflowChild(kidFrame, aPresContext, kidReflowOutput, kidReflowInput, x, y, ReflowChildFlags::Default, status); @@ -279,37 +282,46 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext, maxXMost = std::max(maxXMost, x + kidReflowOutput.Width() + pageCSSMargin.right); - // Is the page complete? + // Is the sheet complete? nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow(); if (status.IsFullyComplete()) { NS_ASSERTION(!kidNextInFlow, "bad child flow list"); } else if (!kidNextInFlow) { - // The page isn't complete and it doesn't have a next-in-flow, so - // create a continuing page. - nsIFrame* continuingPage = + // The sheet isn't complete and it doesn't have a next-in-flow, so + // create a continuing sheet. + nsIFrame* continuingSheet = PresShell()->FrameConstructor()->CreateContinuingFrame(kidFrame, this); // Add it to our child list - mFrames.InsertFrame(nullptr, kidFrame, continuingPage); + mFrames.InsertFrame(nullptr, kidFrame, continuingSheet); } } // Get Total Page Count + // XXXdholbert When we support multiple pages-per-sheet in bug 1631452, + // we'll need to be sure to update this computation to account for the + // sheet-vs-page distinction. // XXXdholbert technically we could calculate this in the loop above, // instead of needing a separate walk. int32_t pageTot = mFrames.GetLength(); // Set Page Number Info int32_t pageNum = 1; - for (nsIFrame* child : mFrames) { - MOZ_ASSERT(child->IsPageFrame(), - "only expecting nsPageFrame children. Other children will make " - "this static_cast bogus & probably violate other assumptions"); - auto* pf = static_cast(child); - pf->SetPageNumInfo(pageNum, pageTot); - pageNum++; + for (nsIFrame* sheetFrame : mFrames) { + MOZ_ASSERT(sheetFrame->IsPrintedSheetFrame(), + "only expecting PrintedSheetFrame children"); + for (nsIFrame* pageFrame : sheetFrame->PrincipalChildList()) { + MOZ_ASSERT( + pageFrame->IsPageFrame(), + "only expecting nsPageFrame grandchildren. Other types will make " + "this static_cast bogus & probably violate other assumptions"); + static_cast(pageFrame)->SetPageNumInfo(pageNum, pageTot); + // XXXdholbert Perhaps we'll need to keep track of the page count *and* + // the printed sheet count? + pageNum++; + } } nsAutoString formattedDateString; @@ -446,6 +458,25 @@ static void GetPrintCanvasElementsInFrame( } } +// XXXdholbert The next four functions[1] exist to support functionality to let +// us include/skip various pages (e.g. only print even pages, or page 7, or the +// page-range "1,4,6-8"). These four functions are all named in terms of +// "page", which is somewhat problematic now that we've got a level of +// indirection between us and the actual page frames; but in practice, they're +// still valid & fine as long as there's exactly one nsPageFrame per +// PrintedSheetFrame (as there is right now). +// +// When we implement built-in support for N Pages Per Sheet (bug 1631452), +// we'll need this logic to cooperate with PrintedSheetFrame, so that a given +// PrintedSheetFrame instance can figure out which of the upcoming pages it +// should include/skip in its N tiny pages. We may need to change our calling +// pattern all the way up to nsPagePrintTimer, too (which currently has a +// repeating timer that fires to prompt us to print each successive page, with +// a variable delay between firings, depending on whether the last page was +// skipped. +// +// [1] DetermineWhetherToPrintPage(), GetCurrentPageFrame(), +// PrePrintNextPage(), and PrintNextPage() void nsPageSequenceFrame::DetermineWhetherToPrintPage() { // See whether we should print this page mPrintThisPage = true; @@ -737,7 +768,11 @@ void nsPageSequenceFrame::SetDateTimeStr(const nsAString& aDateTimeStr) { void nsPageSequenceFrame::AppendDirectlyOwnedAnonBoxes( nsTArray& aResult) { - if (mFrames.NotEmpty()) { - aResult.AppendElement(mFrames.FirstChild()); - } + MOZ_ASSERT( + mFrames.FirstChild() && mFrames.FirstChild()->IsPrintedSheetFrame(), + "nsPageSequenceFrame must have a PrintedSheetFrame child"); + // Only append the first child; all our children are expected to be + // continuations of each other, and our anon box handling always walks + // continuations. + aResult.AppendElement(mFrames.FirstChild()); } diff --git a/layout/generic/nsPageSequenceFrame.h b/layout/generic/nsPageSequenceFrame.h index 8fbb31920467..50044b607857 100644 --- a/layout/generic/nsPageSequenceFrame.h +++ b/layout/generic/nsPageSequenceFrame.h @@ -55,6 +55,9 @@ class nsSharedPageData { }; // Page sequence frame class. Manages a series of pages, in paginated mode. +// (Strictly speaking, this frame's direct children are PrintedSheetFrame +// instances, and each of those will usually contain one nsPageFrame, depending +// on the "pages-per-sheet" setting.) class nsPageSequenceFrame final : public nsContainerFrame { public: friend nsPageSequenceFrame* NS_NewPageSequenceFrame( @@ -98,7 +101,7 @@ class nsPageSequenceFrame final : public nsContainerFrame { bool HasTransformGetter() const override { return true; } /** - * Return our first page frame. + * Return our first sheet frame. */ void AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) override; diff --git a/layout/style/nsCSSAnonBoxList.h b/layout/style/nsCSSAnonBoxList.h index f515e3db4c8b..ab16ef66344d 100644 --- a/layout/style/nsCSSAnonBoxList.h +++ b/layout/style/nsCSSAnonBoxList.h @@ -111,6 +111,7 @@ CSS_ANON_BOX(canvas, ":-moz-canvas") CSS_ANON_BOX(page, ":-moz-page") CSS_ANON_BOX(pageContent, ":-moz-pagecontent") CSS_ANON_BOX(pageSequence, ":-moz-page-sequence") +CSS_ANON_BOX(printedSheet, ":-moz-printed-sheet") CSS_ANON_BOX(scrolledContent, ":-moz-scrolled-content") CSS_ANON_BOX(scrolledCanvas, ":-moz-scrolled-canvas") CSS_ANON_BOX(scrolledPageSequence, ":-moz-scrolled-page-sequence") diff --git a/layout/style/res/ua.css b/layout/style/res/ua.css index a63ca0b964e5..3a65953e3411 100644 --- a/layout/style/res/ua.css +++ b/layout/style/res/ua.css @@ -301,9 +301,9 @@ height: 100%; } -*|*::-moz-page { - /* Individual page in print/print preview. Visual styles may only appear - * in print preview. */ +*|*::-moz-printed-sheet { + /* Individual sheet of paper in print/print preview. Visual styles may only + * appear in print preview. */ display: block; background: white; box-shadow: 5px 5px 8px #202020; diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py index bbec5a648eed..49987ee4b5ed 100644 --- a/xpcom/ds/StaticAtoms.py +++ b/xpcom/ds/StaticAtoms.py @@ -2515,6 +2515,7 @@ STATIC_ATOMS = [ InheritingAnonBoxAtom("AnonBox_page", ":-moz-page"), InheritingAnonBoxAtom("AnonBox_pageContent", ":-moz-pagecontent"), InheritingAnonBoxAtom("AnonBox_pageSequence", ":-moz-page-sequence"), + InheritingAnonBoxAtom("AnonBox_printedSheet", ":-moz-printed-sheet"), InheritingAnonBoxAtom("AnonBox_scrolledContent", ":-moz-scrolled-content"), InheritingAnonBoxAtom("AnonBox_scrolledCanvas", ":-moz-scrolled-canvas"), InheritingAnonBoxAtom("AnonBox_scrolledPageSequence", ":-moz-scrolled-page-sequence"),