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