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