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/Logging.h"
10 #include "mozilla/PresShell.h"
11 #include "mozilla/PrintedSheetFrame.h"
12 #include "mozilla/dom/HTMLCanvasElement.h"
13 #include "mozilla/StaticPresData.h"
15 #include "DateTimeFormat.h"
17 #include "nsDeviceContext.h"
18 #include "nsPresContext.h"
19 #include "gfxContext.h"
20 #include "nsGkAtoms.h"
22 #include "nsIFrameInlines.h"
23 #include "nsIPrintSettings.h"
24 #include "nsPageFrame.h"
25 #include "nsSubDocumentFrame.h"
27 #include "nsCSSFrameConstructor.h"
28 #include "nsContentUtils.h"
29 #include "nsDisplayList.h"
30 #include "nsHTMLCanvasFrame.h"
31 #include "nsICanvasRenderingContextInternal.h"
32 #include "nsServiceManagerUtils.h"
36 using namespace mozilla
;
37 using namespace mozilla::dom
;
39 mozilla::LazyLogModule
gLayoutPrintingLog("printing-layout");
41 #define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1)
43 nsPageSequenceFrame
* NS_NewPageSequenceFrame(PresShell
* aPresShell
,
44 ComputedStyle
* aStyle
) {
45 return new (aPresShell
)
46 nsPageSequenceFrame(aStyle
, aPresShell
->GetPresContext());
49 NS_IMPL_FRAMEARENA_HELPERS(nsPageSequenceFrame
)
51 static const nsPagesPerSheetInfo kSupportedPagesPerSheet
[] = {
52 /* Members are: {mNumPages, mLargerNumTracks} */
63 inline void SanityCheckPagesPerSheetInfo() {
66 MOZ_ASSERT(ArrayLength(kSupportedPagesPerSheet
) > 0,
67 "Should have at least one pages-per-sheet option.");
68 MOZ_ASSERT(kSupportedPagesPerSheet
[0].mNumPages
== 1,
69 "The 0th index is reserved for default 1-page-per-sheet entry");
71 uint16_t prevInfoPPS
= 0;
72 for (const auto& info
: kSupportedPagesPerSheet
) {
73 MOZ_ASSERT(info
.mNumPages
> prevInfoPPS
,
74 "page count field should be positive & monotonically increase");
75 MOZ_ASSERT(info
.mLargerNumTracks
> 0,
76 "page grid has to have a positive number of tracks");
77 MOZ_ASSERT(info
.mNumPages
% info
.mLargerNumTracks
== 0,
78 "page count field should be evenly divisible by "
79 "the given track-count");
80 prevInfoPPS
= info
.mNumPages
;
85 const nsPagesPerSheetInfo
& nsPagesPerSheetInfo::LookupInfo(int32_t aPPS
) {
86 SanityCheckPagesPerSheetInfo();
88 // Walk the array, looking for a match:
89 for (const auto& info
: kSupportedPagesPerSheet
) {
90 if (aPPS
== info
.mNumPages
) {
95 NS_WARNING("Unsupported pages-per-sheet value");
96 // If no match was found, return the first entry (for 1 page per sheet).
97 return kSupportedPagesPerSheet
[0];
100 const nsPagesPerSheetInfo
* nsSharedPageData::PagesPerSheetInfo() {
101 if (mPagesPerSheetInfo
) {
102 return mPagesPerSheetInfo
;
105 int32_t pagesPerSheet
;
106 if (!mPrintSettings
||
107 NS_FAILED(mPrintSettings
->GetNumPagesPerSheet(&pagesPerSheet
))) {
108 // If we can't read the value from print settings, just fall back to 1.
112 mPagesPerSheetInfo
= &nsPagesPerSheetInfo::LookupInfo(pagesPerSheet
);
113 return mPagesPerSheetInfo
;
116 nsPageSequenceFrame::nsPageSequenceFrame(ComputedStyle
* aStyle
,
117 nsPresContext
* aPresContext
)
118 : nsContainerFrame(aStyle
, aPresContext
, kClassID
),
119 mMaxSheetSize(mWritingMode
),
120 mScrollportSize(mWritingMode
),
121 mCalledBeginPage(false),
122 mCurrentCanvasListSetup(false) {
123 mPageData
= MakeUnique
<nsSharedPageData
>();
124 mPageData
->mHeadFootFont
=
127 ->GetFontPrefsForLang(aStyle
->StyleFont()->mLanguage
)
128 ->GetDefaultFont(StyleGenericFontFamily::Serif
);
129 mPageData
->mHeadFootFont
.size
=
130 Length::FromPixels(CSSPixel::FromPoints(10.0f
));
131 mPageData
->mPrintSettings
= aPresContext
->GetPrintSettings();
132 MOZ_RELEASE_ASSERT(mPageData
->mPrintSettings
, "How?");
134 // Doing this here so we only have to go get these formats once
135 SetPageNumberFormat("pagenumber", "%1$d", true);
136 SetPageNumberFormat("pageofpages", "%1$d of %2$d", false);
139 nsPageSequenceFrame::~nsPageSequenceFrame() { ResetPrintCanvasList(); }
141 NS_QUERYFRAME_HEAD(nsPageSequenceFrame
)
142 NS_QUERYFRAME_ENTRY(nsPageSequenceFrame
)
143 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
145 //----------------------------------------------------------------------
147 float nsPageSequenceFrame::GetPrintPreviewScale() const {
148 nsPresContext
* pc
= PresContext();
149 float scale
= pc
->GetPrintPreviewScaleForSequenceFrame();
151 WritingMode wm
= GetWritingMode();
152 if (pc
->IsScreen() && MOZ_LIKELY(mScrollportSize
.ISize(wm
) > 0 &&
153 mScrollportSize
.BSize(wm
) > 0)) {
154 // For print preview, scale down as-needed to ensure that each of our
155 // sheets will fit in the the scrollport.
157 // Check if the current scale is sufficient for our sheets to fit in inline
158 // axis (and if not, reduce the scale so that it will fit).
159 nscoord scaledISize
= NSToCoordCeil(mMaxSheetSize
.ISize(wm
) * scale
);
160 if (scaledISize
> mScrollportSize
.ISize(wm
)) {
161 scale
*= float(mScrollportSize
.ISize(wm
)) / float(scaledISize
);
164 // Further reduce the scale (if needed) to be sure each sheet will fit in
166 // NOTE: in general, a scrollport's BSize *could* be unconstrained,
167 // i.e. sized to its contents. If that happens, then shrinking the contents
168 // to fit the scrollport is not a meaningful operation in this axis, so we
169 // skip over this. But we can be pretty sure that the print-preview UI
170 // will have given the scrollport a fixed size; hence the MOZ_LIKELY here.
171 if (MOZ_LIKELY(mScrollportSize
.BSize(wm
) != NS_UNCONSTRAINEDSIZE
)) {
172 nscoord scaledBSize
= NSToCoordCeil(mMaxSheetSize
.BSize(wm
) * scale
);
173 if (scaledBSize
> mScrollportSize
.BSize(wm
)) {
174 scale
*= float(mScrollportSize
.BSize(wm
)) / float(scaledBSize
);
181 void nsPageSequenceFrame::PopulateReflowOutput(
182 ReflowOutput
& aReflowOutput
, const ReflowInput
& aReflowInput
) {
183 // Aim to fill the whole available space, not only so we can act as a
184 // background in print preview but also handle overflow in child page frames
186 // Use availableISize so we don't cause a needless horizontal scrollbar.
187 float scale
= GetPrintPreviewScale();
189 WritingMode wm
= aReflowInput
.GetWritingMode();
190 nscoord iSize
= wm
.IsVertical() ? mSize
.Height() : mSize
.Width();
191 nscoord bSize
= wm
.IsVertical() ? mSize
.Width() : mSize
.Height();
193 aReflowOutput
.ISize(wm
) =
194 std::max(NSToCoordFloor(iSize
* scale
), aReflowInput
.AvailableISize());
195 aReflowOutput
.BSize(wm
) =
196 std::max(NSToCoordFloor(bSize
* scale
), aReflowInput
.ComputedBSize());
197 aReflowOutput
.SetOverflowAreasToDesiredBounds();
200 // Helper function to compute the offset needed to center a child
201 // page-frame's margin-box inside our content-box.
202 nscoord
nsPageSequenceFrame::ComputeCenteringMargin(
203 nscoord aContainerContentBoxWidth
, nscoord aChildPaddingBoxWidth
,
204 const nsMargin
& aChildPhysicalMargin
) {
205 // We'll be centering our child's margin-box, so get the size of that:
206 nscoord childMarginBoxWidth
=
207 aChildPaddingBoxWidth
+ aChildPhysicalMargin
.LeftRight();
209 // When rendered, our child's rect will actually be scaled up by the
210 // print-preview scale factor, via ComputePageSequenceTransform().
211 // We really want to center *that scaled-up rendering* inside of
212 // aContainerContentBoxWidth. So, we scale up its margin-box here...
213 float scale
= GetPrintPreviewScale();
214 nscoord scaledChildMarginBoxWidth
=
215 NSToCoordRound(childMarginBoxWidth
* scale
);
217 // ...and see we how much space is left over, when we subtract that scaled-up
218 // size from the container width:
219 nscoord scaledExtraSpace
=
220 aContainerContentBoxWidth
- scaledChildMarginBoxWidth
;
222 if (scaledExtraSpace
<= 0) {
223 // (Don't bother centering if there's zero/negative space.)
227 // To center the child, we want to give it an additional left-margin of half
228 // of the extra space. And then, we have to scale that space back down, so
229 // that it'll produce the correct scaled-up amount when we render (because
230 // rendering will scale it back up):
231 return NSToCoordRound(scaledExtraSpace
* 0.5 / scale
);
234 uint32_t nsPageSequenceFrame::GetPagesInFirstSheet() const {
235 nsIFrame
* firstSheet
= mFrames
.FirstChild();
240 MOZ_DIAGNOSTIC_ASSERT(firstSheet
->IsPrintedSheetFrame());
241 return static_cast<PrintedSheetFrame
*>(firstSheet
)->GetNumPages();
245 * Note: we largely position/size out our children (page frames) using
246 * \*physical\* x/y/width/height values, because the print preview UI is always
247 * arranged in the same orientation, regardless of writing mode.
249 void nsPageSequenceFrame::Reflow(nsPresContext
* aPresContext
,
250 ReflowOutput
& aReflowOutput
,
251 const ReflowInput
& aReflowInput
,
252 nsReflowStatus
& aStatus
) {
254 MOZ_ASSERT(aPresContext
->IsRootPaginatedDocument(),
255 "A Page Sequence is only for real pages");
256 DO_GLOBAL_REFLOW_COUNT("nsPageSequenceFrame");
257 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aReflowOutput
, aStatus
);
258 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
259 NS_FRAME_TRACE_REFLOW_IN("nsPageSequenceFrame::Reflow");
261 auto CenterPages
= [&] {
262 for (nsIFrame
* child
: mFrames
) {
263 nsMargin pageCSSMargin
= child
->GetUsedMargin();
264 nscoord centeringMargin
=
265 ComputeCenteringMargin(aReflowInput
.ComputedWidth(),
266 child
->GetRect().Width(), pageCSSMargin
);
267 nscoord newX
= pageCSSMargin
.left
+ centeringMargin
;
269 // Adjust the child's x-position:
270 child
->MovePositionBy(nsPoint(newX
- child
->GetNormalPosition().x
, 0));
274 if (aPresContext
->IsScreen()) {
275 // When we're displayed on-screen, the computed size that we're given is
276 // the size of our scrollport. We need to save this for use in
277 // GetPrintPreviewScale.
278 mScrollportSize
= aReflowInput
.ComputedSize();
281 // Don't do incremental reflow until we've taught tables how to do
282 // it right in paginated mode.
283 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW
)) {
284 // Return our desired size
285 PopulateReflowOutput(aReflowOutput
, aReflowInput
);
286 FinishAndStoreOverflow(&aReflowOutput
);
288 if (GetSize() != aReflowOutput
.PhysicalSize()) {
294 nsIntMargin unwriteableTwips
=
295 mPageData
->mPrintSettings
->GetUnwriteableMarginInTwips();
297 nsIntMargin edgeTwips
= mPageData
->mPrintSettings
->GetEdgeInTwips();
299 // sanity check the values. three inches are sometimes needed
300 int32_t threeInches
= NS_INCHES_TO_INT_TWIPS(3.0);
301 edgeTwips
.EnsureAtMost(
302 nsIntMargin(threeInches
, threeInches
, threeInches
, threeInches
));
303 edgeTwips
.EnsureAtLeast(unwriteableTwips
);
305 mPageData
->mEdgePaperMargin
= nsPresContext::CSSTwipsToAppUnits(edgeTwips
);
307 // Get the custom page-range state:
308 mPageData
->mPrintSettings
->GetPageRanges(mPageData
->mPageRanges
);
310 // We use the CSS "margin" property on the -moz-printed-sheet pseudoelement
311 // to determine the space between each printed sheet in print preview.
312 // Keep a running y-offset for each printed sheet.
315 // These represent the maximum sheet size across all our sheets (in each
316 // axis), inflated a bit to account for the -moz-printed-sheet 'margin'.
317 nscoord maxInflatedSheetWidth
= 0;
318 nscoord maxInflatedSheetHeight
= 0;
320 // Determine the app-unit size of each printed sheet. This is normally the
321 // same as the app-unit size of a page, but it might need the components
322 // swapped, depending on what HasOrthogonalSheetsAndPages says.
323 nsSize sheetSize
= aPresContext
->GetPageSize();
324 if (mPageData
->mPrintSettings
->HasOrthogonalSheetsAndPages()) {
325 std::swap(sheetSize
.width
, sheetSize
.height
);
328 // Tile the sheets vertically
329 for (nsIFrame
* kidFrame
: mFrames
) {
330 // Set the shared data into the page frame before reflow
331 MOZ_ASSERT(kidFrame
->IsPrintedSheetFrame(),
332 "we're only expecting PrintedSheetFrame as children");
333 auto* sheet
= static_cast<PrintedSheetFrame
*>(kidFrame
);
334 sheet
->SetSharedPageData(mPageData
.get());
337 ReflowInput
kidReflowInput(
338 aPresContext
, aReflowInput
, kidFrame
,
339 LogicalSize(kidFrame
->GetWritingMode(), sheetSize
));
340 ReflowOutput
kidReflowOutput(kidReflowInput
);
341 nsReflowStatus status
;
343 kidReflowInput
.SetComputedISize(kidReflowInput
.AvailableISize());
344 // kidReflowInput.SetComputedHeight(kidReflowInput.AvailableHeight());
345 PR_PL(("AV ISize: %d BSize: %d\n", kidReflowInput
.AvailableISize(),
346 kidReflowInput
.AvailableBSize()));
348 nsMargin pageCSSMargin
= kidReflowInput
.ComputedPhysicalMargin();
349 y
+= pageCSSMargin
.top
;
351 nscoord x
= pageCSSMargin
.left
;
353 // Place and size the sheet.
354 ReflowChild(kidFrame
, aPresContext
, kidReflowOutput
, kidReflowInput
, x
, y
,
355 ReflowChildFlags::Default
, status
);
357 FinishReflowChild(kidFrame
, aPresContext
, kidReflowOutput
, &kidReflowInput
,
358 x
, y
, ReflowChildFlags::Default
);
359 y
+= kidReflowOutput
.Height();
360 y
+= pageCSSMargin
.bottom
;
362 maxInflatedSheetWidth
=
363 std::max(maxInflatedSheetWidth
,
364 kidReflowOutput
.Width() + pageCSSMargin
.LeftRight());
365 maxInflatedSheetHeight
=
366 std::max(maxInflatedSheetHeight
,
367 kidReflowOutput
.Height() + pageCSSMargin
.TopBottom());
369 // Is the sheet complete?
370 nsIFrame
* kidNextInFlow
= kidFrame
->GetNextInFlow();
372 if (status
.IsFullyComplete()) {
373 NS_ASSERTION(!kidNextInFlow
, "bad child flow list");
374 } else if (!kidNextInFlow
) {
375 // The sheet isn't complete and it doesn't have a next-in-flow, so
376 // create a continuing sheet.
377 nsIFrame
* continuingSheet
=
378 PresShell()->FrameConstructor()->CreateContinuingFrame(kidFrame
,
381 // Add it to our child list
382 mFrames
.InsertFrame(nullptr, kidFrame
, continuingSheet
);
386 nsAutoString formattedDateString
;
387 PRTime now
= PR_Now();
388 if (NS_SUCCEEDED(DateTimeFormat::FormatPRTime(
389 kDateFormatShort
, kTimeFormatShort
, now
, formattedDateString
))) {
390 SetDateTimeStr(formattedDateString
);
393 // cache the size so we can set the desired size for the other reflows that
394 // happen. Since we're tiling our sheets vertically: in the x axis, we are
395 // as wide as our widest sheet (inflated via "margin"); and in the y axis,
396 // we're as tall as the sum of our sheets' inflated heights, which the 'y'
397 // variable is conveniently storing at this point.
398 mSize
= nsSize(maxInflatedSheetWidth
, y
);
400 if (aPresContext
->IsScreen()) {
401 // Also cache the maximum size of all our sheets, to use together with the
402 // scrollport size (available as our computed size, and captured higher up
403 // in this function), so that we can scale to ensure that every sheet will
404 // fit in the scrollport.
405 WritingMode wm
= aReflowInput
.GetWritingMode();
407 LogicalSize(wm
, nsSize(maxInflatedSheetWidth
, maxInflatedSheetHeight
));
410 // Return our desired size
411 // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the
413 PopulateReflowOutput(aReflowOutput
, aReflowInput
);
415 FinishAndStoreOverflow(&aReflowOutput
);
417 // Now center our pages.
420 NS_FRAME_TRACE_REFLOW_OUT("nsPageSequenceFrame::Reflow", aStatus
);
421 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowInput
, aReflowOutput
);
424 //----------------------------------------------------------------------
426 #ifdef DEBUG_FRAME_DUMP
427 nsresult
nsPageSequenceFrame::GetFrameName(nsAString
& aResult
) const {
428 return MakeFrameName(u
"PageSequence"_ns
, aResult
);
433 void nsPageSequenceFrame::SetPageNumberFormat(const char* aPropName
,
434 const char* aDefPropVal
,
436 // Doing this here so we only have to go get these formats once
437 nsAutoString pageNumberFormat
;
438 // Now go get the Localized Page Formating String
439 nsresult rv
= nsContentUtils::GetLocalizedString(
440 nsContentUtils::ePRINTING_PROPERTIES
, aPropName
, pageNumberFormat
);
441 if (NS_FAILED(rv
)) { // back stop formatting
442 pageNumberFormat
.AssignASCII(aDefPropVal
);
445 SetPageNumberFormat(pageNumberFormat
, aPageNumOnly
);
448 nsresult
nsPageSequenceFrame::StartPrint(nsPresContext
* aPresContext
,
449 nsIPrintSettings
* aPrintSettings
,
450 const nsAString
& aDocTitle
,
451 const nsAString
& aDocURL
) {
452 NS_ENSURE_ARG_POINTER(aPresContext
);
453 NS_ENSURE_ARG_POINTER(aPrintSettings
);
455 if (!mPageData
->mPrintSettings
) {
456 mPageData
->mPrintSettings
= aPrintSettings
;
459 if (!aDocTitle
.IsEmpty()) {
460 mPageData
->mDocTitle
= aDocTitle
;
462 if (!aDocURL
.IsEmpty()) {
463 mPageData
->mDocURL
= aDocURL
;
466 // Begin printing of the document
467 mCurrentSheetIdx
= 0;
471 static void GetPrintCanvasElementsInFrame(
472 nsIFrame
* aFrame
, nsTArray
<RefPtr
<HTMLCanvasElement
>>* aArr
) {
476 for (const auto& childList
: aFrame
->ChildLists()) {
477 for (nsIFrame
* child
: childList
.mList
) {
478 // Check if child is a nsHTMLCanvasFrame.
479 nsHTMLCanvasFrame
* canvasFrame
= do_QueryFrame(child
);
481 // If there is a canvasFrame, try to get actual canvas element.
483 HTMLCanvasElement
* canvas
=
484 HTMLCanvasElement::FromNodeOrNull(canvasFrame
->GetContent());
485 if (canvas
&& canvas
->GetMozPrintCallback()) {
486 aArr
->AppendElement(canvas
);
491 if (!child
->PrincipalChildList().FirstChild()) {
492 nsSubDocumentFrame
* subdocumentFrame
= do_QueryFrame(child
);
493 if (subdocumentFrame
) {
494 // Descend into the subdocument
495 nsIFrame
* root
= subdocumentFrame
->GetSubdocumentRootFrame();
499 // The current child is not a nsHTMLCanvasFrame OR it is but there is
500 // no HTMLCanvasElement on it. Check if children of `child` might
501 // contain a HTMLCanvasElement.
502 GetPrintCanvasElementsInFrame(child
, aArr
);
507 // Note: this isn't quite a full tree traversal, since we exclude any
508 // nsPageFame children that have the NS_PAGE_SKIPPED_BY_CUSTOM_RANGE state-bit.
509 static void GetPrintCanvasElementsInSheet(
510 PrintedSheetFrame
* aSheetFrame
, nsTArray
<RefPtr
<HTMLCanvasElement
>>* aArr
) {
511 MOZ_ASSERT(aSheetFrame
, "Caller should've null-checked for us already");
512 for (nsIFrame
* child
: aSheetFrame
->PrincipalChildList()) {
513 // Exclude any pages that are technically children but are skipped by a
514 // custom range; they're not meant to be printed, so we don't want to
515 // waste time rendering their canvas descendants.
516 MOZ_ASSERT(child
->IsPageFrame(),
517 "PrintedSheetFrame's children must all be nsPageFrames");
518 auto* pageFrame
= static_cast<nsPageFrame
*>(child
);
519 if (!pageFrame
->HasAnyStateBits(NS_PAGE_SKIPPED_BY_CUSTOM_RANGE
)) {
520 GetPrintCanvasElementsInFrame(pageFrame
, aArr
);
525 PrintedSheetFrame
* nsPageSequenceFrame::GetCurrentSheetFrame() {
527 for (nsIFrame
* child
: mFrames
) {
528 MOZ_ASSERT(child
->IsPrintedSheetFrame(),
529 "Our children must all be PrintedSheetFrame");
530 if (i
== mCurrentSheetIdx
) {
531 return static_cast<PrintedSheetFrame
*>(child
);
538 nsresult
nsPageSequenceFrame::PrePrintNextSheet(nsITimerCallback
* aCallback
,
540 PrintedSheetFrame
* currentSheet
= GetCurrentSheetFrame();
543 return NS_ERROR_FAILURE
;
546 if (!PresContext()->IsRootPaginatedDocument()) {
547 // XXXdholbert I don't think this clause is ever actually visited in
548 // practice... Maybe we should warn & return a failure code? There used to
549 // be a comment here explaining why we don't need to proceed past this
550 // point for print preview, but in fact, this function isn't even called for
556 // If the canvasList is null, then generate it and start the render
557 // process for all the canvas.
558 if (!mCurrentCanvasListSetup
) {
559 mCurrentCanvasListSetup
= true;
560 GetPrintCanvasElementsInSheet(currentSheet
, &mCurrentCanvasList
);
562 if (!mCurrentCanvasList
.IsEmpty()) {
565 // Begin printing of the document
566 nsDeviceContext
* dc
= PresContext()->DeviceContext();
568 PR_PL(("***************** BeginPage *****************\n"));
569 rv
= dc
->BeginPage();
570 NS_ENSURE_SUCCESS(rv
, rv
);
572 mCalledBeginPage
= true;
574 RefPtr
<gfxContext
> renderingContext
= dc
->CreateRenderingContext();
575 NS_ENSURE_TRUE(renderingContext
, NS_ERROR_OUT_OF_MEMORY
);
577 DrawTarget
* drawTarget
= renderingContext
->GetDrawTarget();
578 if (NS_WARN_IF(!drawTarget
)) {
579 return NS_ERROR_FAILURE
;
582 for (HTMLCanvasElement
* canvas
: Reversed(mCurrentCanvasList
)) {
583 nsIntSize size
= canvas
->GetSize();
585 RefPtr
<DrawTarget
> canvasTarget
=
586 drawTarget
->CreateSimilarDrawTarget(size
, drawTarget
->GetFormat());
591 nsICanvasRenderingContextInternal
* ctx
= canvas
->GetCurrentContext();
596 // Initialize the context with the new DrawTarget.
597 ctx
->InitializeWithDrawTarget(nullptr, WrapNotNull(canvasTarget
));
599 // Start the rendering process.
600 // Note: Other than drawing to our CanvasRenderingContext2D, the
601 // callback cannot access or mutate our static clone document. It is
602 // evaluated in its original context (the window of the original
603 // document) of course, and our canvas has a strong ref to the
604 // original HTMLCanvasElement (in mOriginalCanvas) so that if the
605 // callback calls GetCanvas() on our CanvasRenderingContext2D (passed
606 // to it via a MozCanvasPrintState argument) it will be given the
607 // original 'canvas' element.
608 AutoWeakFrame weakFrame
= this;
609 canvas
->DispatchPrintCallback(aCallback
);
610 NS_ENSURE_STATE(weakFrame
.IsAlive());
614 uint32_t doneCounter
= 0;
615 for (HTMLCanvasElement
* canvas
: mCurrentCanvasList
) {
616 if (canvas
->IsPrintCallbackDone()) {
620 // If all canvas have finished rendering, return true, otherwise false.
621 *aDone
= doneCounter
== mCurrentCanvasList
.Length();
626 void nsPageSequenceFrame::ResetPrintCanvasList() {
627 for (int32_t i
= mCurrentCanvasList
.Length() - 1; i
>= 0; i
--) {
628 HTMLCanvasElement
* canvas
= mCurrentCanvasList
[i
];
629 canvas
->ResetPrintCallback();
632 mCurrentCanvasList
.Clear();
633 mCurrentCanvasListSetup
= false;
636 nsresult
nsPageSequenceFrame::PrintNextSheet() {
637 // Note: When print al the pages or a page range the printed page shows the
638 // actual page number, when printing selection it prints the page number
639 // starting with the first page of the selection. For example if the user has
640 // a selection that starts on page 2 and ends on page 3, the page numbers when
641 // print are 1 and then two (which is different than printing a page range,
642 // where the page numbers would have been 2 and then 3)
644 PrintedSheetFrame
* currentSheetFrame
= GetCurrentSheetFrame();
645 if (!currentSheetFrame
) {
646 return NS_ERROR_FAILURE
;
651 nsDeviceContext
* dc
= PresContext()->DeviceContext();
653 if (PresContext()->IsRootPaginatedDocument()) {
654 if (!mCalledBeginPage
) {
655 // We must make sure BeginPage() has been called since some printing
656 // backends can't give us a valid rendering context for a [physical]
659 PR_PL(("***************** BeginPage *****************\n"));
660 rv
= dc
->BeginPage();
661 NS_ENSURE_SUCCESS(rv
, rv
);
665 PR_PL(("SeqFr::PrintNextSheet -> %p SheetIdx: %d", currentSheetFrame
,
668 // CreateRenderingContext can fail
669 RefPtr
<gfxContext
> gCtx
= dc
->CreateRenderingContext();
670 NS_ENSURE_TRUE(gCtx
, NS_ERROR_OUT_OF_MEMORY
);
672 nsRect
drawingRect(nsPoint(0, 0), currentSheetFrame
->GetSize());
673 nsRegion
drawingRegion(drawingRect
);
674 nsLayoutUtils::PaintFrame(gCtx
, currentSheetFrame
, drawingRegion
,
676 nsDisplayListBuilderMode::PaintForPrinting
,
677 nsLayoutUtils::PaintFrameFlags::SyncDecodeImages
);
681 nsresult
nsPageSequenceFrame::DoPageEnd() {
683 if (PresContext()->IsRootPaginatedDocument()) {
684 PR_PL(("***************** End Page (DoPageEnd) *****************\n"));
685 rv
= PresContext()->DeviceContext()->EndPage();
686 // Fall through to clean up resources/state below even if EndPage failed.
689 ResetPrintCanvasList();
690 mCalledBeginPage
= false;
697 static gfx::Matrix4x4
ComputePageSequenceTransform(const nsIFrame
* aFrame
,
698 float aAppUnitsPerPixel
) {
699 MOZ_ASSERT(aFrame
->IsPageSequenceFrame());
701 static_cast<const nsPageSequenceFrame
*>(aFrame
)->GetPrintPreviewScale();
702 return gfx::Matrix4x4::Scaling(scale
, scale
, 1);
705 nsIFrame::ComputeTransformFunction
nsPageSequenceFrame::GetTransformGetter()
707 return ComputePageSequenceTransform
;
710 void nsPageSequenceFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
711 const nsDisplayListSet
& aLists
) {
712 aBuilder
->SetDisablePartialUpdates(true);
713 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
715 nsDisplayList content
;
718 // Clear clip state while we construct the children of the
719 // nsDisplayTransform, since they'll be in a different coordinate system.
720 DisplayListClipState::AutoSaveRestore
clipState(aBuilder
);
723 nsIFrame
* child
= PrincipalChildList().FirstChild();
724 nsRect visible
= aBuilder
->GetVisibleRect();
725 visible
.ScaleInverseRoundOut(GetPrintPreviewScale());
728 if (child
->InkOverflowRectRelativeToParent().Intersects(visible
)) {
729 nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(
730 aBuilder
, child
, visible
- child
->GetPosition(),
731 visible
- child
->GetPosition());
732 child
->BuildDisplayListForStackingContext(aBuilder
, &content
);
733 aBuilder
->ResetMarkedFramesForDisplayList(this);
735 child
= child
->GetNextSibling();
739 content
.AppendNewToTop
<nsDisplayTransform
>(
740 aBuilder
, this, &content
, content
.GetBuildingRect(),
741 nsDisplayTransform::WithTransformGetter
);
743 aLists
.Content()->AppendToTop(&content
);
746 //------------------------------------------------------------------------------
747 void nsPageSequenceFrame::SetPageNumberFormat(const nsAString
& aFormatStr
,
748 bool aForPageNumOnly
) {
749 NS_ASSERTION(mPageData
!= nullptr, "mPageData string cannot be null!");
751 if (aForPageNumOnly
) {
752 mPageData
->mPageNumFormat
= aFormatStr
;
754 mPageData
->mPageNumAndTotalsFormat
= aFormatStr
;
758 //------------------------------------------------------------------------------
759 void nsPageSequenceFrame::SetDateTimeStr(const nsAString
& aDateTimeStr
) {
760 NS_ASSERTION(mPageData
!= nullptr, "mPageData string cannot be null!");
762 mPageData
->mDateTimeStr
= aDateTimeStr
;