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