tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsPageFrame.cpp (44065B)


      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 "PrintedSheetFrame.h"
     10 #include "gfxContext.h"
     11 #include "mozilla/AppUnits.h"
     12 #include "mozilla/Logging.h"
     13 #include "mozilla/PresShell.h"
     14 #include "mozilla/StaticPrefs_layout.h"
     15 #include "mozilla/StaticPrefs_print.h"
     16 #include "mozilla/gfx/2D.h"
     17 #include "mozilla/intl/Segmenter.h"
     18 #include "nsBidiUtils.h"
     19 #include "nsDeviceContext.h"
     20 #include "nsDisplayList.h"
     21 #include "nsFieldSetFrame.h"
     22 #include "nsFontMetrics.h"
     23 #include "nsGkAtoms.h"
     24 #include "nsIFrame.h"
     25 #include "nsIPrintSettings.h"
     26 #include "nsLayoutUtils.h"
     27 #include "nsPageContentFrame.h"
     28 #include "nsPageSequenceFrame.h"  // for nsSharedPageData
     29 #include "nsPresContext.h"
     30 #include "nsTextFormatter.h"  // for page number localization formatting
     31 extern mozilla::LazyLogModule gLayoutPrintingLog;
     32 #define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1)
     33 
     34 using namespace mozilla;
     35 using namespace mozilla::gfx;
     36 
     37 nsPageFrame* NS_NewPageFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
     38  return new (aPresShell) nsPageFrame(aStyle, aPresShell->GetPresContext());
     39 }
     40 
     41 NS_IMPL_FRAMEARENA_HELPERS(nsPageFrame)
     42 
     43 NS_QUERYFRAME_HEAD(nsPageFrame)
     44  NS_QUERYFRAME_ENTRY(nsPageFrame)
     45 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
     46 
     47 nsPageFrame::nsPageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
     48    : nsContainerFrame(aStyle, aPresContext, kClassID) {}
     49 
     50 nsPageFrame::~nsPageFrame() = default;
     51 
     52 nsReflowStatus nsPageFrame::ReflowPageContent(
     53    nsPresContext* aPresContext, const ReflowInput& aPageReflowInput) {
     54  nsPageContentFrame* const frame = PageContentFrame();
     55  // If this is the first page, it won't have its page name and computed style
     56  // set yet. Before reflow, make sure that page name and computed style have
     57  // been applied.
     58  frame->EnsurePageName();
     59  // XXX Pay attention to the page's border and padding...
     60  //
     61  // Reflow our ::-moz-page-content frame, allowing it only to be as big as we
     62  // are (minus margins).
     63  const nsSize pageSize = ComputePageSize();
     64  // Scaling applied to the page in the single page-per-sheet case (used for
     65  // down-scaling when the page is too large to fit on the sheet we are printing
     66  // on). In the single page-per-sheet case, we need this here to preemptively
     67  // increase the margins by the same amount that the scaling will reduce them
     68  // in order to make sure that their physical size is unchanged (particularly
     69  // important for the unwriteable margins).
     70  const auto* ppsInfo = GetSharedPageData()->PagesPerSheetInfo();
     71  const float pageSizeScale =
     72      ppsInfo->mNumPages == 1 ? ComputeSinglePPSPageSizeScale(pageSize) : 1.0f;
     73  // Scaling applied to content, as given by the print UI.
     74  // This is an additional scale factor that is applied to the content in the
     75  // nsPageContentFrame.
     76  const float extraContentScale = aPresContext->GetPageScale();
     77  // Size for the page content. This will be scaled by extraContentScale, and
     78  // is used to calculate the computed size of the nsPageContentFrame content
     79  // by subtracting margins.
     80  nsSize availableSpace = pageSize;
     81 
     82  // When the reflow size is NS_UNCONSTRAINEDSIZE it means we are reflowing
     83  // a single page to print selection. So this means we want to use
     84  // NS_UNCONSTRAINEDSIZE without altering it.
     85  //
     86  // FIXME(emilio): Is this still true?
     87  availableSpace.width =
     88      NSToCoordCeil(availableSpace.width / extraContentScale);
     89  if (availableSpace.height != NS_UNCONSTRAINEDSIZE) {
     90    availableSpace.height =
     91        NSToCoordCeil(availableSpace.height / extraContentScale);
     92  }
     93 
     94  // Get the number of Twips per pixel from the PresContext
     95  const nscoord onePixel = AppUnitsPerCSSPixel();
     96 
     97  // insurance against infinite reflow, when reflowing less than a pixel
     98  // XXX Shouldn't we do something more friendly when invalid margins
     99  //     are set?
    100  if (availableSpace.width < onePixel || availableSpace.height < onePixel) {
    101    NS_WARNING("Reflow aborted; no space for content");
    102    return {};
    103  }
    104 
    105  ReflowInput kidReflowInput(
    106      aPresContext, aPageReflowInput, frame,
    107      LogicalSize(frame->GetWritingMode(), availableSpace));
    108  kidReflowInput.mFlags.mIsTopOfPage = true;
    109  kidReflowInput.mFlags.mTableIsSplittable = true;
    110 
    111  nsMargin defaultMargins = aPresContext->GetDefaultPageMargin();
    112  // The default margins are in the coordinate space of the physical paper.
    113  // Scale them by the pageSizeScale to convert them to the content coordinate
    114  // space.
    115  for (const auto side : mozilla::AllPhysicalSides()) {
    116    defaultMargins.Side(side) =
    117        NSToCoordRound((float)defaultMargins.Side(side) / pageSizeScale);
    118  }
    119  mPageContentMargin = defaultMargins;
    120 
    121  // Use the margins given in the @page rule if told to do so.
    122  // We clamp to the paper's unwriteable margins to avoid clipping, *except*
    123  // that we will respect a margin of zero if specified, assuming this means
    124  // the document is intended to fit the paper size exactly, and the client is
    125  // taking full responsibility for what happens around the edges.
    126  const auto anchorResolutionParams =
    127      AnchorPosResolutionParams::From(&kidReflowInput);
    128  if (mPD->mPrintSettings->GetHonorPageRuleMargins()) {
    129    for (const auto side : mozilla::AllPhysicalSides()) {
    130      if (!kidReflowInput.mStyleMargin->GetMargin(side, anchorResolutionParams)
    131               ->IsAuto()) {
    132        // Computed margins are already in the coordinate space of the content,
    133        // do not scale.
    134        const nscoord computed =
    135            kidReflowInput.ComputedPhysicalMargin().Side(side);
    136        // Respecting a zero margin is particularly important when the client
    137        // is PDF.js where the PDF already contains the margins.
    138        // User could also be asking to ignore unwriteable margins (Though
    139        // currently, it is impossible through the print UI to set both
    140        // `HonorPageRuleMargins` and `IgnoreUnwriteableMargins`).
    141        if (computed == 0 ||
    142            mPD->mPrintSettings->GetIgnoreUnwriteableMargins()) {
    143          mPageContentMargin.Side(side) = computed;
    144        } else {
    145          // Unwriteable margins are in the coordinate space of the physical
    146          // paper. Scale them by the pageSizeScale to convert them to the
    147          // content coordinate space.
    148          const int32_t unwriteableTwips =
    149              mPD->mPrintSettings->GetUnwriteableMarginInTwips().Side(side);
    150          const nscoord unwriteable = nsPresContext::CSSTwipsToAppUnits(
    151              (float)unwriteableTwips / pageSizeScale);
    152          mPageContentMargin.Side(side) = std::max(
    153              kidReflowInput.ComputedPhysicalMargin().Side(side), unwriteable);
    154        }
    155      }
    156    }
    157  }
    158 
    159  // TODO: This seems odd that we need to scale the margins by the extra
    160  // scale factor, but this is needed for correct margins.
    161  // Why are the margins already scaled? Shouldn't they be stored so that this
    162  // scaling factor would be redundant?
    163  nscoord computedWidth =
    164      availableSpace.width - mPageContentMargin.LeftRight() / extraContentScale;
    165  nscoord computedHeight;
    166  if (availableSpace.height == NS_UNCONSTRAINEDSIZE) {
    167    computedHeight = NS_UNCONSTRAINEDSIZE;
    168  } else {
    169    computedHeight = availableSpace.height -
    170                     mPageContentMargin.TopBottom() / extraContentScale;
    171  }
    172 
    173  // Check the width and height, if they're too small we reset the margins
    174  // back to the default.
    175  if (computedWidth < onePixel || computedHeight < onePixel) {
    176    mPageContentMargin = defaultMargins;
    177    computedWidth = availableSpace.width -
    178                    mPageContentMargin.LeftRight() / extraContentScale;
    179    if (computedHeight != NS_UNCONSTRAINEDSIZE) {
    180      computedHeight = availableSpace.height -
    181                       mPageContentMargin.TopBottom() / extraContentScale;
    182    }
    183    // And if they're still too small, we give up.
    184    if (computedWidth < onePixel || computedHeight < onePixel) {
    185      NS_WARNING("Reflow aborted; no space for content");
    186      return {};
    187    }
    188  }
    189 
    190  kidReflowInput.SetComputedWidth(computedWidth);
    191  kidReflowInput.SetComputedHeight(computedHeight);
    192 
    193  // calc location of frame
    194  const nscoord xc = mPageContentMargin.left;
    195  const nscoord yc = mPageContentMargin.top;
    196 
    197  // Get the child's desired size
    198  ReflowOutput kidOutput(kidReflowInput);
    199  nsReflowStatus kidStatus;
    200  ReflowChild(frame, aPresContext, kidOutput, kidReflowInput, xc, yc,
    201              ReflowChildFlags::Default, kidStatus);
    202 
    203  // Place and size the child
    204  FinishReflowChild(frame, aPresContext, kidOutput, &kidReflowInput, xc, yc,
    205                    ReflowChildFlags::Default);
    206 
    207  NS_ASSERTION(!kidStatus.IsFullyComplete() || !frame->GetNextInFlow(),
    208               "bad child flow list");
    209  return kidStatus;
    210 }
    211 
    212 void nsPageFrame::Reflow(nsPresContext* aPresContext,
    213                         ReflowOutput& aReflowOutput,
    214                         const ReflowInput& aReflowInput,
    215                         nsReflowStatus& aStatus) {
    216  MarkInReflow();
    217  DO_GLOBAL_REFLOW_COUNT("nsPageFrame");
    218  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
    219  MOZ_ASSERT(mPD, "Need a pointer to nsSharedPageData before reflow starts");
    220 
    221  // Our status is the same as our child's.
    222  aStatus = ReflowPageContent(aPresContext, aReflowInput);
    223 
    224  PR_PL(("PageFrame::Reflow %p ", this));
    225  PR_PL(("[%d,%d][%d,%d]\n", aReflowOutput.Width(), aReflowOutput.Height(),
    226         aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
    227 
    228  // Return our desired size
    229  WritingMode wm = aReflowInput.GetWritingMode();
    230  aReflowOutput.ISize(wm) = aReflowInput.AvailableISize();
    231  if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
    232    aReflowOutput.BSize(wm) = aReflowInput.AvailableBSize();
    233  }
    234 
    235  aReflowOutput.SetOverflowAreasToDesiredBounds();
    236  FinishAndStoreOverflow(&aReflowOutput);
    237 
    238  PR_PL(("PageFrame::Reflow %p ", this));
    239  PR_PL(("[%d,%d]\n", aReflowInput.AvailableWidth(),
    240         aReflowInput.AvailableHeight()));
    241 }
    242 
    243 #ifdef DEBUG_FRAME_DUMP
    244 nsresult nsPageFrame::GetFrameName(nsAString& aResult) const {
    245  return MakeFrameName(u"Page"_ns, aResult);
    246 }
    247 #endif
    248 
    249 void nsPageFrame::ProcessSpecialCodes(const nsString& aStr, nsString& aNewStr) {
    250  aNewStr = aStr;
    251 
    252  // Helper to wrap a string in Unicode FSI/PDI controls, so that its bidi
    253  // rendering will be isolated from the context, and will respect the default
    254  // directionality of the string's content.
    255  // We apply this to each of the strings being substituted in for "special"
    256  // codes in the header/footer.
    257  constexpr char16_t kFirstStrongIsolate = char16_t(0x2068);
    258  constexpr char16_t kPopDirectionalIsolate = char16_t(0x2069);
    259  auto bidiIsolateWrap = [](nsString& aString) {
    260    aString.Insert(kFirstStrongIsolate, 0);
    261    aString.Append(kPopDirectionalIsolate);
    262  };
    263 
    264  // Search to see if the &D code is in the string
    265  // then subst in the current date/time
    266  constexpr auto kDate = u"&D"_ns;
    267  if (aStr.Find(kDate) != kNotFound) {
    268    nsAutoString uStr(mPD->mDateTimeStr);
    269    bidiIsolateWrap(uStr);
    270    aNewStr.ReplaceSubstring(kDate, uStr);
    271  }
    272 
    273  // NOTE: Must search for &PT before searching for &P
    274  //
    275  // Search to see if the "page number and page" total code are in the string
    276  // and replace the page number and page total code with the actual
    277  // values
    278  constexpr auto kPageAndTotal = u"&PT"_ns;
    279  if (aStr.Find(kPageAndTotal) != kNotFound) {
    280    nsAutoString uStr;
    281    nsTextFormatter::ssprintf(uStr, mPD->mPageNumAndTotalsFormat.get(),
    282                              mPageNum, mPD->mRawNumPages);
    283    bidiIsolateWrap(uStr);
    284    aNewStr.ReplaceSubstring(kPageAndTotal, uStr);
    285  }
    286 
    287  // Search to see if the page number code is in the string
    288  // and replace the page number code with the actual value
    289  constexpr auto kPage = u"&P"_ns;
    290  if (aStr.Find(kPage) != kNotFound) {
    291    nsAutoString uStr;
    292    nsTextFormatter::ssprintf(uStr, mPD->mPageNumFormat.get(), mPageNum);
    293    bidiIsolateWrap(uStr);
    294    aNewStr.ReplaceSubstring(kPage, uStr);
    295  }
    296 
    297  constexpr auto kTitle = u"&T"_ns;
    298  if (aStr.Find(kTitle) != kNotFound) {
    299    nsAutoString uStr(mPD->mDocTitle);
    300    bidiIsolateWrap(uStr);
    301    aNewStr.ReplaceSubstring(kTitle, uStr);
    302  }
    303 
    304  constexpr auto kDocURL = u"&U"_ns;
    305  if (aStr.Find(kDocURL) != kNotFound) {
    306    nsAutoString uStr(mPD->mDocURL);
    307    bidiIsolateWrap(uStr);
    308    aNewStr.ReplaceSubstring(kDocURL, uStr);
    309  }
    310 
    311  constexpr auto kPageTotal = u"&L"_ns;
    312  if (aStr.Find(kPageTotal) != kNotFound) {
    313    nsAutoString uStr;
    314    nsTextFormatter::ssprintf(uStr, mPD->mPageNumFormat.get(),
    315                              mPD->mRawNumPages);
    316    bidiIsolateWrap(uStr);
    317    aNewStr.ReplaceSubstring(kPageTotal, uStr);
    318  }
    319 }
    320 
    321 //------------------------------------------------------------------------------
    322 nscoord nsPageFrame::GetXPosition(gfxContext& aRenderingContext,
    323                                  nsFontMetrics& aFontMetrics,
    324                                  const nsRect& aRect, int32_t aJust,
    325                                  const nsString& aStr) {
    326  nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(
    327      aStr, this, aFontMetrics, aRenderingContext);
    328  nscoord x = aRect.x;
    329  switch (aJust) {
    330    case nsIPrintSettings::kJustLeft:
    331      x += mPD->mEdgePaperMargin.left;
    332      break;
    333 
    334    case nsIPrintSettings::kJustCenter:
    335      x += (aRect.width - width) / 2;
    336      break;
    337 
    338    case nsIPrintSettings::kJustRight:
    339      x += aRect.width - width - mPD->mEdgePaperMargin.right;
    340      break;
    341  }  // switch
    342 
    343  return x;
    344 }
    345 
    346 // Draw a header or footer
    347 // @param aRenderingContext - rendering content to draw into
    348 // @param aHeaderFooter - indicates whether it is a header or footer
    349 // @param aStrLeft - string for the left header or footer; can be empty
    350 // @param aStrCenter - string for the center header or footer; can be empty
    351 // @param aStrRight - string for the right header or footer; can be empty
    352 // @param aRect - the rect of the page
    353 // @param aAscent - the ascent of the font
    354 // @param aHeight - the height of the font
    355 void nsPageFrame::DrawHeaderFooter(
    356    gfxContext& aRenderingContext, nsFontMetrics& aFontMetrics,
    357    nsHeaderFooterEnum aHeaderFooter, const nsString& aStrLeft,
    358    const nsString& aStrCenter, const nsString& aStrRight, const nsRect& aRect,
    359    nscoord aAscent, nscoord aHeight) {
    360  int32_t numStrs = 0;
    361  if (!aStrLeft.IsEmpty()) {
    362    numStrs++;
    363  }
    364  if (!aStrCenter.IsEmpty()) {
    365    numStrs++;
    366  }
    367  if (!aStrRight.IsEmpty()) {
    368    numStrs++;
    369  }
    370 
    371  if (numStrs == 0) {
    372    return;
    373  }
    374  const nscoord contentWidth =
    375      aRect.width - (mPD->mEdgePaperMargin.left + mPD->mEdgePaperMargin.right);
    376  const nscoord strSpace = contentWidth / numStrs;
    377 
    378  if (!aStrLeft.IsEmpty()) {
    379    DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
    380                     nsIPrintSettings::kJustLeft, aStrLeft, aRect, aAscent,
    381                     aHeight, strSpace);
    382  }
    383  if (!aStrCenter.IsEmpty()) {
    384    DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
    385                     nsIPrintSettings::kJustCenter, aStrCenter, aRect, aAscent,
    386                     aHeight, strSpace);
    387  }
    388  if (!aStrRight.IsEmpty()) {
    389    DrawHeaderFooter(aRenderingContext, aFontMetrics, aHeaderFooter,
    390                     nsIPrintSettings::kJustRight, aStrRight, aRect, aAscent,
    391                     aHeight, strSpace);
    392  }
    393 }
    394 
    395 // Draw a header or footer string
    396 // @param aRenderingContext - rendering context to draw into
    397 // @param aHeaderFooter - indicates whether it is a header or footer
    398 // @param aJust - indicates where the string is located within the header/footer
    399 // @param aStr - the string to be drawn
    400 // @param aRect - the rect of the page
    401 // @param aHeight - the height of the font
    402 // @param aAscent - the ascent of the font
    403 // @param aWidth - available width for the string
    404 void nsPageFrame::DrawHeaderFooter(gfxContext& aRenderingContext,
    405                                   nsFontMetrics& aFontMetrics,
    406                                   nsHeaderFooterEnum aHeaderFooter,
    407                                   int32_t aJust, const nsString& aStr,
    408                                   const nsRect& aRect, nscoord aAscent,
    409                                   nscoord aHeight, nscoord aWidth) {
    410  DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
    411 
    412  if ((aHeaderFooter == eHeader && aHeight < mPageContentMargin.top) ||
    413      (aHeaderFooter == eFooter && aHeight < mPageContentMargin.bottom)) {
    414    nsAutoString str;
    415    ProcessSpecialCodes(aStr, str);
    416 
    417    int32_t len = (int32_t)str.Length();
    418    if (len == 0) {
    419      return;  // bail is empty string
    420    }
    421 
    422    int32_t index;
    423    int32_t textWidth = 0;
    424    const char16_t* text = str.get();
    425    // find how much text fits, the "position" is the size of the available area
    426    if (nsLayoutUtils::BinarySearchForPosition(drawTarget, aFontMetrics, text,
    427                                               0, 0, 0, len, int32_t(aWidth),
    428                                               index, textWidth)) {
    429      if (index < len - 1) {
    430        // we can't fit in all the text, try to remove 3 glyphs and append
    431        // three "." charactrers.
    432 
    433        // TODO: This might not actually remove three glyphs in cases where
    434        // ZWJ sequences, regional indicators, etc are used.
    435        // We also have guarantee that removing three glyphs will make enough
    436        // space for the ellipse, if they are zero-width or even just narrower
    437        // than the "." character.
    438        // See https://bugzilla.mozilla.org/1765008
    439        mozilla::intl::GraphemeClusterBreakReverseIteratorUtf16 revIter(str);
    440 
    441        // Start iteration at the point where the text does properly fit.
    442        revIter.Seek(index);
    443 
    444        // Step backwards 3 times, checking if we have any string left by the
    445        // end.
    446        revIter.Next();
    447        revIter.Next();
    448        if (const Maybe<uint32_t> maybeIndex = revIter.Next()) {
    449          // TODO: We should consider checking for the ellipse character, or
    450          // possibly for another continuation indicator based on
    451          // localization.
    452          // See https://bugzilla.mozilla.org/1765007
    453          str.Truncate(*maybeIndex);
    454          str.AppendLiteral("...");
    455        } else {
    456          // We can only fit 3 or fewer chars.  Just show nothing
    457          str.Truncate();
    458        }
    459      }
    460    } else {
    461      return;  // bail if couldn't find the correct length
    462    }
    463 
    464    if (HasRTLChars(str)) {
    465      PresContext()->SetBidiEnabled();
    466    }
    467 
    468    // calc the x and y positions of the text
    469    nscoord x =
    470        GetXPosition(aRenderingContext, aFontMetrics, aRect, aJust, str);
    471    nscoord y;
    472    if (aHeaderFooter == eHeader) {
    473      y = aRect.y + mPD->mEdgePaperMargin.top;
    474    } else {
    475      y = aRect.YMost() - aHeight - mPD->mEdgePaperMargin.bottom;
    476    }
    477 
    478    // set up new clip and draw the text
    479    aRenderingContext.Save();
    480    aRenderingContext.Clip(NSRectToSnappedRect(
    481        aRect, PresContext()->AppUnitsPerDevPixel(), *drawTarget));
    482    aRenderingContext.SetColor(sRGBColor::OpaqueBlack());
    483    nsLayoutUtils::DrawString(this, aFontMetrics, &aRenderingContext, str.get(),
    484                              str.Length(), nsPoint(x, y + aAscent), nullptr,
    485                              DrawStringFlags::ForceHorizontal);
    486    aRenderingContext.Restore();
    487  }
    488 }
    489 
    490 class nsDisplayHeaderFooter final : public nsPaintedDisplayItem {
    491 public:
    492  nsDisplayHeaderFooter(nsDisplayListBuilder* aBuilder, nsPageFrame* aFrame)
    493      : nsPaintedDisplayItem(aBuilder, aFrame) {
    494    MOZ_COUNT_CTOR(nsDisplayHeaderFooter);
    495  }
    496 
    497  MOZ_COUNTED_DTOR_FINAL(nsDisplayHeaderFooter)
    498 
    499  virtual void Paint(nsDisplayListBuilder* aBuilder,
    500                     gfxContext* aCtx) override {
    501 #ifdef DEBUG
    502    nsPageFrame* pageFrame = do_QueryFrame(mFrame);
    503    MOZ_ASSERT(pageFrame, "We should have an nsPageFrame");
    504 #endif
    505    static_cast<nsPageFrame*>(mFrame)->PaintHeaderFooter(
    506        *aCtx, ToReferenceFrame(), false);
    507  }
    508  NS_DISPLAY_DECL_NAME("HeaderFooter", TYPE_HEADER_FOOTER)
    509 
    510  virtual nsRect GetComponentAlphaBounds(
    511      nsDisplayListBuilder* aBuilder) const override {
    512    bool snap;
    513    return GetBounds(aBuilder, &snap);
    514  }
    515 };
    516 
    517 static void PaintMarginGuides(nsIFrame* aFrame, DrawTarget* aDrawTarget,
    518                              const nsRect& aDirtyRect, nsPoint aPt) {
    519  // Set up parameters needed to draw the guides: we draw them in blue,
    520  // using 2px-long dashes with 2px separation and a line width of 0.5px.
    521  // Drawing is antialiased, so on a non-hidpi screen where the line width is
    522  // less than one device pixel, it doesn't disappear but renders fainter
    523  // than a solid 1px-wide line would be.
    524  // (In many cases, the entire preview is scaled down so that the guides
    525  // will be nominally less than 1 dev px even on a hidpi screen, resulting
    526  // in lighter antialiased rendering so they don't dominate the page.)
    527  ColorPattern pattern(ToDeviceColor(sRGBColor(0.0f, 0.0f, 1.0f)));
    528  Float dashes[] = {2.0f, 2.0f};
    529  StrokeOptions stroke(/* line width (in CSS px) */ 0.5f,
    530                       JoinStyle::MITER_OR_BEVEL, CapStyle::BUTT,
    531                       /* mitre limit (default, not used) */ 10.0f,
    532                       /* set dash pattern of 2px stroke, 2px gap */
    533                       std::size(dashes), dashes,
    534                       /* dash offset */ 0.0f);
    535  DrawOptions options;
    536 
    537  MOZ_RELEASE_ASSERT(aFrame->IsPageFrame());
    538  const nsMargin& margin =
    539      static_cast<nsPageFrame*>(aFrame)->GetUsedPageContentMargin();
    540  int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
    541 
    542  // Get the frame's rect and inset by the margins to get the edges of the
    543  // content area, where we want to draw the guides.
    544  // We draw in two stages, first applying the top/bottom margins and drawing
    545  // the horizontal guides across the full width of the page.
    546  nsRect rect(aPt, aFrame->GetSize());
    547  rect.Deflate(nsMargin(margin.top, 0, margin.bottom, 0));
    548  Rect r = NSRectToRect(rect, appUnitsPerDevPx);
    549  aDrawTarget->StrokeLine(r.TopLeft(), r.TopRight(), pattern, stroke, options);
    550  aDrawTarget->StrokeLine(r.BottomLeft(), r.BottomRight(), pattern, stroke,
    551                          options);
    552 
    553  // Then reset rect, apply the left/right margins, and draw vertical guides
    554  // extending the full height of the page.
    555  rect = nsRect(aPt, aFrame->GetSize());
    556  rect.Deflate(nsMargin(0, margin.right, 0, margin.left));
    557  r = NSRectToRect(rect, appUnitsPerDevPx);
    558  aDrawTarget->StrokeLine(r.TopLeft(), r.BottomLeft(), pattern, stroke,
    559                          options);
    560  aDrawTarget->StrokeLine(r.TopRight(), r.BottomRight(), pattern, stroke,
    561                          options);
    562 }
    563 
    564 static std::tuple<uint32_t, uint32_t> GetRowAndColFromIdx(uint32_t aIdxOnSheet,
    565                                                          uint32_t aNumCols) {
    566  // Compute the row index by *dividing* the item's ordinal position by how
    567  // many items fit in each row (i.e. the number of columns), and flooring.
    568  // Compute the column index by getting the remainder of that division:
    569  // Notably, mNumRows is irrelevant to this computation; that's because
    570  // we're adding new items column-by-column rather than row-by-row.
    571  return {aIdxOnSheet / aNumCols, aIdxOnSheet % aNumCols};
    572 }
    573 
    574 // The minimum ratio for which we will center the page on the sheet when using
    575 // auto-detect logic.
    576 // Note that this ratio is of the content's size to the sheet size scaled to be
    577 // in content space, and so the actual ratio will always be from 0.0 to 1.0,
    578 // with this marking the smallest ratio we consider a near-miss.
    579 // The ratio of A4 on Letter is 0.915034. A threshold of 0.9 will ensure that
    580 // A4 on Letter works, as well as other near-misses.
    581 //
    582 // The ratio is computed as so:
    583 // scale = min(1, sheetHeight / pageHeight, sheetWidth / pageWidth)
    584 //
    585 // Where pageSize is pageWidth or pageHeight, and sheetSize is sheetWidth or
    586 // sheetHeight, respectively:
    587 // scaledPageSize = pageSize * scale
    588 // ratio = scaledPageSize / sheetSize
    589 //
    590 // A4 (210mm x 297mm) on US Letter (215.9mm x 279.4mm) is derived as so:
    591 // scale = min(1, 215.9 / 210, 279.4 / 297) = 0.9407407407..
    592 //
    593 // Using the widths:
    594 // scaledPageSize = (210 * 0.940741) = 197.556
    595 // ratio = 197.556 / 215.9 = 0.915034
    596 //
    597 // See nsPageFrame::ComputeSinglePPSPageSizeScale for scale calculation, and
    598 // OffsetToCenterPage for ratio calculation.
    599 constexpr float kCenterPageRatioThreshold = 0.9f;
    600 
    601 // Numeric values for the pref "print.center_page_on_sheet"
    602 enum {
    603  kPrintCenterPageOnSheetNever = 0,
    604  kPrintCenterPageOnSheetAlways = 1,
    605  kPrintCenterPageOnSheetAuto = 2
    606 };
    607 
    608 // Returns an offset to center the page on the sheet, with a given scale.
    609 // When no centering can/should happen, this will avoid extra calculations and
    610 // return 0.0f.
    611 // This takes into account the value of the pref "print.center_page_on_sheet".
    612 static float OffsetToCenterPage(nscoord aContentSize, nscoord aSheetSize,
    613                                float aScale, float aAppUnitsPerPixel) {
    614  MOZ_ASSERT(aScale <= 1.0f && aScale > 0.0f,
    615             "Scale must be in the range (0,1]");
    616  const unsigned centerPagePref = StaticPrefs::print_center_page_on_sheet();
    617  if (centerPagePref == kPrintCenterPageOnSheetNever) {
    618    return 0.0f;
    619  }
    620 
    621  // Determine the ratio of scaled page to the sheet size.
    622  const float sheetSize =
    623      NSAppUnitsToFloatPixels(aSheetSize, aAppUnitsPerPixel);
    624  const float scaledContentSize =
    625      NSAppUnitsToFloatPixels(aContentSize, aAppUnitsPerPixel) * aScale;
    626  const float ratio = scaledContentSize / sheetSize;
    627 
    628  // If the ratio is within the threshold, or the pref indicates we should
    629  // always center the page, return half the difference to form the offset.
    630  if (centerPagePref == kPrintCenterPageOnSheetAlways ||
    631      ratio >= kCenterPageRatioThreshold) {
    632    return (sheetSize - scaledContentSize) * 0.5f;
    633  }
    634  return 0.0f;
    635 }
    636 
    637 // Helper for BuildDisplayList:
    638 static gfx::Matrix4x4 ComputePagesPerSheetAndPageSizeTransform(
    639    const nsIFrame* aFrame, float aAppUnitsPerPixel) {
    640  MOZ_ASSERT(aFrame->IsPageFrame());
    641  auto* pageFrame = static_cast<const nsPageFrame*>(aFrame);
    642  const nsSize contentPageSize = pageFrame->ComputePageSize();
    643  MOZ_ASSERT(contentPageSize.width > 0 && contentPageSize.height > 0);
    644  nsSharedPageData* pd = pageFrame->GetSharedPageData();
    645  const auto* ppsInfo = pd->PagesPerSheetInfo();
    646 
    647  const nsContainerFrame* const parentFrame = pageFrame->GetParent();
    648  MOZ_ASSERT(parentFrame->IsPrintedSheetFrame(),
    649             "Parent of nsPageFrame should be PrintedSheetFrame");
    650  const auto* sheetFrame = static_cast<const PrintedSheetFrame*>(parentFrame);
    651 
    652  const double rotation =
    653      pageFrame->GetPageOrientationRotation(pageFrame->GetSharedPageData());
    654 
    655  gfx::Matrix4x4 transform;
    656 
    657  if (ppsInfo->mNumPages == 1) {
    658    const nsSize sheetSize = sheetFrame->GetSizeForChildren();
    659    if (rotation != 0.0) {
    660      const bool sheetIsPortrait = sheetSize.width < sheetSize.height;
    661      const bool rotatingClockwise = rotation > 0.0;
    662 
    663      // rotation point:
    664      int32_t x, y;
    665      if (rotatingClockwise != sheetIsPortrait) {
    666        // rotating portrait clockwise, or landscape counterclockwise
    667        x = y = std::min(sheetSize.width, sheetSize.height) / 2;
    668      } else {
    669        // rotating portrait counterclockwise, or landscape clockwise
    670        x = y = std::max(sheetSize.width, sheetSize.height) / 2;
    671      }
    672 
    673      transform = gfx::Matrix4x4::Translation(
    674          NSAppUnitsToFloatPixels(x, aAppUnitsPerPixel),
    675          NSAppUnitsToFloatPixels(y, aAppUnitsPerPixel), 0);
    676      transform.RotateZ(rotation);
    677      transform.PreTranslate(NSAppUnitsToFloatPixels(-x, aAppUnitsPerPixel),
    678                             NSAppUnitsToFloatPixels(-y, aAppUnitsPerPixel), 0);
    679    }
    680 
    681    // If the difference in horizontal size, after scaling, is relatively small
    682    // then center the page on the sheet.
    683    const float scale =
    684        pageFrame->ComputeSinglePPSPageSizeScale(contentPageSize);
    685    const float centeringOffset = OffsetToCenterPage(
    686        contentPageSize.width, sheetSize.width, scale, aAppUnitsPerPixel);
    687 
    688    // Only bother with the translation if it is at least one pixel.
    689    // It's possible for a mismatch in the paper size reported by the print
    690    // server and the paper size from Gecko to lead to small offsets, or
    691    // even (in combination with floating point error) a very small negative
    692    // offset. Do not apply an offset in those cases.
    693    if (centeringOffset >= 1.0f) {
    694      transform.PreTranslate(centeringOffset, 0, 0);
    695    }
    696    transform.PreScale(scale, scale, 1);
    697    return transform;
    698  }
    699 
    700  // The multiple pages-per-sheet case.
    701 
    702  // Begin with the translation of the page to its pages-per-sheet grid "cell"
    703  // (the grid origin accounts for the sheet's unwriteable margins):
    704  const nsPoint gridOrigin = sheetFrame->GetGridOrigin();
    705  const nscoord cellWidth = sheetFrame->GetGridCellWidth();
    706  const nscoord cellHeight = sheetFrame->GetGridCellHeight();
    707  uint32_t rowIdx, colIdx;
    708  std::tie(rowIdx, colIdx) = GetRowAndColFromIdx(pageFrame->IndexOnSheet(),
    709                                                 sheetFrame->GetGridNumCols());
    710  transform = gfx::Matrix4x4::Translation(
    711      NSAppUnitsToFloatPixels(gridOrigin.x + nscoord(colIdx) * cellWidth,
    712                              aAppUnitsPerPixel),
    713      NSAppUnitsToFloatPixels(gridOrigin.y + nscoord(rowIdx) * cellHeight,
    714                              aAppUnitsPerPixel),
    715      0.0f);
    716 
    717  // Scale the page to fit, centered, in the grid cell:
    718  float scaleX = float(cellWidth) / float(contentPageSize.width);
    719  float scaleY = float(cellHeight) / float(contentPageSize.height);
    720  MOZ_ASSERT(scaleX > 0.0f && scaleX <= 1.0f && scaleY > 0.0f &&
    721             scaleY <= 1.0f);
    722  float scale;
    723  float dx = 0.0f, dy = 0.0f;
    724  if (scaleX < scaleY) {
    725    scale = scaleX;
    726    // We need to scale down more for the width than the height, so we'll have
    727    // some spare space in the page's vertical direction. We offset the page
    728    // to share that space equally above and below the page to center it.
    729    nscoord extraSpace =
    730        cellHeight - NSToCoordRound(float(contentPageSize.height) * scale);
    731    dy = NSAppUnitsToFloatPixels(extraSpace / 2, aAppUnitsPerPixel);
    732  } else {
    733    scale = scaleY;
    734    nscoord extraSpace =
    735        cellWidth - NSToCoordRound(float(contentPageSize.width) * scale);
    736    dx = NSAppUnitsToFloatPixels(extraSpace / 2, aAppUnitsPerPixel);
    737  }
    738  transform.PreTranslate(dx, dy, 0.0f);
    739  transform.PreScale(scale, scale, 1.0f);
    740 
    741  // Apply 'page-orientation' rotation, if applicable:
    742  if (rotation != 0.0) {
    743    // We've already translated and scaled the page to fit the cell, ignoring
    744    // rotation. Here we rotate the page around its center and, if necessary,
    745    // also scale it to fit it to its cell for its orientation change.
    746 
    747    float fitScale = 1.0f;
    748    if (MOZ_LIKELY(cellWidth != cellHeight &&
    749                   contentPageSize.width != contentPageSize.height)) {
    750      // If neither the cell nor the page are square, the scale must change.
    751      float cellRatio = float(cellWidth) / float(cellHeight);
    752      float pageRatio =
    753          float(contentPageSize.width) / float(contentPageSize.height);
    754      const bool orientationWillMatchAfterRotation =
    755          floor(cellRatio) != floor(pageRatio);
    756      if (cellRatio > 1.0f) {
    757        cellRatio = 1.0f / cellRatio;  // normalize
    758      }
    759      if (pageRatio > 1.0f) {
    760        pageRatio = 1.0f / pageRatio;  // normalize
    761      }
    762      fitScale = std::max(cellRatio, pageRatio);
    763      if (orientationWillMatchAfterRotation) {
    764        // Scale up, not down
    765        fitScale = 1.0f / fitScale;
    766      }
    767    }
    768 
    769    transform.PreTranslate(
    770        NSAppUnitsToFloatPixels(contentPageSize.width / 2, aAppUnitsPerPixel),
    771        NSAppUnitsToFloatPixels(contentPageSize.height / 2, aAppUnitsPerPixel),
    772        0);
    773    if (MOZ_LIKELY(fitScale != 1.0f)) {
    774      transform.PreScale(fitScale, fitScale, 1.0f);
    775    }
    776    transform.RotateZ(rotation);
    777    transform.PreTranslate(
    778        NSAppUnitsToFloatPixels(-contentPageSize.width / 2, aAppUnitsPerPixel),
    779        NSAppUnitsToFloatPixels(-contentPageSize.height / 2, aAppUnitsPerPixel),
    780        0);
    781  }
    782 
    783  return transform;
    784 }
    785 
    786 nsIFrame::ComputeTransformFunction nsPageFrame::GetTransformGetter() const {
    787  return ComputePagesPerSheetAndPageSizeTransform;
    788 }
    789 
    790 nsPageContentFrame* nsPageFrame::PageContentFrame() const {
    791  nsIFrame* const frame = mFrames.FirstChild();
    792  MOZ_ASSERT(frame, "pageFrame must have one child");
    793  MOZ_ASSERT(frame->IsPageContentFrame(),
    794             "pageFrame must have pageContentFrame as the first child");
    795  return static_cast<nsPageContentFrame*>(frame);
    796 }
    797 
    798 nsSize nsPageFrame::ComputePageSize() const {
    799  // Compute the expected page-size.
    800  const StylePageSize& pageSize = PageContentFrame()->StylePage()->mSize;
    801  nsSize size = PresContext()->GetPageSize();
    802  if (pageSize.IsSize()) {
    803    // Use the specified size,
    804    // ignoring sizes that include a zero width or height.
    805    // These are also ignored in ServoStyleSet::GetPageSizeForPageName()
    806    // when getting the paper size.
    807    nscoord cssPageWidth = pageSize.AsSize().width.ToAppUnits();
    808    nscoord cssPageHeight = pageSize.AsSize().height.ToAppUnits();
    809    if (cssPageWidth > 0 && cssPageHeight > 0) {
    810      return nsSize{cssPageWidth, cssPageHeight};
    811    }
    812    // Invalid size; just return the default
    813    return size;
    814  }
    815 
    816  if (pageSize.IsOrientation()) {
    817    // Ensure the correct orientation is applied.
    818    if (pageSize.AsOrientation() == StylePageSizeOrientation::Portrait) {
    819      if (size.width > size.height) {
    820        std::swap(size.width, size.height);
    821      }
    822    } else {
    823      MOZ_ASSERT(pageSize.AsOrientation() ==
    824                 StylePageSizeOrientation::Landscape);
    825      if (size.width < size.height) {
    826        std::swap(size.width, size.height);
    827      }
    828    }
    829  } else {
    830    MOZ_ASSERT(pageSize.IsAuto(), "Impossible page-size value?");
    831  }
    832  return size;
    833 }
    834 
    835 float nsPageFrame::ComputeSinglePPSPageSizeScale(
    836    const nsSize aContentPageSize) const {
    837  MOZ_ASSERT(GetSharedPageData()->PagesPerSheetInfo()->mNumPages == 1,
    838             "Only intended for the pps==1 case");
    839  MOZ_ASSERT(aContentPageSize == ComputePageSize(),
    840             "Incorrect content page size");
    841 
    842  if (PageContentFrame()->StylePage()->mSize.IsAuto()) {
    843    return 1.0f;
    844  }
    845 
    846  const nsContainerFrame* const parent = GetParent();
    847  MOZ_ASSERT(parent && parent->IsPrintedSheetFrame(),
    848             "Parent of nsPageFrame should be PrintedSheetFrame");
    849  const auto* sheet = static_cast<const PrintedSheetFrame*>(parent);
    850 
    851  // Compute scaling due to a possible mismatch in the paper size we are
    852  // printing to (from the pres context) and the specified page size when the
    853  // content uses "@page {size: ...}" to specify a page size for the content.
    854  float scale = 1.0f;
    855 
    856  const nsSize sheetSize = sheet->GetSizeForChildren();
    857  nscoord contentPageHeight = aContentPageSize.height;
    858  // Scale down if the target is too wide.
    859  if (aContentPageSize.width > sheetSize.width) {
    860    scale *= float(sheetSize.width) / float(aContentPageSize.width);
    861    contentPageHeight = NSToCoordRound(contentPageHeight * scale);
    862  }
    863  // Scale down if the target is too tall.
    864  if (contentPageHeight > sheetSize.height) {
    865    scale *= float(sheetSize.height) / float(contentPageHeight);
    866  }
    867  MOZ_ASSERT(
    868      scale <= 1.0f,
    869      "Page-size mismatches should only have caused us to scale down, not up.");
    870  return scale;
    871 }
    872 
    873 double nsPageFrame::GetPageOrientationRotation(nsSharedPageData* aPD) const {
    874  if (aPD->PagesPerSheetInfo()->mNumPages == 1 && !PresContext()->IsScreen() &&
    875      aPD->mPrintSettings->GetOutputFormat() !=
    876          nsIPrintSettings::kOutputFormatPDF) {
    877    // In the single page-per-sheet case we rotate the page by essentially
    878    // rotating the entire sheet. But we can't do that when the output device
    879    // doesn't support mixed sheet orientations.
    880    return 0.0;
    881  }
    882 
    883  const StylePageOrientation& orientation =
    884      PageContentFrame()->StylePage()->mPageOrientation;
    885 
    886  if (orientation == StylePageOrientation::RotateLeft) {
    887    return -M_PI / 2.0;
    888  }
    889  if (orientation == StylePageOrientation::RotateRight) {
    890    return M_PI / 2.0;
    891  }
    892  return 0.0;
    893 }
    894 
    895 void nsPageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    896                                   const nsDisplayListSet& aLists) {
    897  nsDisplayList content(aBuilder);
    898  nsDisplayListSet set(&content, &content, &content, &content, &content,
    899                       &content);
    900  {
    901    DisplayListClipState::AutoSaveRestore clipState(aBuilder);
    902    clipState.Clear();
    903 
    904    nsPresContext* const pc = PresContext();
    905    {
    906      // We need to extend the building rect to include the specified page size
    907      // (scaled by the print scaling factor), in case it is larger than the
    908      // physical page size. In that case the nsPageFrame will be the size of
    909      // the physical page, but the child nsPageContentFrame will be the larger
    910      // specified page size. The more correct way to do this would be to fully
    911      // reverse the result of ComputePagesPerSheetAndPageSizeTransform to
    912      // handle this scaling, but this should have the same result and is
    913      // easier.
    914      const float scale = pc->GetPageScale();
    915      const nsSize pageSize = ComputePageSize();
    916      const nsRect scaledPageRect{0, 0, NSToCoordCeil(pageSize.width / scale),
    917                                  NSToCoordCeil(pageSize.height / scale)};
    918      nsDisplayListBuilder::AutoBuildingDisplayList buildingForPageContentFrame(
    919          aBuilder, this, scaledPageRect, scaledPageRect);
    920 
    921      nsContainerFrame::BuildDisplayList(aBuilder, set);
    922    }
    923 
    924    if (pc->IsRootPaginatedDocument()) {
    925      content.AppendNewToTop<nsDisplayHeaderFooter>(aBuilder, this);
    926 
    927      // For print-preview, show margin guides if requested in the settings.
    928      if (pc->Type() == nsPresContext::eContext_PrintPreview &&
    929          mPD->mPrintSettings->GetShowMarginGuides()) {
    930        content.AppendNewToTop<nsDisplayGeneric>(
    931            aBuilder, this, PaintMarginGuides, "MarginGuides",
    932            DisplayItemType::TYPE_MARGIN_GUIDES);
    933      }
    934    }
    935  }
    936 
    937  // We'll be drawing the page with a (usually-trivial)
    938  // N-pages-per-sheet transform applied, so our passed-in visible rect
    939  // isn't meaningful while we're drawing our children, because the
    940  // transform could scale down content whose coordinates are off-screen
    941  // such that it ends up on-screen. So: we temporarily update the visible
    942  // rect to be the child nsPageFrame's whole frame-rect (represented in
    943  // this PrintedSheetFrame's coordinate space.
    944  content.AppendNewToTop<nsDisplayTransform>(
    945      aBuilder, this, &content, content.GetBuildingRect(),
    946      nsDisplayTransform::WithTransformGetter);
    947 
    948  set.MoveTo(aLists);
    949 }
    950 
    951 //------------------------------------------------------------------------------
    952 void nsPageFrame::DeterminePageNum() {
    953  // If we have no previous continuation, we're page 1. Otherwise, we're
    954  // just one more than our previous continuation's page number.
    955  auto* prevContinuation = static_cast<nsPageFrame*>(GetPrevContinuation());
    956  mPageNum = prevContinuation ? prevContinuation->GetPageNum() + 1 : 1;
    957 }
    958 
    959 void nsPageFrame::PaintHeaderFooter(gfxContext& aRenderingContext, nsPoint aPt,
    960                                    bool aDisableSubpixelAA) {
    961  nsPresContext* pc = PresContext();
    962 
    963  nsRect rect(aPt, ComputePageSize());
    964  aRenderingContext.SetColor(sRGBColor::OpaqueBlack());
    965 
    966  DrawTargetAutoDisableSubpixelAntialiasing disable(
    967      aRenderingContext.GetDrawTarget(), aDisableSubpixelAA);
    968 
    969  // Get the FontMetrics to determine width.height of strings
    970  nsFontMetrics::Params params;
    971  params.userFontSet = pc->GetUserFontSet();
    972  params.textPerf = pc->GetTextPerfMetrics();
    973  params.featureValueLookup = pc->GetFontFeatureValuesLookup();
    974  RefPtr<nsFontMetrics> fontMet = pc->GetMetricsFor(mPD->mHeadFootFont, params);
    975 
    976  nscoord ascent = fontMet->MaxAscent();
    977  nscoord visibleHeight = fontMet->MaxHeight();
    978 
    979  // print document headers and footers
    980  nsString headerLeft, headerCenter, headerRight;
    981  mPD->mPrintSettings->GetHeaderStrLeft(headerLeft);
    982  mPD->mPrintSettings->GetHeaderStrCenter(headerCenter);
    983  mPD->mPrintSettings->GetHeaderStrRight(headerRight);
    984  DrawHeaderFooter(aRenderingContext, *fontMet, eHeader, headerLeft,
    985                   headerCenter, headerRight, rect, ascent, visibleHeight);
    986 
    987  nsString footerLeft, footerCenter, footerRight;
    988  mPD->mPrintSettings->GetFooterStrLeft(footerLeft);
    989  mPD->mPrintSettings->GetFooterStrCenter(footerCenter);
    990  mPD->mPrintSettings->GetFooterStrRight(footerRight);
    991  DrawHeaderFooter(aRenderingContext, *fontMet, eFooter, footerLeft,
    992                   footerCenter, footerRight, rect, ascent, visibleHeight);
    993 }
    994 
    995 void nsPageFrame::SetSharedPageData(nsSharedPageData* aPD) {
    996  mPD = aPD;
    997  // Set the shared data into the page frame before reflow
    998  PageContentFrame()->SetSharedPageData(mPD);
    999 }
   1000 
   1001 nsIFrame* NS_NewPageBreakFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
   1002  MOZ_ASSERT(aPresShell, "null PresShell");
   1003  // check that we are only creating page break frames when printing
   1004  NS_ASSERTION(aPresShell->GetPresContext()->IsPaginated(),
   1005               "created a page break frame while not printing");
   1006 
   1007  return new (aPresShell)
   1008      nsPageBreakFrame(aStyle, aPresShell->GetPresContext());
   1009 }
   1010 
   1011 NS_IMPL_FRAMEARENA_HELPERS(nsPageBreakFrame)
   1012 
   1013 nsPageBreakFrame::nsPageBreakFrame(ComputedStyle* aStyle,
   1014                                   nsPresContext* aPresContext)
   1015    : nsLeafFrame(aStyle, aPresContext, kClassID) {}
   1016 
   1017 nsPageBreakFrame::~nsPageBreakFrame() = default;
   1018 
   1019 IntrinsicSize nsPageBreakFrame::GetIntrinsicSize() {
   1020  IntrinsicSize intrinsicSize;
   1021  intrinsicSize.ISize(GetWritingMode())
   1022      .emplace(nsPresContext::CSSPixelsToAppUnits(1));
   1023  return intrinsicSize;
   1024 }
   1025 
   1026 void nsPageBreakFrame::Reflow(nsPresContext* aPresContext,
   1027                              ReflowOutput& aReflowOutput,
   1028                              const ReflowInput& aReflowInput,
   1029                              nsReflowStatus& aStatus) {
   1030  DO_GLOBAL_REFLOW_COUNT("nsPageBreakFrame");
   1031  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
   1032 
   1033  // Override reflow, since we don't want to deal with what our
   1034  // computed values are.
   1035  const WritingMode wm = aReflowInput.GetWritingMode();
   1036  nscoord bSize = aReflowInput.AvailableBSize();
   1037  if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
   1038    bSize = nscoord(0);
   1039  } else if (GetContent()->IsHTMLElement(nsGkAtoms::legend)) {
   1040    // If this is a page break frame for a _rendered legend_ then it should be
   1041    // ignored since these frames are inserted inside the fieldset's inner
   1042    // frame and thus "misplaced".  nsFieldSetFrame::Reflow deals with these
   1043    // forced breaks explicitly instead.
   1044    const nsContainerFrame* parent = GetParent();
   1045    if (parent &&
   1046        parent->Style()->GetPseudoType() == PseudoStyleType::fieldsetContent) {
   1047      while ((parent = parent->GetParent())) {
   1048        if (const nsFieldSetFrame* const fieldset = do_QueryFrame(parent)) {
   1049          const auto* const legend = fieldset->GetLegend();
   1050          if (legend && legend->GetContent() == GetContent()) {
   1051            bSize = nscoord(0);
   1052          }
   1053          break;
   1054        }
   1055      }
   1056    }
   1057  }
   1058  LogicalSize finalSize(wm, *GetIntrinsicSize().ISize(wm), bSize);
   1059  // round the height down to the nearest pixel
   1060  // XXX(mats) why???
   1061  finalSize.BSize(wm) -=
   1062      finalSize.BSize(wm) % nsPresContext::CSSPixelsToAppUnits(1);
   1063  aReflowOutput.SetSize(wm, finalSize);
   1064 }
   1065 
   1066 #ifdef DEBUG_FRAME_DUMP
   1067 nsresult nsPageBreakFrame::GetFrameName(nsAString& aResult) const {
   1068  return MakeFrameName(u"PageBreak"_ns, aResult);
   1069 }
   1070 #endif