Coverage Report

Created: 2018-09-25 14:53

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