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