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