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/. */
7 #include "nsPageSequenceFrame.h"
9 #include "mozilla/intl/AppDateTimeFormat.h"
10 #include "mozilla/Logging.h"
11 #include "mozilla/PresShell.h"
12 #include "mozilla/PrintedSheetFrame.h"
13 #include "mozilla/dom/HTMLCanvasElement.h"
14 #include "mozilla/gfx/Point.h"
15 #include "mozilla/StaticPresData.h"
18 #include "nsDeviceContext.h"
19 #include "nsPresContext.h"
20 #include "gfxContext.h"
21 #include "nsGkAtoms.h"
23 #include "nsIFrameInlines.h"
24 #include "nsIPrintSettings.h"
25 #include "nsPageFrame.h"
26 #include "nsSubDocumentFrame.h"
28 #include "nsCSSFrameConstructor.h"
29 #include "nsContentUtils.h"
30 #include "nsDisplayList.h"
31 #include "nsHTMLCanvasFrame.h"
32 #include "nsICanvasRenderingContextInternal.h"
33 #include "nsServiceManagerUtils.h"
37 using namespace mozilla
;
38 using namespace mozilla::dom
;
40 mozilla::LazyLogModule
gLayoutPrintingLog("printing-layout");
42 #define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1)
44 nsPageSequenceFrame
* NS_NewPageSequenceFrame(PresShell
* aPresShell
,
45 ComputedStyle
* aStyle
) {
46 return new (aPresShell
)
47 nsPageSequenceFrame(aStyle
, aPresShell
->GetPresContext());
50 NS_IMPL_FRAMEARENA_HELPERS(nsPageSequenceFrame
)
52 static const nsPagesPerSheetInfo kSupportedPagesPerSheet
[] = {
53 /* Members are: {mNumPages, mLargerNumTracks} */
64 inline void SanityCheckPagesPerSheetInfo() {
67 MOZ_ASSERT(ArrayLength(kSupportedPagesPerSheet
) > 0,
68 "Should have at least one pages-per-sheet option.");
69 MOZ_ASSERT(kSupportedPagesPerSheet
[0].mNumPages
== 1,
70 "The 0th index is reserved for default 1-page-per-sheet entry");
72 uint16_t prevInfoPPS
= 0;
73 for (const auto& info
: kSupportedPagesPerSheet
) {
74 MOZ_ASSERT(info
.mNumPages
> prevInfoPPS
,
75 "page count field should be positive & monotonically increase");
76 MOZ_ASSERT(info
.mLargerNumTracks
> 0,
77 "page grid has to have a positive number of tracks");
78 MOZ_ASSERT(info
.mNumPages
% info
.mLargerNumTracks
== 0,
79 "page count field should be evenly divisible by "
80 "the given track-count");
81 prevInfoPPS
= info
.mNumPages
;
86 const nsPagesPerSheetInfo
& nsPagesPerSheetInfo::LookupInfo(int32_t aPPS
) {
87 SanityCheckPagesPerSheetInfo();
89 // Walk the array, looking for a match:
90 for (const auto& info
: kSupportedPagesPerSheet
) {
91 if (aPPS
== info
.mNumPages
) {
96 NS_WARNING("Unsupported pages-per-sheet value");
97 // If no match was found, return the first entry (for 1 page per sheet).
98 return kSupportedPagesPerSheet
[0];
101 const nsPagesPerSheetInfo
* nsSharedPageData::PagesPerSheetInfo() {
102 if (mPagesPerSheetInfo
) {
103 return mPagesPerSheetInfo
;
106 int32_t pagesPerSheet
;
107 if (!mPrintSettings
||
108 NS_FAILED(mPrintSettings
->GetNumPagesPerSheet(&pagesPerSheet
))) {
109 // If we can't read the value from print settings, just fall back to 1.
113 mPagesPerSheetInfo
= &nsPagesPerSheetInfo::LookupInfo(pagesPerSheet
);
114 return mPagesPerSheetInfo
;
117 nsPageSequenceFrame::nsPageSequenceFrame(ComputedStyle
* aStyle
,
118 nsPresContext
* aPresContext
)
119 : nsContainerFrame(aStyle
, aPresContext
, kClassID
),
120 mMaxSheetSize(mWritingMode
),
121 mScrollportSize(mWritingMode
),
122 mCalledBeginPage(false),
123 mCurrentCanvasListSetup(false) {
124 mPageData
= MakeUnique
<nsSharedPageData
>();
125 mPageData
->mHeadFootFont
=
128 ->GetFontPrefsForLang(aStyle
->StyleFont()->mLanguage
)
129 ->GetDefaultFont(StyleGenericFontFamily::Serif
);
130 mPageData
->mHeadFootFont
.size
=
131 Length::FromPixels(CSSPixel::FromPoints(10.0f
));
132 mPageData
->mPrintSettings
= aPresContext
->GetPrintSettings();
133 MOZ_RELEASE_ASSERT(mPageData
->mPrintSettings
, "How?");
135 // Doing this here so we only have to go get these formats once
136 SetPageNumberFormat("pagenumber", "%1$d", true);
137 SetPageNumberFormat("pageofpages", "%1$d of %2$d", false);
140 nsPageSequenceFrame::~nsPageSequenceFrame() { ResetPrintCanvasList(); }
142 NS_QUERYFRAME_HEAD(nsPageSequenceFrame
)
143 NS_QUERYFRAME_ENTRY(nsPageSequenceFrame
)
144 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
146 //----------------------------------------------------------------------
148 float nsPageSequenceFrame::GetPrintPreviewScale() const {
149 nsPresContext
* pc
= PresContext();
150 float scale
= pc
->GetPrintPreviewScaleForSequenceFrameOrScrollbars();
152 WritingMode wm
= GetWritingMode();
153 if (pc
->IsScreen() && MOZ_LIKELY(mScrollportSize
.ISize(wm
) > 0 &&
154 mScrollportSize
.BSize(wm
) > 0)) {
155 // For print preview, scale down as-needed to ensure that each of our
156 // sheets will fit in the the scrollport.
158 // Check if the current scale is sufficient for our sheets to fit in inline
159 // axis (and if not, reduce the scale so that it will fit).
160 nscoord scaledISize
= NSToCoordCeil(mMaxSheetSize
.ISize(wm
) * scale
);
161 if (scaledISize
> mScrollportSize
.ISize(wm
)) {
162 scale
*= float(mScrollportSize
.ISize(wm
)) / float(scaledISize
);
165 // Further reduce the scale (if needed) to be sure each sheet will fit in
167 // NOTE: in general, a scrollport's BSize *could* be unconstrained,
168 // i.e. sized to its contents. If that happens, then shrinking the contents
169 // to fit the scrollport is not a meaningful operation in this axis, so we
170 // skip over this. But we can be pretty sure that the print-preview UI
171 // will have given the scrollport a fixed size; hence the MOZ_LIKELY here.
172 if (MOZ_LIKELY(mScrollportSize
.BSize(wm
) != NS_UNCONSTRAINEDSIZE
)) {
173 nscoord scaledBSize
= NSToCoordCeil(mMaxSheetSize
.BSize(wm
) * scale
);
174 if (scaledBSize
> mScrollportSize
.BSize(wm
)) {
175 scale
*= float(mScrollportSize
.BSize(wm
)) / float(scaledBSize
);
182 void nsPageSequenceFrame::PopulateReflowOutput(
183 ReflowOutput
& aReflowOutput
, const ReflowInput
& aReflowInput
) {
184 // Aim to fill the whole available space, not only so we can act as a
185 // background in print preview but also handle overflow in child page frames
187 // Use availableISize so we don't cause a needless horizontal scrollbar.
188 float scale
= GetPrintPreviewScale();
190 WritingMode wm
= aReflowInput
.GetWritingMode();
191 nscoord iSize
= wm
.IsVertical() ? mSize
.Height() : mSize
.Width();
192 nscoord bSize
= wm
.IsVertical() ? mSize
.Width() : mSize
.Height();
194 nscoord availableISize
= aReflowInput
.AvailableISize();
195 nscoord computedBSize
= aReflowInput
.ComputedBSize();
196 if (MOZ_UNLIKELY(computedBSize
== NS_UNCONSTRAINEDSIZE
)) {
197 // We have unconstrained BSize, which should only happen if someone calls
198 // SizeToContent() on our window (which we don't expect to happen for
199 // actual user flows, but is possible for fuzzers to trigger). We just nerf
200 // the ReflowInput's contributions to the std::max() expressions below,
201 // which does indeed make us "size to content", via letting std::max()
202 // choose the scaled iSize/bSize expressions.
203 availableISize
= computedBSize
= 0;
205 aReflowOutput
.ISize(wm
) =
206 std::max(NSToCoordFloor(iSize
* scale
), availableISize
);
207 aReflowOutput
.BSize(wm
) =
208 std::max(NSToCoordFloor(bSize
* scale
), computedBSize
);
209 aReflowOutput
.SetOverflowAreasToDesiredBounds();
212 // Helper function to compute the offset needed to center a child
213 // page-frame's margin-box inside our content-box.
214 nscoord
nsPageSequenceFrame::ComputeCenteringMargin(
215 nscoord aContainerContentBoxWidth
, nscoord aChildPaddingBoxWidth
,
216 const nsMargin
& aChildPhysicalMargin
) {
217 // We'll be centering our child's margin-box, so get the size of that:
218 nscoord childMarginBoxWidth
=
219 aChildPaddingBoxWidth
+ aChildPhysicalMargin
.LeftRight();
221 // When rendered, our child's rect will actually be scaled up by the
222 // print-preview scale factor, via ComputePageSequenceTransform().
223 // We really want to center *that scaled-up rendering* inside of
224 // aContainerContentBoxWidth. So, we scale up its margin-box here...
225 float scale
= GetPrintPreviewScale();
226 nscoord scaledChildMarginBoxWidth
=
227 NSToCoordRound(childMarginBoxWidth
* scale
);
229 // ...and see we how much space is left over, when we subtract that scaled-up
230 // size from the container width:
231 nscoord scaledExtraSpace
=
232 aContainerContentBoxWidth
- scaledChildMarginBoxWidth
;
234 if (scaledExtraSpace
<= 0) {
235 // (Don't bother centering if there's zero/negative space.)
239 // To center the child, we want to give it an additional left-margin of half
240 // of the extra space. And then, we have to scale that space back down, so
241 // that it'll produce the correct scaled-up amount when we render (because
242 // rendering will scale it back up):
243 return NSToCoordRound(scaledExtraSpace
* 0.5 / scale
);
246 uint32_t nsPageSequenceFrame::GetPagesInFirstSheet() const {
247 nsIFrame
* firstSheet
= mFrames
.FirstChild();
252 MOZ_DIAGNOSTIC_ASSERT(firstSheet
->IsPrintedSheetFrame());
253 return static_cast<PrintedSheetFrame
*>(firstSheet
)->GetNumPages();
257 * Note: we largely position/size out our children (page frames) using
258 * \*physical\* x/y/width/height values, because the print preview UI is always
259 * arranged in the same orientation, regardless of writing mode.
261 void nsPageSequenceFrame::Reflow(nsPresContext
* aPresContext
,
262 ReflowOutput
& aReflowOutput
,
263 const ReflowInput
& aReflowInput
,
264 nsReflowStatus
& aStatus
) {
266 MOZ_ASSERT(aPresContext
->IsRootPaginatedDocument(),
267 "A Page Sequence is only for real pages");
268 DO_GLOBAL_REFLOW_COUNT("nsPageSequenceFrame");
269 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aReflowOutput
, aStatus
);
270 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
271 NS_FRAME_TRACE_REFLOW_IN("nsPageSequenceFrame::Reflow");
273 auto CenterPages
= [&] {
274 for (nsIFrame
* child
: mFrames
) {
275 nsMargin pageCSSMargin
= child
->GetUsedMargin();
276 nscoord centeringMargin
=
277 ComputeCenteringMargin(aReflowInput
.ComputedWidth(),
278 child
->GetRect().Width(), pageCSSMargin
);
279 nscoord newX
= pageCSSMargin
.left
+ centeringMargin
;
281 // Adjust the child's x-position:
282 child
->MovePositionBy(nsPoint(newX
- child
->GetNormalPosition().x
, 0));
286 if (aPresContext
->IsScreen()) {
287 // When we're displayed on-screen, the computed size that we're given is
288 // the size of our scrollport. We need to save this for use in
289 // GetPrintPreviewScale.
290 // (NOTE: It's possible but unlikely that we have an unconstrained BSize
291 // here, if we're being sized to content. GetPrintPreviewScale() checks
292 // for and handles this, when making use of this member-var.)
293 mScrollportSize
= aReflowInput
.ComputedSize();
296 // Don't do incremental reflow until we've taught tables how to do
297 // it right in paginated mode.
298 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW
)) {
299 // Return our desired size
300 PopulateReflowOutput(aReflowOutput
, aReflowInput
);
301 FinishAndStoreOverflow(&aReflowOutput
);
303 if (GetSize() != aReflowOutput
.PhysicalSize()) {
309 nsIntMargin unwriteableTwips
=
310 mPageData
->mPrintSettings
->GetUnwriteableMarginInTwips();
312 nsIntMargin edgeTwips
= mPageData
->mPrintSettings
->GetEdgeInTwips();
314 // sanity check the values. three inches are sometimes needed
315 int32_t threeInches
= NS_INCHES_TO_INT_TWIPS(3.0);
316 edgeTwips
.EnsureAtMost(
317 nsIntMargin(threeInches
, threeInches
, threeInches
, threeInches
));
318 edgeTwips
.EnsureAtLeast(unwriteableTwips
);
320 mPageData
->mEdgePaperMargin
= nsPresContext::CSSTwipsToAppUnits(edgeTwips
);
322 // Get the custom page-range state:
323 mPageData
->mPrintSettings
->GetPageRanges(mPageData
->mPageRanges
);
325 // We use the CSS "margin" property on the -moz-printed-sheet pseudoelement
326 // to determine the space between each printed sheet in print preview.
327 // Keep a running y-offset for each printed sheet.
330 // These represent the maximum sheet size across all our sheets (in each
331 // axis), inflated a bit to account for the -moz-printed-sheet 'margin'.
332 nscoord maxInflatedSheetWidth
= 0;
333 nscoord maxInflatedSheetHeight
= 0;
335 // Tile the sheets vertically
336 for (nsIFrame
* kidFrame
: mFrames
) {
337 // Set the shared data into the page frame before reflow
338 MOZ_ASSERT(kidFrame
->IsPrintedSheetFrame(),
339 "we're only expecting PrintedSheetFrame as children");
340 auto* sheet
= static_cast<PrintedSheetFrame
*>(kidFrame
);
341 sheet
->SetSharedPageData(mPageData
.get());
343 // If we want to reliably access the nsPageFrame before reflowing the sheet
344 // frame, we need to call this:
345 sheet
->ClaimPageFrameFromPrevInFlow();
347 const nsSize sheetSize
= sheet
->ComputeSheetSize(aPresContext
);
350 ReflowInput
kidReflowInput(
351 aPresContext
, aReflowInput
, kidFrame
,
352 LogicalSize(kidFrame
->GetWritingMode(), sheetSize
));
353 kidReflowInput
.mBreakType
= ReflowInput::BreakType::Page
;
355 ReflowOutput
kidReflowOutput(kidReflowInput
);
356 nsReflowStatus status
;
358 kidReflowInput
.SetComputedISize(kidReflowInput
.AvailableISize());
359 // kidReflowInput.SetComputedHeight(kidReflowInput.AvailableHeight());
360 PR_PL(("AV ISize: %d BSize: %d\n", kidReflowInput
.AvailableISize(),
361 kidReflowInput
.AvailableBSize()));
363 nsMargin pageCSSMargin
= kidReflowInput
.ComputedPhysicalMargin();
364 y
+= pageCSSMargin
.top
;
366 nscoord x
= pageCSSMargin
.left
;
368 // Place and size the sheet.
369 ReflowChild(kidFrame
, aPresContext
, kidReflowOutput
, kidReflowInput
, x
, y
,
370 ReflowChildFlags::Default
, status
);
372 FinishReflowChild(kidFrame
, aPresContext
, kidReflowOutput
, &kidReflowInput
,
373 x
, y
, ReflowChildFlags::Default
);
374 MOZ_ASSERT(kidFrame
->GetSize() == sheetSize
,
375 "PrintedSheetFrame::ComputeSheetSize() gave the wrong size!");
376 y
+= kidReflowOutput
.Height();
377 y
+= pageCSSMargin
.bottom
;
379 maxInflatedSheetWidth
=
380 std::max(maxInflatedSheetWidth
,
381 kidReflowOutput
.Width() + pageCSSMargin
.LeftRight());
382 maxInflatedSheetHeight
=
383 std::max(maxInflatedSheetHeight
,
384 kidReflowOutput
.Height() + pageCSSMargin
.TopBottom());
386 // Is the sheet complete?
387 nsIFrame
* kidNextInFlow
= kidFrame
->GetNextInFlow();
389 if (status
.IsFullyComplete()) {
390 NS_ASSERTION(!kidNextInFlow
, "bad child flow list");
391 } else if (!kidNextInFlow
) {
392 // The sheet isn't complete and it doesn't have a next-in-flow, so
393 // create a continuing sheet.
394 nsIFrame
* continuingSheet
=
395 PresShell()->FrameConstructor()->CreateContinuingFrame(kidFrame
,
398 // Add it to our child list
399 mFrames
.InsertFrame(nullptr, kidFrame
, continuingSheet
);
403 nsAutoString formattedDateString
;
404 PRTime now
= PR_Now();
405 mozilla::intl::DateTimeFormat::StyleBag style
;
406 style
.date
= Some(mozilla::intl::DateTimeFormat::Style::Short
);
407 style
.time
= Some(mozilla::intl::DateTimeFormat::Style::Short
);
408 if (NS_SUCCEEDED(mozilla::intl::AppDateTimeFormat::Format(
409 style
, now
, formattedDateString
))) {
410 SetDateTimeStr(formattedDateString
);
413 // cache the size so we can set the desired size for the other reflows that
414 // happen. Since we're tiling our sheets vertically: in the x axis, we are
415 // as wide as our widest sheet (inflated via "margin"); and in the y axis,
416 // we're as tall as the sum of our sheets' inflated heights, which the 'y'
417 // variable is conveniently storing at this point.
418 mSize
= nsSize(maxInflatedSheetWidth
, y
);
420 if (aPresContext
->IsScreen()) {
421 // Also cache the maximum size of all our sheets, to use together with the
422 // scrollport size (available as our computed size, and captured higher up
423 // in this function), so that we can scale to ensure that every sheet will
424 // fit in the scrollport.
425 WritingMode wm
= aReflowInput
.GetWritingMode();
427 LogicalSize(wm
, nsSize(maxInflatedSheetWidth
, maxInflatedSheetHeight
));
430 // Return our desired size
431 // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the
433 PopulateReflowOutput(aReflowOutput
, aReflowInput
);
435 FinishAndStoreOverflow(&aReflowOutput
);
437 // Now center our pages.
440 NS_FRAME_TRACE_REFLOW_OUT("nsPageSequenceFrame::Reflow", aStatus
);
443 //----------------------------------------------------------------------
445 #ifdef DEBUG_FRAME_DUMP
446 nsresult
nsPageSequenceFrame::GetFrameName(nsAString
& aResult
) const {
447 return MakeFrameName(u
"PageSequence"_ns
, aResult
);
452 void nsPageSequenceFrame::SetPageNumberFormat(const char* aPropName
,
453 const char* aDefPropVal
,
455 // Doing this here so we only have to go get these formats once
456 nsAutoString pageNumberFormat
;
457 // Now go get the Localized Page Formating String
458 nsresult rv
= nsContentUtils::GetLocalizedString(
459 nsContentUtils::ePRINTING_PROPERTIES
, aPropName
, pageNumberFormat
);
460 if (NS_FAILED(rv
)) { // back stop formatting
461 pageNumberFormat
.AssignASCII(aDefPropVal
);
464 SetPageNumberFormat(pageNumberFormat
, aPageNumOnly
);
467 nsresult
nsPageSequenceFrame::StartPrint(nsPresContext
* aPresContext
,
468 nsIPrintSettings
* aPrintSettings
,
469 const nsAString
& aDocTitle
,
470 const nsAString
& aDocURL
) {
471 NS_ENSURE_ARG_POINTER(aPresContext
);
472 NS_ENSURE_ARG_POINTER(aPrintSettings
);
474 if (!mPageData
->mPrintSettings
) {
475 mPageData
->mPrintSettings
= aPrintSettings
;
478 if (!aDocTitle
.IsEmpty()) {
479 mPageData
->mDocTitle
= aDocTitle
;
481 if (!aDocURL
.IsEmpty()) {
482 mPageData
->mDocURL
= aDocURL
;
485 // Begin printing of the document
486 mCurrentSheetIdx
= 0;
490 static void GetPrintCanvasElementsInFrame(
491 nsIFrame
* aFrame
, nsTArray
<RefPtr
<HTMLCanvasElement
>>* aArr
) {
495 for (const auto& childList
: aFrame
->ChildLists()) {
496 for (nsIFrame
* child
: childList
.mList
) {
497 // Check if child is a nsHTMLCanvasFrame.
498 nsHTMLCanvasFrame
* canvasFrame
= do_QueryFrame(child
);
500 // If there is a canvasFrame, try to get actual canvas element.
502 HTMLCanvasElement
* canvas
=
503 HTMLCanvasElement::FromNodeOrNull(canvasFrame
->GetContent());
504 if (canvas
&& canvas
->GetMozPrintCallback()) {
505 aArr
->AppendElement(canvas
);
510 if (!child
->PrincipalChildList().FirstChild()) {
511 nsSubDocumentFrame
* subdocumentFrame
= do_QueryFrame(child
);
512 if (subdocumentFrame
) {
513 // Descend into the subdocument
514 nsIFrame
* root
= subdocumentFrame
->GetSubdocumentRootFrame();
518 // The current child is not a nsHTMLCanvasFrame OR it is but there is
519 // no HTMLCanvasElement on it. Check if children of `child` might
520 // contain a HTMLCanvasElement.
521 GetPrintCanvasElementsInFrame(child
, aArr
);
526 // Note: this isn't quite a full tree traversal, since we exclude any
527 // nsPageFame children that have the NS_PAGE_SKIPPED_BY_CUSTOM_RANGE state-bit.
528 static void GetPrintCanvasElementsInSheet(
529 PrintedSheetFrame
* aSheetFrame
, nsTArray
<RefPtr
<HTMLCanvasElement
>>* aArr
) {
530 MOZ_ASSERT(aSheetFrame
, "Caller should've null-checked for us already");
531 for (nsIFrame
* child
: aSheetFrame
->PrincipalChildList()) {
532 // Exclude any pages that are technically children but are skipped by a
533 // custom range; they're not meant to be printed, so we don't want to
534 // waste time rendering their canvas descendants.
535 MOZ_ASSERT(child
->IsPageFrame(),
536 "PrintedSheetFrame's children must all be nsPageFrames");
537 auto* pageFrame
= static_cast<nsPageFrame
*>(child
);
538 if (!pageFrame
->HasAnyStateBits(NS_PAGE_SKIPPED_BY_CUSTOM_RANGE
)) {
539 GetPrintCanvasElementsInFrame(pageFrame
, aArr
);
544 PrintedSheetFrame
* nsPageSequenceFrame::GetCurrentSheetFrame() {
546 for (nsIFrame
* child
: mFrames
) {
547 MOZ_ASSERT(child
->IsPrintedSheetFrame(),
548 "Our children must all be PrintedSheetFrame");
549 if (i
== mCurrentSheetIdx
) {
550 return static_cast<PrintedSheetFrame
*>(child
);
557 nsresult
nsPageSequenceFrame::PrePrintNextSheet(nsITimerCallback
* aCallback
,
559 PrintedSheetFrame
* currentSheet
= GetCurrentSheetFrame();
562 return NS_ERROR_FAILURE
;
565 if (!PresContext()->IsRootPaginatedDocument()) {
566 // XXXdholbert I don't think this clause is ever actually visited in
567 // practice... Maybe we should warn & return a failure code? There used to
568 // be a comment here explaining why we don't need to proceed past this
569 // point for print preview, but in fact, this function isn't even called for
575 // If the canvasList is null, then generate it and start the render
576 // process for all the canvas.
577 if (!mCurrentCanvasListSetup
) {
578 mCurrentCanvasListSetup
= true;
579 GetPrintCanvasElementsInSheet(currentSheet
, &mCurrentCanvasList
);
581 if (!mCurrentCanvasList
.IsEmpty()) {
584 // Begin printing of the document
585 nsDeviceContext
* dc
= PresContext()->DeviceContext();
587 PR_PL(("***************** BeginPage *****************\n"));
588 const gfx::IntSize sizeInPoints
=
589 currentSheet
->GetPrintTargetSizeInPoints(
590 dc
->AppUnitsPerPhysicalInch());
591 rv
= dc
->BeginPage(sizeInPoints
);
592 NS_ENSURE_SUCCESS(rv
, rv
);
594 mCalledBeginPage
= true;
596 UniquePtr
<gfxContext
> renderingContext
= dc
->CreateRenderingContext();
597 NS_ENSURE_TRUE(renderingContext
, NS_ERROR_OUT_OF_MEMORY
);
599 DrawTarget
* drawTarget
= renderingContext
->GetDrawTarget();
600 if (NS_WARN_IF(!drawTarget
)) {
601 return NS_ERROR_FAILURE
;
604 for (HTMLCanvasElement
* canvas
: Reversed(mCurrentCanvasList
)) {
605 nsIntSize size
= canvas
->GetSize();
607 RefPtr
<DrawTarget
> canvasTarget
=
608 drawTarget
->CreateSimilarDrawTarget(size
, drawTarget
->GetFormat());
613 nsICanvasRenderingContextInternal
* ctx
= canvas
->GetCurrentContext();
618 // Initialize the context with the new DrawTarget.
619 ctx
->InitializeWithDrawTarget(nullptr, WrapNotNull(canvasTarget
));
621 // Start the rendering process.
622 // Note: Other than drawing to our CanvasRenderingContext2D, the
623 // callback cannot access or mutate our static clone document. It is
624 // evaluated in its original context (the window of the original
625 // document) of course, and our canvas has a strong ref to the
626 // original HTMLCanvasElement (in mOriginalCanvas) so that if the
627 // callback calls GetCanvas() on our CanvasRenderingContext2D (passed
628 // to it via a MozCanvasPrintState argument) it will be given the
629 // original 'canvas' element.
630 AutoWeakFrame weakFrame
= this;
631 canvas
->DispatchPrintCallback(aCallback
);
632 NS_ENSURE_STATE(weakFrame
.IsAlive());
636 uint32_t doneCounter
= 0;
637 for (HTMLCanvasElement
* canvas
: mCurrentCanvasList
) {
638 if (canvas
->IsPrintCallbackDone()) {
642 // If all canvas have finished rendering, return true, otherwise false.
643 *aDone
= doneCounter
== mCurrentCanvasList
.Length();
648 void nsPageSequenceFrame::ResetPrintCanvasList() {
649 for (int32_t i
= mCurrentCanvasList
.Length() - 1; i
>= 0; i
--) {
650 HTMLCanvasElement
* canvas
= mCurrentCanvasList
[i
];
651 canvas
->ResetPrintCallback();
654 mCurrentCanvasList
.Clear();
655 mCurrentCanvasListSetup
= false;
658 nsresult
nsPageSequenceFrame::PrintNextSheet() {
659 // Note: When print al the pages or a page range the printed page shows the
660 // actual page number, when printing selection it prints the page number
661 // starting with the first page of the selection. For example if the user has
662 // a selection that starts on page 2 and ends on page 3, the page numbers when
663 // print are 1 and then two (which is different than printing a page range,
664 // where the page numbers would have been 2 and then 3)
666 PrintedSheetFrame
* currentSheetFrame
= GetCurrentSheetFrame();
667 if (!currentSheetFrame
) {
668 return NS_ERROR_FAILURE
;
673 nsDeviceContext
* dc
= PresContext()->DeviceContext();
675 if (PresContext()->IsRootPaginatedDocument()) {
676 if (!mCalledBeginPage
) {
677 // We must make sure BeginPage() has been called since some printing
678 // backends can't give us a valid rendering context for a [physical]
681 PR_PL(("***************** BeginPage *****************\n"));
682 const gfx::IntSize sizeInPoints
=
683 currentSheetFrame
->GetPrintTargetSizeInPoints(
684 dc
->AppUnitsPerPhysicalInch());
685 rv
= dc
->BeginPage(sizeInPoints
);
686 NS_ENSURE_SUCCESS(rv
, rv
);
690 PR_PL(("SeqFr::PrintNextSheet -> %p SheetIdx: %d", currentSheetFrame
,
693 // CreateRenderingContext can fail
694 UniquePtr
<gfxContext
> gCtx
= dc
->CreateRenderingContext();
695 NS_ENSURE_TRUE(gCtx
, NS_ERROR_OUT_OF_MEMORY
);
697 nsRect
drawingRect(nsPoint(0, 0), currentSheetFrame
->GetSize());
698 nsRegion
drawingRegion(drawingRect
);
699 nsLayoutUtils::PaintFrame(gCtx
.get(), currentSheetFrame
, drawingRegion
,
701 nsDisplayListBuilderMode::PaintForPrinting
,
702 nsLayoutUtils::PaintFrameFlags::SyncDecodeImages
);
706 nsresult
nsPageSequenceFrame::DoPageEnd() {
708 if (PresContext()->IsRootPaginatedDocument()) {
709 PR_PL(("***************** End Page (DoPageEnd) *****************\n"));
710 rv
= PresContext()->DeviceContext()->EndPage();
711 // Fall through to clean up resources/state below even if EndPage failed.
714 ResetPrintCanvasList();
715 mCalledBeginPage
= false;
722 static gfx::Matrix4x4
ComputePageSequenceTransform(const nsIFrame
* aFrame
,
723 float aAppUnitsPerPixel
) {
724 MOZ_ASSERT(aFrame
->IsPageSequenceFrame());
726 static_cast<const nsPageSequenceFrame
*>(aFrame
)->GetPrintPreviewScale();
727 return gfx::Matrix4x4::Scaling(scale
, scale
, 1);
730 nsIFrame::ComputeTransformFunction
nsPageSequenceFrame::GetTransformGetter()
732 return ComputePageSequenceTransform
;
735 void nsPageSequenceFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
736 const nsDisplayListSet
& aLists
) {
737 aBuilder
->SetDisablePartialUpdates(true);
738 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
740 nsDisplayList
content(aBuilder
);
743 // Clear clip state while we construct the children of the
744 // nsDisplayTransform, since they'll be in a different coordinate system.
745 DisplayListClipState::AutoSaveRestore
clipState(aBuilder
);
748 nsIFrame
* child
= PrincipalChildList().FirstChild();
749 nsRect visible
= aBuilder
->GetVisibleRect();
750 visible
.ScaleInverseRoundOut(GetPrintPreviewScale());
753 if (child
->InkOverflowRectRelativeToParent().Intersects(visible
)) {
754 nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(
755 aBuilder
, child
, visible
- child
->GetPosition(),
756 visible
- child
->GetPosition());
757 child
->BuildDisplayListForStackingContext(aBuilder
, &content
);
758 aBuilder
->ResetMarkedFramesForDisplayList(this);
760 child
= child
->GetNextSibling();
764 content
.AppendNewToTop
<nsDisplayTransform
>(
765 aBuilder
, this, &content
, content
.GetBuildingRect(),
766 nsDisplayTransform::WithTransformGetter
);
768 aLists
.Content()->AppendToTop(&content
);
771 //------------------------------------------------------------------------------
772 void nsPageSequenceFrame::SetPageNumberFormat(const nsAString
& aFormatStr
,
773 bool aForPageNumOnly
) {
774 NS_ASSERTION(mPageData
!= nullptr, "mPageData string cannot be null!");
776 if (aForPageNumOnly
) {
777 mPageData
->mPageNumFormat
= aFormatStr
;
779 mPageData
->mPageNumAndTotalsFormat
= aFormatStr
;
783 //------------------------------------------------------------------------------
784 void nsPageSequenceFrame::SetDateTimeStr(const nsAString
& aDateTimeStr
) {
785 NS_ASSERTION(mPageData
!= nullptr, "mPageData string cannot be null!");
787 mPageData
->mDateTimeStr
= aDateTimeStr
;