1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsPageFrame.h"
8
9 #include "mozilla/PresShell.h"
10 #include "mozilla/gfx/2D.h"
11 #include "gfxContext.h"
12 #include "nsDeviceContext.h"
13 #include "nsFontMetrics.h"
14 #include "nsLayoutUtils.h"
15 #include "nsPresContext.h"
16 #include "nsGkAtoms.h"
17 #include "nsFieldSetFrame.h"
18 #include "nsPageContentFrame.h"
19 #include "nsDisplayList.h"
20 #include "nsPageSequenceFrame.h" // for nsSharedPageData
21 #include "nsTextFormatter.h" // for page number localization formatting
22 #include "nsBidiUtils.h"
23 #include "nsIPrintSettings.h"
24
25 #include "mozilla/Logging.h"
26 extern mozilla::LazyLogModule gLayoutPrintingLog;
27 #define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1)
28
29 using namespace mozilla;
30 using namespace mozilla::gfx;
31
NS_NewPageFrame(PresShell * aPresShell,ComputedStyle * aStyle)32 nsPageFrame* NS_NewPageFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
33 return new (aPresShell) nsPageFrame(aStyle, aPresShell->GetPresContext());
34 }
35
36 NS_IMPL_FRAMEARENA_HELPERS(nsPageFrame)
37
NS_QUERYFRAME_HEAD(nsPageFrame)38 NS_QUERYFRAME_HEAD(nsPageFrame)
39 NS_QUERYFRAME_ENTRY(nsPageFrame)
40 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
41
42 nsPageFrame::nsPageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
43 : nsContainerFrame(aStyle, aPresContext, kClassID) {}
44
45 nsPageFrame::~nsPageFrame() = default;
46
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)47 void nsPageFrame::Reflow(nsPresContext* aPresContext,
48 ReflowOutput& aDesiredSize,
49 const ReflowInput& aReflowInput,
50 nsReflowStatus& aStatus) {
51 MarkInReflow();
52 DO_GLOBAL_REFLOW_COUNT("nsPageFrame");
53 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
54 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
55
56 NS_ASSERTION(
57 mFrames.FirstChild() && mFrames.FirstChild()->IsPageContentFrame(),
58 "pageFrame must have a pageContentFrame child");
59
60 // Resize our frame allowing it only to be as big as we are
61 // XXX Pay attention to the page's border and padding...
62 if (mFrames.NotEmpty()) {
63 nsIFrame* frame = mFrames.FirstChild();
64 // When the reflow size is NS_UNCONSTRAINEDSIZE it means we are reflowing
65 // a single page to print selection. So this means we want to use
66 // NS_UNCONSTRAINEDSIZE without altering it
67 nscoord avHeight;
68 if (mPD->mReflowSize.height == NS_UNCONSTRAINEDSIZE) {
69 avHeight = NS_UNCONSTRAINEDSIZE;
70 } else {
71 avHeight = mPD->mReflowSize.height;
72 }
73 nsSize maxSize(mPD->mReflowSize.width, avHeight);
74 float scale = aPresContext->GetPageScale();
75 maxSize.width = NSToCoordCeil(maxSize.width / scale);
76 if (maxSize.height != NS_UNCONSTRAINEDSIZE) {
77 maxSize.height = NSToCoordCeil(maxSize.height / scale);
78 }
79 // Get the number of Twips per pixel from the PresContext
80 nscoord onePixelInTwips = nsPresContext::CSSPixelsToAppUnits(1);
81 // insurance against infinite reflow, when reflowing less than a pixel
82 // XXX Shouldn't we do something more friendly when invalid margins
83 // are set?
84 if (maxSize.width < onePixelInTwips || maxSize.height < onePixelInTwips) {
85 aDesiredSize.ClearSize();
86 NS_WARNING("Reflow aborted; no space for content");
87 return;
88 }
89
90 ReflowInput kidReflowInput(aPresContext, aReflowInput, frame,
91 LogicalSize(frame->GetWritingMode(), maxSize));
92 kidReflowInput.mFlags.mIsTopOfPage = true;
93 kidReflowInput.mFlags.mTableIsSplittable = true;
94
95 // Use the margins given in the @page rule.
96 // If a margin is 'auto', use the margin from the print settings for that
97 // side.
98 const auto& marginStyle = kidReflowInput.mStyleMargin->mMargin;
99 for (const auto side : mozilla::AllPhysicalSides()) {
100 if (marginStyle.Get(side).IsAuto()) {
101 mPageContentMargin.Side(side) = mPD->mReflowMargin.Side(side);
102 } else {
103 mPageContentMargin.Side(side) =
104 kidReflowInput.ComputedPhysicalMargin().Side(side);
105 }
106 }
107
108 nscoord maxWidth = maxSize.width - mPageContentMargin.LeftRight() / scale;
109 nscoord maxHeight;
110 if (maxSize.height == NS_UNCONSTRAINEDSIZE) {
111 maxHeight = NS_UNCONSTRAINEDSIZE;
112 } else {
113 maxHeight = maxSize.height - mPageContentMargin.TopBottom() / scale;
114 }
115
116 // Check the width and height, if they're too small we reset the margins
117 // back to the default.
118 if (maxWidth < onePixelInTwips ||
119 (maxHeight != NS_UNCONSTRAINEDSIZE && maxHeight < onePixelInTwips)) {
120 for (const auto side : mozilla::AllPhysicalSides()) {
121 mPageContentMargin.Side(side) = mPD->mReflowMargin.Side(side);
122 }
123 maxWidth = maxSize.width - mPageContentMargin.LeftRight() / scale;
124 if (maxHeight != NS_UNCONSTRAINEDSIZE) {
125 maxHeight = maxSize.height - mPageContentMargin.TopBottom() / scale;
126 }
127 }
128
129 kidReflowInput.SetComputedWidth(maxWidth);
130 kidReflowInput.SetComputedHeight(maxHeight);
131
132 // calc location of frame
133 nscoord xc = mPageContentMargin.left;
134 nscoord yc = mPageContentMargin.top;
135
136 // Get the child's desired size
137 ReflowChild(frame, aPresContext, aDesiredSize, kidReflowInput, xc, yc,
138 ReflowChildFlags::Default, aStatus);
139
140 // Place and size the child
141 FinishReflowChild(frame, aPresContext, aDesiredSize, &kidReflowInput, xc,
142 yc, ReflowChildFlags::Default);
143
144 NS_ASSERTION(!aStatus.IsFullyComplete() || !frame->GetNextInFlow(),
145 "bad child flow list");
146 }
147 PR_PL(("PageFrame::Reflow %p ", this));
148 PR_PL(("[%d,%d][%d,%d]\n", aDesiredSize.Width(), aDesiredSize.Height(),
149 aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
150
151 // Return our desired size
152 WritingMode wm = aReflowInput.GetWritingMode();
153 aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
154 if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
155 aDesiredSize.BSize(wm) = aReflowInput.AvailableBSize();
156 }
157
158 aDesiredSize.SetOverflowAreasToDesiredBounds();
159 FinishAndStoreOverflow(&aDesiredSize);
160
161 PR_PL(("PageFrame::Reflow %p ", this));
162 PR_PL(("[%d,%d]\n", aReflowInput.AvailableWidth(),
163 aReflowInput.AvailableHeight()));
164
165 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
166 }
167
168 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const169 nsresult nsPageFrame::GetFrameName(nsAString& aResult) const {
170 return MakeFrameName(NS_LITERAL_STRING("Page"), aResult);
171 }
172 #endif
173
ProcessSpecialCodes(const nsString & aStr,nsString & aNewStr)174 void nsPageFrame::ProcessSpecialCodes(const nsString& aStr, nsString& aNewStr) {
175 aNewStr = aStr;
176
177 // Search to see if the &D code is in the string
178 // then subst in the current date/time
179 NS_NAMED_LITERAL_STRING(kDate, "&D");
180 if (aStr.Find(kDate) != kNotFound) {
181 aNewStr.ReplaceSubstring(kDate, mPD->mDateTimeStr);
182 }
183
184 // NOTE: Must search for &PT before searching for &P
185 //
186 // Search to see if the "page number and page" total code are in the string
187 // and replace the page number and page total code with the actual
188 // values
189 NS_NAMED_LITERAL_STRING(kPageAndTotal, "&PT");
190 if (aStr.Find(kPageAndTotal) != kNotFound) {
191 nsAutoString uStr;
192 nsTextFormatter::ssprintf(uStr, mPD->mPageNumAndTotalsFormat.get(),
193 mPageNum, mTotNumPages);
194 aNewStr.ReplaceSubstring(kPageAndTotal, uStr);
195 }
196
197 // Search to see if the page number code is in the string
198 // and replace the page number code with the actual value
199 NS_NAMED_LITERAL_STRING(kPage, "&P");
200 if (aStr.Find(kPage) != kNotFound) {
201 nsAutoString uStr;
202 nsTextFormatter::ssprintf(uStr, mPD->mPageNumFormat.get(), mPageNum);
203 aNewStr.ReplaceSubstring(kPage, uStr);
204 }
205
206 NS_NAMED_LITERAL_STRING(kTitle, "&T");
207 if (aStr.Find(kTitle) != kNotFound) {
208 aNewStr.ReplaceSubstring(kTitle, mPD->mDocTitle);
209 }
210
211 NS_NAMED_LITERAL_STRING(kDocURL, "&U");
212 if (aStr.Find(kDocURL) != kNotFound) {
213 aNewStr.ReplaceSubstring(kDocURL, mPD->mDocURL);
214 }
215
216 NS_NAMED_LITERAL_STRING(kPageTotal, "&L");
217 if (aStr.Find(kPageTotal) != kNotFound) {
218 nsAutoString uStr;
219 nsTextFormatter::ssprintf(uStr, mPD->mPageNumFormat.get(), mTotNumPages);
220 aNewStr.ReplaceSubstring(kPageTotal, uStr);
221 }
222 }
223
224 //------------------------------------------------------------------------------
GetXPosition(gfxContext & aRenderingContext,nsFontMetrics & aFontMetrics,const nsRect & aRect,int32_t aJust,const nsString & aStr)225 nscoord nsPageFrame::GetXPosition(gfxContext& aRenderingContext,
226 nsFontMetrics& aFontMetrics,
227 const nsRect& aRect, int32_t aJust,
228 const nsString& aStr) {
229 nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(
230 aStr, this, aFontMetrics, aRenderingContext);
231 nscoord x = aRect.x;
232 switch (aJust) {
233 case nsIPrintSettings::kJustLeft:
234 x += mPD->mEdgePaperMargin.left;
235 break;
236
237 case nsIPrintSettings::kJustCenter:
238 x += (aRect.width - width) / 2;
239 break;
240
241 case nsIPrintSettings::kJustRight:
242 x += aRect.width - width - mPD->mEdgePaperMargin.right;
243 break;
244 } // switch
245
246 return x;
247 }
248
249 // Draw a header or footer
250 // @param aRenderingContext - rendering content ot draw into
251 // @param aHeaderFooter - indicates whether it is a header or footer
252 // @param aStrLeft - string for the left header or footer; can be empty
253 // @param aStrCenter - string for the center header or footer; can be empty
254 // @param aStrRight - string for the right header or footer; can be empty
255 // @param aRect - the rect of the page
256 // @param aAscent - the ascent of the font
257 // @param aHeight - the height of the font
DrawHeaderFooter(gfxContext & aRenderingContext,nsFontMetrics & aFontMetrics,nsHeaderFooterEnum aHeaderFooter,const nsString & aStrLeft,const nsString & aStrCenter,const nsString & aStrRight,const nsRect & aRect,nscoord aAscent,nscoord aHeight)258 void nsPageFrame::DrawHeaderFooter(
259 gfxContext& aRenderingContext, nsFontMetrics& aFontMetrics,
260 nsHeaderFooterEnum aHeaderFooter, const nsString& aStrLeft,
261 const nsString& aStrCenter, const nsString& aStrRight, const nsRect& aRect,
262 nscoord aAscent, nscoord aHeight) {
263 int32_t numStrs = 0;
264 if (!aStrLeft.IsEmpty()) numStrs++;
265 if (!aStrCenter.IsEmpty()) numStrs++;
266 if (!aStrRight.IsEmpty()) numStrs++;
267
268 if (numStrs == 0) return;
269 nscoord strSpace = aRect.width / numStrs;
270
271 if (!aStrLeft.IsEmpty()) {
272 DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
273 nsIPrintSettings::kJustLeft, aStrLeft, aRect, aAscent,
274 aHeight, strSpace);
275 }
276 if (!aStrCenter.IsEmpty()) {
277 DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
278 nsIPrintSettings::kJustCenter, aStrCenter, aRect, aAscent,
279 aHeight, strSpace);
280 }
281 if (!aStrRight.IsEmpty()) {
282 DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
283 nsIPrintSettings::kJustRight, aStrRight, aRect, aAscent,
284 aHeight, strSpace);
285 }
286 }
287
288 // Draw a header or footer string
289 // @param aRenderingContext - rendering context to draw into
290 // @param aHeaderFooter - indicates whether it is a header or footer
291 // @param aJust - indicates where the string is located within the header/footer
292 // @param aStr - the string to be drawn
293 // @param aRect - the rect of the page
294 // @param aHeight - the height of the font
295 // @param aAscent - the ascent of the font
296 // @param aWidth - available width for the string
DrawHeaderFooter(gfxContext & aRenderingContext,nsFontMetrics & aFontMetrics,nsHeaderFooterEnum aHeaderFooter,int32_t aJust,const nsString & aStr,const nsRect & aRect,nscoord aAscent,nscoord aHeight,nscoord aWidth)297 void nsPageFrame::DrawHeaderFooter(gfxContext& aRenderingContext,
298 nsFontMetrics& aFontMetrics,
299 nsHeaderFooterEnum aHeaderFooter,
300 int32_t aJust, const nsString& aStr,
301 const nsRect& aRect, nscoord aAscent,
302 nscoord aHeight, nscoord aWidth) {
303 nscoord contentWidth =
304 aWidth - (mPD->mEdgePaperMargin.left + mPD->mEdgePaperMargin.right);
305
306 DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
307
308 if ((aHeaderFooter == eHeader && aHeight < mPageContentMargin.top) ||
309 (aHeaderFooter == eFooter && aHeight < mPageContentMargin.bottom)) {
310 nsAutoString str;
311 ProcessSpecialCodes(aStr, str);
312
313 int32_t indx;
314 int32_t textWidth = 0;
315 const char16_t* text = str.get();
316
317 int32_t len = (int32_t)str.Length();
318 if (len == 0) {
319 return; // bail is empty string
320 }
321 // find how much text fits, the "position" is the size of the available area
322 if (nsLayoutUtils::BinarySearchForPosition(
323 drawTarget, aFontMetrics, text, 0, 0, 0, len, int32_t(contentWidth),
324 indx, textWidth)) {
325 if (indx < len - 1) {
326 // we can't fit in all the text
327 if (indx > 3) {
328 // But we can fit in at least 4 chars. Show all but 3 of them, then
329 // an ellipsis.
330 // XXXbz for non-plane0 text, this may be cutting things in the
331 // middle of a codepoint! Also, we have no guarantees that the three
332 // dots will fit in the space the three chars we removed took up with
333 // these font metrics!
334 str.Truncate(indx - 3);
335 str.AppendLiteral("...");
336 } else {
337 // We can only fit 3 or fewer chars. Just show nothing
338 str.Truncate();
339 }
340 }
341 } else {
342 return; // bail if couldn't find the correct length
343 }
344
345 if (HasRTLChars(str)) {
346 PresContext()->SetBidiEnabled();
347 }
348
349 // cacl the x and y positions of the text
350 nscoord x =
351 GetXPosition(aRenderingContext, aFontMetrics, aRect, aJust, str);
352 nscoord y;
353 if (aHeaderFooter == eHeader) {
354 y = aRect.y + mPD->mEdgePaperMargin.top;
355 } else {
356 y = aRect.YMost() - aHeight - mPD->mEdgePaperMargin.bottom;
357 }
358
359 // set up new clip and draw the text
360 aRenderingContext.Save();
361 aRenderingContext.Clip(NSRectToSnappedRect(
362 aRect, PresContext()->AppUnitsPerDevPixel(), *drawTarget));
363 aRenderingContext.SetColor(sRGBColor::OpaqueBlack());
364 nsLayoutUtils::DrawString(this, aFontMetrics, &aRenderingContext, str.get(),
365 str.Length(), nsPoint(x, y + aAscent), nullptr,
366 DrawStringFlags::ForceHorizontal);
367 aRenderingContext.Restore();
368 }
369 }
370
371 /**
372 * Remove all leaf display items that are not for descendants of
373 * aBuilder->GetReferenceFrame() from aList.
374 * @param aPage the page we're constructing the display list for
375 * @param aExtraPage the page we constructed aList for
376 * @param aList the list that is modified in-place
377 */
PruneDisplayListForExtraPage(nsDisplayListBuilder * aBuilder,nsPageFrame * aPage,nsIFrame * aExtraPage,nsDisplayList * aList)378 static void PruneDisplayListForExtraPage(nsDisplayListBuilder* aBuilder,
379 nsPageFrame* aPage,
380 nsIFrame* aExtraPage,
381 nsDisplayList* aList) {
382 nsDisplayList newList;
383
384 while (true) {
385 nsDisplayItem* i = aList->RemoveBottom();
386 if (!i) break;
387 nsDisplayList* subList = i->GetSameCoordinateSystemChildren();
388 if (subList) {
389 PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, subList);
390 i->UpdateBounds(aBuilder);
391 } else {
392 nsIFrame* f = i->Frame();
393 if (!nsLayoutUtils::IsProperAncestorFrameCrossDoc(aPage, f)) {
394 // We're throwing this away so call its destructor now. The memory
395 // is owned by aBuilder which destroys all items at once.
396 i->Destroy(aBuilder);
397 continue;
398 }
399 }
400 newList.AppendToTop(i);
401 }
402 aList->AppendToTop(&newList);
403 }
404
BuildDisplayListForExtraPage(nsDisplayListBuilder * aBuilder,nsPageFrame * aPage,nsIFrame * aExtraPage,nsDisplayList * aList)405 static void BuildDisplayListForExtraPage(nsDisplayListBuilder* aBuilder,
406 nsPageFrame* aPage,
407 nsIFrame* aExtraPage,
408 nsDisplayList* aList) {
409 // The only content in aExtraPage we care about is out-of-flow content from
410 // aPage, whose placeholders have occurred in aExtraPage. If
411 // NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO is not set, then aExtraPage has
412 // no such content.
413 if (!aExtraPage->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
414 return;
415 }
416 nsDisplayList list;
417 aExtraPage->BuildDisplayListForStackingContext(aBuilder, &list);
418 PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, &list);
419 aList->AppendToTop(&list);
420 }
421
GetNextPage(nsIFrame * aPageContentFrame)422 static nsIFrame* GetNextPage(nsIFrame* aPageContentFrame) {
423 // XXX ugh
424 nsIFrame* pageFrame = aPageContentFrame->GetParent();
425 NS_ASSERTION(pageFrame->IsPageFrame(),
426 "pageContentFrame has unexpected parent");
427 nsIFrame* nextPageFrame = pageFrame->GetNextSibling();
428 if (!nextPageFrame) return nullptr;
429 NS_ASSERTION(nextPageFrame->IsPageFrame(),
430 "pageFrame's sibling is not a page frame...");
431 nsIFrame* f = nextPageFrame->PrincipalChildList().FirstChild();
432 NS_ASSERTION(f, "pageFrame has no page content frame!");
433 NS_ASSERTION(f->IsPageContentFrame(),
434 "pageFrame's child is not page content!");
435 return f;
436 }
437
ComputePageTransform(nsIFrame * aFrame,float aAppUnitsPerPixel)438 static gfx::Matrix4x4 ComputePageTransform(nsIFrame* aFrame,
439 float aAppUnitsPerPixel) {
440 float scale = aFrame->PresContext()->GetPageScale();
441 return gfx::Matrix4x4::Scaling(scale, scale, 1);
442 }
443
444 class nsDisplayHeaderFooter final : public nsPaintedDisplayItem {
445 public:
nsDisplayHeaderFooter(nsDisplayListBuilder * aBuilder,nsPageFrame * aFrame)446 nsDisplayHeaderFooter(nsDisplayListBuilder* aBuilder, nsPageFrame* aFrame)
447 : nsPaintedDisplayItem(aBuilder, aFrame) {
448 MOZ_COUNT_CTOR(nsDisplayHeaderFooter);
449 }
MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayHeaderFooter)450 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayHeaderFooter)
451
452 virtual void Paint(nsDisplayListBuilder* aBuilder,
453 gfxContext* aCtx) override {
454 #ifdef DEBUG
455 nsPageFrame* pageFrame = do_QueryFrame(mFrame);
456 MOZ_ASSERT(pageFrame, "We should have an nsPageFrame");
457 #endif
458 static_cast<nsPageFrame*>(mFrame)->PaintHeaderFooter(
459 *aCtx, ToReferenceFrame(), IsSubpixelAADisabled());
460 }
461 NS_DISPLAY_DECL_NAME("HeaderFooter", TYPE_HEADER_FOOTER)
462
GetComponentAlphaBounds(nsDisplayListBuilder * aBuilder) const463 virtual nsRect GetComponentAlphaBounds(
464 nsDisplayListBuilder* aBuilder) const override {
465 bool snap;
466 return GetBounds(aBuilder, &snap);
467 }
468 };
469
470 //------------------------------------------------------------------------------
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)471 void nsPageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
472 const nsDisplayListSet& aLists) {
473 nsDisplayListCollection set(aBuilder);
474
475 if (PresContext()->IsScreen()) {
476 DisplayBorderBackgroundOutline(aBuilder, aLists);
477 }
478
479 nsIFrame* child = mFrames.FirstChild();
480 float scale = PresContext()->GetPageScale();
481 nsRect clipRect(nsPoint(0, 0), child->GetSize());
482 // Note: this computation matches how we compute maxSize.height
483 // in nsPageFrame::Reflow
484 nscoord expectedPageContentHeight = NSToCoordCeil(GetSize().height / scale);
485 if (clipRect.height > expectedPageContentHeight) {
486 // We're doing print-selection, with one long page-content frame.
487 // Clip to the appropriate page-content slice for the current page.
488 NS_ASSERTION(mPageNum > 0, "page num should be positive");
489 // Note: The pageContentFrame's y-position has been set such that a zero
490 // y-value matches the top edge of the current page. So, to clip to the
491 // current page's content (in coordinates *relative* to the page content
492 // frame), we just negate its y-position and add the top margin.
493 clipRect.y =
494 NSToCoordCeil((-child->GetRect().y + mPD->mReflowMargin.top) / scale);
495 clipRect.height = expectedPageContentHeight;
496 NS_ASSERTION(clipRect.y < child->GetSize().height,
497 "Should be clipping to region inside the page content bounds");
498 }
499 clipRect += aBuilder->ToReferenceFrame(child);
500
501 nsDisplayList content;
502 {
503 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
504
505 // Overwrite current clip, since we're going to wrap in a transform
506 // and the current clip is no longer meaningful.
507 clipState.Clear();
508 clipState.ClipContainingBlockDescendants(clipRect, nullptr);
509
510 nsRect visibleRect = child->GetVisualOverflowRectRelativeToSelf();
511 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
512 aBuilder, child, visibleRect, visibleRect);
513 child->BuildDisplayListForStackingContext(aBuilder, &content);
514
515 // We may need to paint out-of-flow frames whose placeholders are
516 // on other pages. Add those pages to our display list. Note that
517 // out-of-flow frames can't be placed after their placeholders so
518 // we don't have to process earlier pages. The display lists for
519 // these extra pages are pruned so that only display items for the
520 // page we currently care about (which we would have reached by
521 // following placeholders to their out-of-flows) end up on the list.
522 //
523 // Stacking context frames that wrap content on their normal page,
524 // as well as OOF content for this page will have their container
525 // items duplicated. We tell the builder to include our page number
526 // in the unique key for any extra page items so that they can be
527 // differentiated from the ones created on the normal page.
528 NS_ASSERTION(mPageNum <= 255, "Too many pages to handle OOFs");
529 if (mPageNum <= 255) {
530 uint8_t oldPageNum = aBuilder->GetBuildingExtraPagesForPageNum();
531 aBuilder->SetBuildingExtraPagesForPageNum(mPageNum);
532
533 nsIFrame* page = child;
534 while ((page = GetNextPage(page)) != nullptr) {
535 nsRect childVisible = visibleRect + child->GetOffsetTo(page);
536
537 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
538 aBuilder, page, childVisible, childVisible);
539 BuildDisplayListForExtraPage(aBuilder, this, page, &content);
540 }
541
542 aBuilder->SetBuildingExtraPagesForPageNum(oldPageNum);
543 }
544
545 // Invoke AutoBuildingDisplayList to ensure that the correct visibleRect
546 // is used to compute the visible rect if AddCanvasBackgroundColorItem
547 // creates a display item.
548 nsDisplayListBuilder::AutoBuildingDisplayList building(
549 aBuilder, child, visibleRect, visibleRect);
550
551 // Add the canvas background color to the bottom of the list. This
552 // happens after we've built the list so that AddCanvasBackgroundColorItem
553 // can monkey with the contents if necessary.
554 nsRect backgroundRect =
555 nsRect(aBuilder->ToReferenceFrame(child), child->GetSize());
556
557 PresContext()->GetPresShell()->AddCanvasBackgroundColorItem(
558 aBuilder, &content, child, backgroundRect, NS_RGBA(0, 0, 0, 0));
559 }
560
561 content.AppendNewToTop<nsDisplayTransform>(aBuilder, child, &content,
562 content.GetBuildingRect(),
563 ::ComputePageTransform);
564
565 set.Content()->AppendToTop(&content);
566
567 if (PresContext()->IsRootPaginatedDocument()) {
568 set.Content()->AppendNewToTop<nsDisplayHeaderFooter>(aBuilder, this);
569 }
570
571 set.MoveTo(aLists);
572 }
573
574 //------------------------------------------------------------------------------
SetPageNumInfo(int32_t aPageNumber,int32_t aTotalPages)575 void nsPageFrame::SetPageNumInfo(int32_t aPageNumber, int32_t aTotalPages) {
576 mPageNum = aPageNumber;
577 mTotNumPages = aTotalPages;
578 }
579
PaintHeaderFooter(gfxContext & aRenderingContext,nsPoint aPt,bool aDisableSubpixelAA)580 void nsPageFrame::PaintHeaderFooter(gfxContext& aRenderingContext, nsPoint aPt,
581 bool aDisableSubpixelAA) {
582 nsPresContext* pc = PresContext();
583
584 if (!mPD->mPrintSettings) {
585 if (pc->Type() == nsPresContext::eContext_PrintPreview || pc->IsDynamic())
586 mPD->mPrintSettings = pc->GetPrintSettings();
587 if (!mPD->mPrintSettings) return;
588 }
589
590 nsRect rect(aPt, mRect.Size());
591 aRenderingContext.SetColor(sRGBColor::OpaqueBlack());
592
593 DrawTargetAutoDisableSubpixelAntialiasing disable(
594 aRenderingContext.GetDrawTarget(), aDisableSubpixelAA);
595
596 // Get the FontMetrics to determine width.height of strings
597 nsFontMetrics::Params params;
598 params.userFontSet = pc->GetUserFontSet();
599 params.textPerf = pc->GetTextPerfMetrics();
600 params.fontStats = pc->GetFontMatchingStats();
601 params.featureValueLookup = pc->GetFontFeatureValuesLookup();
602 RefPtr<nsFontMetrics> fontMet =
603 pc->DeviceContext()->GetMetricsFor(mPD->mHeadFootFont, params);
604
605 nscoord ascent = 0;
606 nscoord visibleHeight = 0;
607 if (fontMet) {
608 visibleHeight = fontMet->MaxHeight();
609 ascent = fontMet->MaxAscent();
610 }
611
612 // print document headers and footers
613 nsString headerLeft, headerCenter, headerRight;
614 mPD->mPrintSettings->GetHeaderStrLeft(headerLeft);
615 mPD->mPrintSettings->GetHeaderStrCenter(headerCenter);
616 mPD->mPrintSettings->GetHeaderStrRight(headerRight);
617 DrawHeaderFooter(aRenderingContext, *fontMet, eHeader, headerLeft,
618 headerCenter, headerRight, rect, ascent, visibleHeight);
619
620 nsString footerLeft, footerCenter, footerRight;
621 mPD->mPrintSettings->GetFooterStrLeft(footerLeft);
622 mPD->mPrintSettings->GetFooterStrCenter(footerCenter);
623 mPD->mPrintSettings->GetFooterStrRight(footerRight);
624 DrawHeaderFooter(aRenderingContext, *fontMet, eFooter, footerLeft,
625 footerCenter, footerRight, rect, ascent, visibleHeight);
626 }
627
SetSharedPageData(nsSharedPageData * aPD)628 void nsPageFrame::SetSharedPageData(nsSharedPageData* aPD) {
629 mPD = aPD;
630 // Set the shared data into the page frame before reflow
631 nsPageContentFrame* pcf =
632 static_cast<nsPageContentFrame*>(mFrames.FirstChild());
633 if (pcf) {
634 pcf->SetSharedPageData(mPD);
635 }
636 }
637
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)638 void nsPageFrame::AppendDirectlyOwnedAnonBoxes(
639 nsTArray<OwnedAnonBox>& aResult) {
640 MOZ_ASSERT(mFrames.FirstChild() && mFrames.FirstChild()->IsPageContentFrame(),
641 "pageFrame must have a pageContentFrame child");
642 aResult.AppendElement(mFrames.FirstChild());
643 }
644
NS_NewPageBreakFrame(PresShell * aPresShell,ComputedStyle * aStyle)645 nsIFrame* NS_NewPageBreakFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
646 MOZ_ASSERT(aPresShell, "null PresShell");
647 // check that we are only creating page break frames when printing
648 NS_ASSERTION(aPresShell->GetPresContext()->IsPaginated(),
649 "created a page break frame while not printing");
650
651 return new (aPresShell)
652 nsPageBreakFrame(aStyle, aPresShell->GetPresContext());
653 }
654
NS_IMPL_FRAMEARENA_HELPERS(nsPageBreakFrame)655 NS_IMPL_FRAMEARENA_HELPERS(nsPageBreakFrame)
656
657 nsPageBreakFrame::nsPageBreakFrame(ComputedStyle* aStyle,
658 nsPresContext* aPresContext)
659 : nsLeafFrame(aStyle, aPresContext, kClassID), mHaveReflowed(false) {}
660
661 nsPageBreakFrame::~nsPageBreakFrame() = default;
662
GetIntrinsicISize()663 nscoord nsPageBreakFrame::GetIntrinsicISize() {
664 return nsPresContext::CSSPixelsToAppUnits(1);
665 }
666
GetIntrinsicBSize()667 nscoord nsPageBreakFrame::GetIntrinsicBSize() { return 0; }
668
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)669 void nsPageBreakFrame::Reflow(nsPresContext* aPresContext,
670 ReflowOutput& aDesiredSize,
671 const ReflowInput& aReflowInput,
672 nsReflowStatus& aStatus) {
673 DO_GLOBAL_REFLOW_COUNT("nsPageBreakFrame");
674 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
675 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
676
677 // Override reflow, since we don't want to deal with what our
678 // computed values are.
679 WritingMode wm = aReflowInput.GetWritingMode();
680 nscoord bSize = aReflowInput.AvailableBSize();
681 if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
682 bSize = nscoord(0);
683 } else if (GetContent()->IsHTMLElement(nsGkAtoms::legend)) {
684 // If this is a page break frame for a _rendered legend_ then it should be
685 // ignored since these frames are inserted inside the fieldset's inner
686 // frame and thus "misplaced". nsFieldSetFrame::Reflow deals with these
687 // forced breaks explicitly instead.
688 nsContainerFrame* parent = GetParent();
689 if (parent &&
690 parent->Style()->GetPseudoType() == PseudoStyleType::fieldsetContent) {
691 while ((parent = parent->GetParent())) {
692 if (nsFieldSetFrame* fieldset = do_QueryFrame(parent)) {
693 auto* legend = fieldset->GetLegend();
694 if (legend && legend->GetContent() == GetContent()) {
695 bSize = nscoord(0);
696 }
697 break;
698 }
699 }
700 }
701 }
702 LogicalSize finalSize(wm, GetIntrinsicISize(), bSize);
703 // round the height down to the nearest pixel
704 // XXX(mats) why???
705 finalSize.BSize(wm) -=
706 finalSize.BSize(wm) % nsPresContext::CSSPixelsToAppUnits(1);
707 aDesiredSize.SetSize(wm, finalSize);
708
709 // Note: not using NS_FRAME_FIRST_REFLOW here, since it's not clear whether
710 // DidReflow will always get called before the next Reflow() call.
711 mHaveReflowed = true;
712 }
713
714 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const715 nsresult nsPageBreakFrame::GetFrameName(nsAString& aResult) const {
716 return MakeFrameName(NS_LITERAL_STRING("PageBreak"), aResult);
717 }
718 #endif
719