1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsSimplePageSequenceFrame.h"
8
9 #include "DateTimeFormat.h"
10 #include "nsCOMPtr.h"
11 #include "nsDeviceContext.h"
12 #include "nsPresContext.h"
13 #include "gfxContext.h"
14 #include "nsGkAtoms.h"
15 #include "nsIPresShell.h"
16 #include "nsIPrintSettings.h"
17 #include "nsPageFrame.h"
18 #include "nsSubDocumentFrame.h"
19 #include "nsRegion.h"
20 #include "nsCSSFrameConstructor.h"
21 #include "nsContentUtils.h"
22 #include "nsDisplayList.h"
23 #include "nsHTMLCanvasFrame.h"
24 #include "mozilla/dom/HTMLCanvasElement.h"
25 #include "nsICanvasRenderingContextInternal.h"
26 #include "nsServiceManagerUtils.h"
27 #include <algorithm>
28
29 #define OFFSET_NOT_SET -1
30
31 using namespace mozilla;
32 using namespace mozilla::dom;
33
34 #include "mozilla/Logging.h"
35 mozilla::LazyLogModule gLayoutPrintingLog("printing-layout");
36
37 #define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1)
38
NS_NewSimplePageSequenceFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)39 nsSimplePageSequenceFrame* NS_NewSimplePageSequenceFrame(
40 nsIPresShell* aPresShell, nsStyleContext* aContext) {
41 return new (aPresShell) nsSimplePageSequenceFrame(aContext);
42 }
43
NS_IMPL_FRAMEARENA_HELPERS(nsSimplePageSequenceFrame)44 NS_IMPL_FRAMEARENA_HELPERS(nsSimplePageSequenceFrame)
45
46 nsSimplePageSequenceFrame::nsSimplePageSequenceFrame(nsStyleContext* aContext)
47 : nsContainerFrame(aContext, kClassID),
48 mTotalPages(-1),
49 mCalledBeginPage(false),
50 mCurrentCanvasListSetup(false) {
51 nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5));
52 mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch);
53
54 // XXX Unsafe to assume successful allocation
55 mPageData = new nsSharedPageData();
56 mPageData->mHeadFootFont = *PresContext()->GetDefaultFont(
57 kGenericFont_serif, aContext->StyleFont()->mLanguage);
58 mPageData->mHeadFootFont.size = nsPresContext::CSSPointsToAppUnits(10);
59
60 // Doing this here so we only have to go get these formats once
61 SetPageNumberFormat("pagenumber", "%1$d", true);
62 SetPageNumberFormat("pageofpages", "%1$d of %2$d", false);
63 }
64
~nsSimplePageSequenceFrame()65 nsSimplePageSequenceFrame::~nsSimplePageSequenceFrame() {
66 delete mPageData;
67 ResetPrintCanvasList();
68 }
69
70 NS_QUERYFRAME_HEAD(nsSimplePageSequenceFrame)
NS_QUERYFRAME_ENTRY(nsIPageSequenceFrame)71 NS_QUERYFRAME_ENTRY(nsIPageSequenceFrame)
72 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
73
74 //----------------------------------------------------------------------
75
76 void nsSimplePageSequenceFrame::SetDesiredSize(ReflowOutput& aDesiredSize,
77 const ReflowInput& aReflowInput,
78 nscoord aWidth,
79 nscoord aHeight) {
80 // Aim to fill the whole size of the document, not only so we
81 // can act as a background in print preview but also handle overflow
82 // in child page frames correctly.
83 // Use availableISize so we don't cause a needless horizontal scrollbar.
84 WritingMode wm = aReflowInput.GetWritingMode();
85 nscoord scaledWidth = aWidth * PresContext()->GetPrintPreviewScale();
86 nscoord scaledHeight = aHeight * PresContext()->GetPrintPreviewScale();
87
88 nscoord scaledISize = (wm.IsVertical() ? scaledHeight : scaledWidth);
89 nscoord scaledBSize = (wm.IsVertical() ? scaledWidth : scaledHeight);
90
91 aDesiredSize.ISize(wm) = std::max(scaledISize, aReflowInput.AvailableISize());
92 aDesiredSize.BSize(wm) = std::max(scaledBSize, aReflowInput.ComputedBSize());
93 }
94
95 // Helper function to compute the offset needed to center a child
96 // page-frame's margin-box inside our content-box.
ComputeCenteringMargin(nscoord aContainerContentBoxWidth,nscoord aChildPaddingBoxWidth,const nsMargin & aChildPhysicalMargin)97 nscoord nsSimplePageSequenceFrame::ComputeCenteringMargin(
98 nscoord aContainerContentBoxWidth, nscoord aChildPaddingBoxWidth,
99 const nsMargin& aChildPhysicalMargin) {
100 // We'll be centering our child's margin-box, so get the size of that:
101 nscoord childMarginBoxWidth =
102 aChildPaddingBoxWidth + aChildPhysicalMargin.LeftRight();
103
104 // When rendered, our child's rect will actually be scaled up by the
105 // print-preview scale factor, via ComputePageSequenceTransform().
106 // We really want to center *that scaled-up rendering* inside of
107 // aContainerContentBoxWidth. So, we scale up its margin-box here...
108 auto ppScale = PresContext()->GetPrintPreviewScale();
109 nscoord scaledChildMarginBoxWidth =
110 NSToCoordRound(childMarginBoxWidth * ppScale);
111
112 // ...and see we how much space is left over, when we subtract that scaled-up
113 // size from the container width:
114 nscoord scaledExtraSpace =
115 aContainerContentBoxWidth - scaledChildMarginBoxWidth;
116
117 if (scaledExtraSpace <= 0) {
118 // (Don't bother centering if there's zero/negative space.)
119 return 0;
120 }
121
122 // To center the child, we want to give it an additional left-margin of half
123 // of the extra space. And then, we have to scale that space back down, so
124 // that it'll produce the correct scaled-up amount when we render (because
125 // rendering will scale it back up):
126 return NSToCoordRound(scaledExtraSpace * 0.5 / ppScale);
127 }
128
129 /*
130 * Note: we largely position/size out our children (page frames) using
131 * \*physical\* x/y/width/height values, because the print preview UI is always
132 * arranged in the same orientation, regardless of writing mode.
133 */
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)134 void nsSimplePageSequenceFrame::Reflow(nsPresContext* aPresContext,
135 ReflowOutput& aDesiredSize,
136 const ReflowInput& aReflowInput,
137 nsReflowStatus& aStatus) {
138 MarkInReflow();
139 NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(),
140 "A Page Sequence is only for real pages");
141 DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame");
142 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
143 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
144 NS_FRAME_TRACE_REFLOW_IN("nsSimplePageSequenceFrame::Reflow");
145
146 // Don't do incremental reflow until we've taught tables how to do
147 // it right in paginated mode.
148 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
149 // Return our desired size
150 SetDesiredSize(aDesiredSize, aReflowInput, mSize.width, mSize.height);
151 aDesiredSize.SetOverflowAreasToDesiredBounds();
152 FinishAndStoreOverflow(&aDesiredSize);
153
154 if (GetRect().Width() != aDesiredSize.Width()) {
155 // Our width is changing; we need to re-center our children (our pages).
156 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
157 nsIFrame* child = e.get();
158 nsMargin pageCSSMargin = child->GetUsedMargin();
159 nscoord centeringMargin =
160 ComputeCenteringMargin(aReflowInput.ComputedWidth(),
161 child->GetRect().Width(), pageCSSMargin);
162 nscoord newX = pageCSSMargin.left + centeringMargin;
163
164 // Adjust the child's x-position:
165 child->MovePositionBy(nsPoint(newX - child->GetNormalPosition().x, 0));
166 }
167 }
168 return;
169 }
170
171 // See if we can get a Print Settings from the Context
172 if (!mPageData->mPrintSettings &&
173 aPresContext->Medium() == nsGkAtoms::print) {
174 mPageData->mPrintSettings = aPresContext->GetPrintSettings();
175 }
176
177 // now get out margins & edges
178 if (mPageData->mPrintSettings) {
179 nsIntMargin unwriteableTwips;
180 mPageData->mPrintSettings->GetUnwriteableMarginInTwips(unwriteableTwips);
181 NS_ASSERTION(unwriteableTwips.left >= 0 && unwriteableTwips.top >= 0 &&
182 unwriteableTwips.right >= 0 &&
183 unwriteableTwips.bottom >= 0,
184 "Unwriteable twips should be non-negative");
185
186 nsIntMargin marginTwips;
187 mPageData->mPrintSettings->GetMarginInTwips(marginTwips);
188 mMargin = aPresContext->CSSTwipsToAppUnits(marginTwips + unwriteableTwips);
189
190 int16_t printType;
191 mPageData->mPrintSettings->GetPrintRange(&printType);
192 mPrintRangeType = printType;
193
194 nsIntMargin edgeTwips;
195 mPageData->mPrintSettings->GetEdgeInTwips(edgeTwips);
196
197 // sanity check the values. three inches are sometimes needed
198 int32_t inchInTwips = NS_INCHES_TO_INT_TWIPS(3.0);
199 edgeTwips.top = clamped(edgeTwips.top, 0, inchInTwips);
200 edgeTwips.bottom = clamped(edgeTwips.bottom, 0, inchInTwips);
201 edgeTwips.left = clamped(edgeTwips.left, 0, inchInTwips);
202 edgeTwips.right = clamped(edgeTwips.right, 0, inchInTwips);
203
204 mPageData->mEdgePaperMargin =
205 aPresContext->CSSTwipsToAppUnits(edgeTwips + unwriteableTwips);
206 }
207
208 // *** Special Override ***
209 // If this is a sub-sdoc (meaning it doesn't take the whole page)
210 // and if this Document is in the upper left hand corner
211 // we need to suppress the top margin or it will reflow too small
212
213 nsSize pageSize = aPresContext->GetPageSize();
214
215 mPageData->mReflowSize = pageSize;
216 mPageData->mReflowMargin = mMargin;
217
218 // We use the CSS "margin" property on the -moz-page pseudoelement
219 // to determine the space between each page in print preview.
220 // Keep a running y-offset for each page.
221 nscoord y = 0;
222 nscoord maxXMost = 0;
223
224 // Tile the pages vertically
225 ReflowOutput kidSize(aReflowInput);
226 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
227 nsIFrame* kidFrame = e.get();
228 // Set the shared data into the page frame before reflow
229 nsPageFrame* pf = static_cast<nsPageFrame*>(kidFrame);
230 pf->SetSharedPageData(mPageData);
231
232 // Reflow the page
233 ReflowInput kidReflowInput(
234 aPresContext, aReflowInput, kidFrame,
235 LogicalSize(kidFrame->GetWritingMode(), pageSize));
236 nsReflowStatus status;
237
238 kidReflowInput.SetComputedISize(kidReflowInput.AvailableISize());
239 // kidReflowInput.SetComputedHeight(kidReflowInput.AvailableHeight());
240 PR_PL(("AV ISize: %d BSize: %d\n", kidReflowInput.AvailableISize(),
241 kidReflowInput.AvailableBSize()));
242
243 nsMargin pageCSSMargin = kidReflowInput.ComputedPhysicalMargin();
244 y += pageCSSMargin.top;
245
246 nscoord x = pageCSSMargin.left;
247
248 // Place and size the page.
249 ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, x, y, 0,
250 status);
251
252 // If the page is narrower than our width, then center it horizontally:
253 x += ComputeCenteringMargin(aReflowInput.ComputedWidth(), kidSize.Width(),
254 pageCSSMargin);
255
256 FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, x, y, 0);
257 y += kidSize.Height();
258 y += pageCSSMargin.bottom;
259
260 maxXMost = std::max(maxXMost, x + kidSize.Width() + pageCSSMargin.right);
261
262 // Is the page complete?
263 nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
264
265 if (status.IsFullyComplete()) {
266 NS_ASSERTION(!kidNextInFlow, "bad child flow list");
267 } else if (!kidNextInFlow) {
268 // The page isn't complete and it doesn't have a next-in-flow, so
269 // create a continuing page.
270 nsIFrame* continuingPage =
271 aPresContext->PresShell()->FrameConstructor()->CreateContinuingFrame(
272 aPresContext, kidFrame, this);
273
274 // Add it to our child list
275 mFrames.InsertFrame(nullptr, kidFrame, continuingPage);
276 }
277 }
278
279 // Get Total Page Count
280 // XXXdholbert technically we could calculate this in the loop above,
281 // instead of needing a separate walk.
282 int32_t pageTot = mFrames.GetLength();
283
284 // Set Page Number Info
285 int32_t pageNum = 1;
286 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
287 MOZ_ASSERT(e.get()->IsPageFrame(),
288 "only expecting nsPageFrame children. Other children will make "
289 "this static_cast bogus & probably violate other assumptions");
290 nsPageFrame* pf = static_cast<nsPageFrame*>(e.get());
291 pf->SetPageNumInfo(pageNum, pageTot);
292 pageNum++;
293 }
294
295 nsAutoString formattedDateString;
296 PRTime now = PR_Now();
297 if (NS_SUCCEEDED(DateTimeFormat::FormatPRTime(
298 kDateFormatShort, kTimeFormatNoSeconds, now, formattedDateString))) {
299 SetDateTimeStr(formattedDateString);
300 }
301
302 // Return our desired size
303 // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the
304 // correct size
305 SetDesiredSize(aDesiredSize, aReflowInput, maxXMost, y);
306
307 aDesiredSize.SetOverflowAreasToDesiredBounds();
308 FinishAndStoreOverflow(&aDesiredSize);
309
310 // cache the size so we can set the desired size
311 // for the other reflows that happen
312 mSize.width = maxXMost;
313 mSize.height = y;
314
315 NS_FRAME_TRACE_REFLOW_OUT("nsSimplePageSequeceFrame::Reflow", aStatus);
316 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
317 }
318
319 //----------------------------------------------------------------------
320
321 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const322 nsresult nsSimplePageSequenceFrame::GetFrameName(nsAString& aResult) const {
323 return MakeFrameName(NS_LITERAL_STRING("SimplePageSequence"), aResult);
324 }
325 #endif
326
327 //====================================================================
328 //== Asynch Printing
329 //====================================================================
330 NS_IMETHODIMP
GetCurrentPageNum(int32_t * aPageNum)331 nsSimplePageSequenceFrame::GetCurrentPageNum(int32_t* aPageNum) {
332 NS_ENSURE_ARG_POINTER(aPageNum);
333
334 *aPageNum = mPageNum;
335 return NS_OK;
336 }
337
338 NS_IMETHODIMP
GetNumPages(int32_t * aNumPages)339 nsSimplePageSequenceFrame::GetNumPages(int32_t* aNumPages) {
340 NS_ENSURE_ARG_POINTER(aNumPages);
341
342 *aNumPages = mTotalPages;
343 return NS_OK;
344 }
345
346 NS_IMETHODIMP
IsDoingPrintRange(bool * aDoing)347 nsSimplePageSequenceFrame::IsDoingPrintRange(bool* aDoing) {
348 NS_ENSURE_ARG_POINTER(aDoing);
349
350 *aDoing = mDoingPageRange;
351 return NS_OK;
352 }
353
354 NS_IMETHODIMP
GetPrintRange(int32_t * aFromPage,int32_t * aToPage)355 nsSimplePageSequenceFrame::GetPrintRange(int32_t* aFromPage, int32_t* aToPage) {
356 NS_ENSURE_ARG_POINTER(aFromPage);
357 NS_ENSURE_ARG_POINTER(aToPage);
358
359 *aFromPage = mFromPageNum;
360 *aToPage = mToPageNum;
361 return NS_OK;
362 }
363
364 // Helper Function
SetPageNumberFormat(const char * aPropName,const char * aDefPropVal,bool aPageNumOnly)365 void nsSimplePageSequenceFrame::SetPageNumberFormat(const char* aPropName,
366 const char* aDefPropVal,
367 bool aPageNumOnly) {
368 // Doing this here so we only have to go get these formats once
369 nsAutoString pageNumberFormat;
370 // Now go get the Localized Page Formating String
371 nsresult rv = nsContentUtils::GetLocalizedString(
372 nsContentUtils::ePRINTING_PROPERTIES, aPropName, pageNumberFormat);
373 if (NS_FAILED(rv)) { // back stop formatting
374 pageNumberFormat.AssignASCII(aDefPropVal);
375 }
376
377 SetPageNumberFormat(pageNumberFormat, aPageNumOnly);
378 }
379
380 NS_IMETHODIMP
StartPrint(nsPresContext * aPresContext,nsIPrintSettings * aPrintSettings,const nsAString & aDocTitle,const nsAString & aDocURL)381 nsSimplePageSequenceFrame::StartPrint(nsPresContext* aPresContext,
382 nsIPrintSettings* aPrintSettings,
383 const nsAString& aDocTitle,
384 const nsAString& aDocURL) {
385 NS_ENSURE_ARG_POINTER(aPresContext);
386 NS_ENSURE_ARG_POINTER(aPrintSettings);
387
388 if (!mPageData->mPrintSettings) {
389 mPageData->mPrintSettings = aPrintSettings;
390 }
391
392 if (!aDocTitle.IsEmpty()) {
393 mPageData->mDocTitle = aDocTitle;
394 }
395 if (!aDocURL.IsEmpty()) {
396 mPageData->mDocURL = aDocURL;
397 }
398
399 aPrintSettings->GetStartPageRange(&mFromPageNum);
400 aPrintSettings->GetEndPageRange(&mToPageNum);
401 aPrintSettings->GetPageRanges(mPageRanges);
402
403 mDoingPageRange =
404 nsIPrintSettings::kRangeSpecifiedPageRange == mPrintRangeType;
405
406 // If printing a range of pages make sure at least the starting page
407 // number is valid
408 mTotalPages = mFrames.GetLength();
409
410 if (mDoingPageRange) {
411 if (mFromPageNum > mTotalPages) {
412 return NS_ERROR_INVALID_ARG;
413 }
414 }
415
416 // Begin printing of the document
417 nsresult rv = NS_OK;
418
419 mPageNum = 1;
420
421 return rv;
422 }
423
GetPrintCanvasElementsInFrame(nsIFrame * aFrame,nsTArray<RefPtr<HTMLCanvasElement>> * aArr)424 static void GetPrintCanvasElementsInFrame(
425 nsIFrame* aFrame, nsTArray<RefPtr<HTMLCanvasElement> >* aArr) {
426 if (!aFrame) {
427 return;
428 }
429 for (nsIFrame::ChildListIterator childLists(aFrame); !childLists.IsDone();
430 childLists.Next()) {
431 nsFrameList children = childLists.CurrentList();
432 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
433 nsIFrame* child = e.get();
434
435 // Check if child is a nsHTMLCanvasFrame.
436 nsHTMLCanvasFrame* canvasFrame = do_QueryFrame(child);
437
438 // If there is a canvasFrame, try to get actual canvas element.
439 if (canvasFrame) {
440 HTMLCanvasElement* canvas =
441 HTMLCanvasElement::FromContentOrNull(canvasFrame->GetContent());
442 if (canvas && canvas->GetMozPrintCallback()) {
443 aArr->AppendElement(canvas);
444 continue;
445 }
446 }
447
448 if (!child->PrincipalChildList().FirstChild()) {
449 nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(child);
450 if (subdocumentFrame) {
451 // Descend into the subdocument
452 nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
453 child = root;
454 }
455 }
456 // The current child is not a nsHTMLCanvasFrame OR it is but there is
457 // no HTMLCanvasElement on it. Check if children of `child` might
458 // contain a HTMLCanvasElement.
459 GetPrintCanvasElementsInFrame(child, aArr);
460 }
461 }
462 }
463
DetermineWhetherToPrintPage()464 void nsSimplePageSequenceFrame::DetermineWhetherToPrintPage() {
465 // See whether we should print this page
466 mPrintThisPage = true;
467 bool printEvenPages, printOddPages;
468 mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintEvenPages,
469 &printEvenPages);
470 mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintOddPages,
471 &printOddPages);
472
473 // If printing a range of pages check whether the page number is in the
474 // range of pages to print
475 if (mDoingPageRange) {
476 if (mPageNum < mFromPageNum) {
477 mPrintThisPage = false;
478 } else if (mPageNum > mToPageNum) {
479 mPageNum++;
480 mPrintThisPage = false;
481 return;
482 } else {
483 int32_t length = mPageRanges.Length();
484
485 // Page ranges are pairs (start, end)
486 if (length && (length % 2 == 0)) {
487 mPrintThisPage = false;
488
489 int32_t i;
490 for (i = 0; i < length; i += 2) {
491 if (mPageRanges[i] <= mPageNum && mPageNum <= mPageRanges[i + 1]) {
492 mPrintThisPage = true;
493 break;
494 }
495 }
496 }
497 }
498 }
499
500 // Check for printing of odd and even pages
501 if (mPageNum & 0x1) {
502 if (!printOddPages) {
503 mPrintThisPage = false; // don't print odd numbered page
504 }
505 } else {
506 if (!printEvenPages) {
507 mPrintThisPage = false; // don't print even numbered page
508 }
509 }
510 }
511
GetCurrentPageFrame()512 nsIFrame* nsSimplePageSequenceFrame::GetCurrentPageFrame() {
513 int32_t i = 1;
514 for (nsFrameList::Enumerator childFrames(mFrames); !childFrames.AtEnd();
515 childFrames.Next()) {
516 if (i == mPageNum) {
517 return childFrames.get();
518 }
519 ++i;
520 }
521 return nullptr;
522 }
523
524 NS_IMETHODIMP
PrePrintNextPage(nsITimerCallback * aCallback,bool * aDone)525 nsSimplePageSequenceFrame::PrePrintNextPage(nsITimerCallback* aCallback,
526 bool* aDone) {
527 nsIFrame* currentPage = GetCurrentPageFrame();
528 if (!currentPage) {
529 *aDone = true;
530 return NS_ERROR_FAILURE;
531 }
532
533 DetermineWhetherToPrintPage();
534 // Nothing to do if the current page doesn't get printed OR rendering to
535 // preview. For preview, the `CallPrintCallback` is called from within the
536 // HTMLCanvasElement::HandlePrintCallback.
537 if (!mPrintThisPage || !PresContext()->IsRootPaginatedDocument()) {
538 *aDone = true;
539 return NS_OK;
540 }
541
542 // If the canvasList is null, then generate it and start the render
543 // process for all the canvas.
544 if (!mCurrentCanvasListSetup) {
545 mCurrentCanvasListSetup = true;
546 GetPrintCanvasElementsInFrame(currentPage, &mCurrentCanvasList);
547
548 if (mCurrentCanvasList.Length() != 0) {
549 nsresult rv = NS_OK;
550
551 // Begin printing of the document
552 nsDeviceContext* dc = PresContext()->DeviceContext();
553 PR_PL(("\n"));
554 PR_PL(("***************** BeginPage *****************\n"));
555 rv = dc->BeginPage();
556 NS_ENSURE_SUCCESS(rv, rv);
557
558 mCalledBeginPage = true;
559
560 RefPtr<gfxContext> renderingContext = dc->CreateRenderingContext();
561 NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY);
562
563 DrawTarget* drawTarget = renderingContext->GetDrawTarget();
564 if (NS_WARN_IF(!drawTarget)) {
565 return NS_ERROR_FAILURE;
566 }
567
568 for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0; i--) {
569 HTMLCanvasElement* canvas = mCurrentCanvasList[i];
570 nsIntSize size = canvas->GetSize();
571
572 RefPtr<DrawTarget> canvasTarget =
573 drawTarget->CreateSimilarDrawTarget(size, drawTarget->GetFormat());
574 if (!canvasTarget) {
575 continue;
576 }
577
578 nsICanvasRenderingContextInternal* ctx = canvas->GetContextAtIndex(0);
579 if (!ctx) {
580 continue;
581 }
582
583 // Initialize the context with the new DrawTarget.
584 ctx->InitializeWithDrawTarget(nullptr, WrapNotNull(canvasTarget));
585
586 // Start the rendering process.
587 AutoWeakFrame weakFrame = this;
588 canvas->DispatchPrintCallback(aCallback);
589 NS_ENSURE_STATE(weakFrame.IsAlive());
590 }
591 }
592 }
593 uint32_t doneCounter = 0;
594 for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0; i--) {
595 HTMLCanvasElement* canvas = mCurrentCanvasList[i];
596
597 if (canvas->IsPrintCallbackDone()) {
598 doneCounter++;
599 }
600 }
601 // If all canvas have finished rendering, return true, otherwise false.
602 *aDone = doneCounter == mCurrentCanvasList.Length();
603
604 return NS_OK;
605 }
606
607 NS_IMETHODIMP
ResetPrintCanvasList()608 nsSimplePageSequenceFrame::ResetPrintCanvasList() {
609 for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0; i--) {
610 HTMLCanvasElement* canvas = mCurrentCanvasList[i];
611 canvas->ResetPrintCallback();
612 }
613
614 mCurrentCanvasList.Clear();
615 mCurrentCanvasListSetup = false;
616 return NS_OK;
617 }
618
619 NS_IMETHODIMP
PrintNextPage()620 nsSimplePageSequenceFrame::PrintNextPage() {
621 // Note: When print al the pages or a page range the printed page shows the
622 // actual page number, when printing selection it prints the page number
623 // starting with the first page of the selection. For example if the user has
624 // a selection that starts on page 2 and ends on page 3, the page numbers when
625 // print are 1 and then two (which is different than printing a page range,
626 // where the page numbers would have been 2 and then 3)
627
628 nsIFrame* currentPageFrame = GetCurrentPageFrame();
629 if (!currentPageFrame) {
630 return NS_ERROR_FAILURE;
631 }
632
633 nsresult rv = NS_OK;
634
635 DetermineWhetherToPrintPage();
636
637 if (mPrintThisPage) {
638 nsDeviceContext* dc = PresContext()->DeviceContext();
639
640 if (PresContext()->IsRootPaginatedDocument()) {
641 if (!mCalledBeginPage) {
642 // We must make sure BeginPage() has been called since some printing
643 // backends can't give us a valid rendering context for a [physical]
644 // page otherwise.
645 PR_PL(("\n"));
646 PR_PL(("***************** BeginPage *****************\n"));
647 rv = dc->BeginPage();
648 NS_ENSURE_SUCCESS(rv, rv);
649 }
650 }
651
652 PR_PL(
653 ("SeqFr::PrintNextPage -> %p PageNo: %d", currentPageFrame, mPageNum));
654
655 // CreateRenderingContext can fail
656 RefPtr<gfxContext> gCtx = dc->CreateRenderingContext();
657 NS_ENSURE_TRUE(gCtx, NS_ERROR_OUT_OF_MEMORY);
658
659 nsRect drawingRect(nsPoint(0, 0), currentPageFrame->GetSize());
660 nsRegion drawingRegion(drawingRect);
661 nsLayoutUtils::PaintFrame(
662 gCtx, currentPageFrame, drawingRegion, NS_RGBA(0, 0, 0, 0),
663 nsDisplayListBuilderMode::PAINTING,
664 nsLayoutUtils::PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES);
665 }
666 return rv;
667 }
668
669 NS_IMETHODIMP
DoPageEnd()670 nsSimplePageSequenceFrame::DoPageEnd() {
671 nsresult rv = NS_OK;
672 if (PresContext()->IsRootPaginatedDocument() && mPrintThisPage) {
673 PR_PL(("***************** End Page (DoPageEnd) *****************\n"));
674 rv = PresContext()->DeviceContext()->EndPage();
675 NS_ENSURE_SUCCESS(rv, rv);
676 }
677
678 ResetPrintCanvasList();
679 mCalledBeginPage = false;
680
681 mPageNum++;
682
683 return rv;
684 }
685
ComputePageSequenceTransform(nsIFrame * aFrame,float aAppUnitsPerPixel)686 inline gfx::Matrix4x4 ComputePageSequenceTransform(nsIFrame* aFrame,
687 float aAppUnitsPerPixel) {
688 float scale = aFrame->PresContext()->GetPrintPreviewScale();
689 return gfx::Matrix4x4::Scaling(scale, scale, 1);
690 }
691
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)692 void nsSimplePageSequenceFrame::BuildDisplayList(
693 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
694 DisplayBorderBackgroundOutline(aBuilder, aLists);
695
696 nsDisplayList content;
697
698 {
699 // Clear clip state while we construct the children of the
700 // nsDisplayTransform, since they'll be in a different coordinate system.
701 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
702 clipState.Clear();
703
704 nsIFrame* child = PrincipalChildList().FirstChild();
705 nsRect visible = aBuilder->GetVisibleRect();
706 visible.ScaleInverseRoundOut(PresContext()->GetPrintPreviewScale());
707
708 while (child) {
709 if (child->GetVisualOverflowRectRelativeToParent().Intersects(visible)) {
710 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
711 aBuilder, child, visible - child->GetPosition(),
712 visible - child->GetPosition(),
713 aBuilder->IsAtRootOfPseudoStackingContext());
714 child->BuildDisplayListForStackingContext(aBuilder, &content);
715 aBuilder->ResetMarkedFramesForDisplayList(this);
716 }
717 child = child->GetNextSibling();
718 }
719 }
720
721 content.AppendToTop(MakeDisplayItem<nsDisplayTransform>(
722 aBuilder, this, &content, content.GetVisibleRect(),
723 ::ComputePageSequenceTransform));
724
725 aLists.Content()->AppendToTop(&content);
726 }
727
728 //------------------------------------------------------------------------------
SetPageNumberFormat(const nsAString & aFormatStr,bool aForPageNumOnly)729 void nsSimplePageSequenceFrame::SetPageNumberFormat(const nsAString& aFormatStr,
730 bool aForPageNumOnly) {
731 NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!");
732
733 if (aForPageNumOnly) {
734 mPageData->mPageNumFormat = aFormatStr;
735 } else {
736 mPageData->mPageNumAndTotalsFormat = aFormatStr;
737 }
738 }
739
740 //------------------------------------------------------------------------------
SetDateTimeStr(const nsAString & aDateTimeStr)741 void nsSimplePageSequenceFrame::SetDateTimeStr(const nsAString& aDateTimeStr) {
742 NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!");
743
744 mPageData->mDateTimeStr = aDateTimeStr;
745 }
746
747 //------------------------------------------------------------------------------
748 // For Shrink To Fit
749 //
750 // Return the percentage that the page needs to shrink to
751 //
752 NS_IMETHODIMP
GetSTFPercent(float & aSTFPercent)753 nsSimplePageSequenceFrame::GetSTFPercent(float& aSTFPercent) {
754 NS_ENSURE_TRUE(mPageData, NS_ERROR_UNEXPECTED);
755 aSTFPercent = mPageData->mShrinkToFitRatio;
756 return NS_OK;
757 }
758
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)759 void nsSimplePageSequenceFrame::AppendDirectlyOwnedAnonBoxes(
760 nsTArray<OwnedAnonBox>& aResult) {
761 if (mFrames.NotEmpty()) {
762 aResult.AppendElement(mFrames.FirstChild());
763 }
764 }
765