Bug 1652278 part 2: Flesh out PrintedSheetFrame implementation so that it serves as the container for nsPageFrame. r=TYLin

This patch:
- Creates an anon-box pseudo-style for PrintedSheetFrame, in part so that it
  can co-opt the styles that we formerly gave to page-frames in ua.css, to draw
  the sheet of paper and the shadow in Print Preview.
- Adjusts nsCSSFrameConstructor to create a PrintedSheetFrame as the parent of
  nsPageFrame (inserting between it and its nsPageSequenceFrame container, in
  the frame tree).
- Fleshes out out a simple BuildDisplayList() implementation for
  PrintedSheetFrame (taking the responsibility for "paper"-drawing from
  nsPageFrame).
- Fleshes out a simple Reflow implementation for PrintedSheetFrame, just
  placing the child page (assuming there's only one for now) at the origin.
- Adjusts nsPageFrame and nsPageSequenceFrame to account for the fact that
  there's another layer between them now.

Note that PrintedSheetFrame needs to implement AppendDirectlyOwnedAnonBoxes()
(just as nsSimplePageSequence and nsPageFrame do), since it owns anonymous
nsPageFrame instances. This implementation only needs to append the first
child, as explained in the code-comment and in
https://bugzilla.mozilla.org/show_bug.cgi?id=1374761#c9 (and of course, for
now, PrintedSheetFrame only has one child at a time anyway.)

Differential Revision: https://phabricator.services.mozilla.com/D83457
This commit is contained in:
Daniel Holbert 2020-08-05 03:28:32 +00:00
Родитель 41ce298d55
Коммит 96d67c6eb3
10 изменённых файлов: 238 добавлений и 50 удалений

Просмотреть файл

@ -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<ComputedStyle> 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<ComputedStyle> pagePseudoStyle;
pagePseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle(
PseudoStyleType::page, parentComputedStyle);
RefPtr<ComputedStyle> 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) {

Просмотреть файл

@ -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,

Просмотреть файл

@ -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<nsPageFrame*>(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<OwnedAnonBox>& 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

Просмотреть файл

@ -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<OwnedAnonBox>& 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

Просмотреть файл

@ -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();

Просмотреть файл

@ -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<nsPageFrame*>(kidFrame);
pf->SetSharedPageData(mPageData.get());
MOZ_ASSERT(kidFrame->IsPrintedSheetFrame(),
"we're only expecting PrintedSheetFrame as children");
auto* sheet = static_cast<PrintedSheetFrame*>(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<nsPageFrame*>(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<nsPageFrame*>(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<OwnedAnonBox>& 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());
}

Просмотреть файл

@ -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<OwnedAnonBox>& aResult) override;

Просмотреть файл

@ -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")

Просмотреть файл

@ -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;

Просмотреть файл

@ -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"),