Coverage Report

Created: 2018-09-25 14:53

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