Bug 1414834 - Part 1 - made print selection go through the print timer; r?bobowen draft
authorAlex Gaynor <agaynor@mozilla.com>
Wed, 01 Nov 2017 11:55:38 -0400
changeset 696365 098bd9f2a324c126f110e7d5cf3db1ceebe89d6c
parent 696354 0e18448c495cb5b3da689a554e8137dbd8eddcc0
child 696366 6b36ad32f90fd2ceccf69889a22a24993d66a059
push id88694
push userbmo:agaynor@mozilla.com
push dateFri, 10 Nov 2017 15:08:06 +0000
reviewersbobowen
bugs1414834
milestone58.0a1
Bug 1414834 - Part 1 - made print selection go through the print timer; r?bobowen When printing a selection, we treat all pages in a selection as a single 'page' internally. Before this patch we handled this in a single iteration, thereby bypassing the control flow normally applied to the IPC. With this patch, we don't attempt to print additional pages until IPC related to the former page has completed. MozReview-Commit-ID: 1Oi2ZFMLP8D
layout/generic/nsIPageSequenceFrame.h
layout/generic/nsSimplePageSequenceFrame.cpp
layout/generic/nsSimplePageSequenceFrame.h
layout/printing/nsPrintEngine.cpp
--- a/layout/generic/nsIPageSequenceFrame.h
+++ b/layout/generic/nsIPageSequenceFrame.h
@@ -51,13 +51,13 @@ public:
 
   NS_IMETHOD DoPageEnd() = 0;
   NS_IMETHOD SetSelectionHeight(nscoord aYOffset, nscoord aHeight) = 0;
 
   NS_IMETHOD SetTotalNumPages(int32_t aTotal) = 0;
 
   // For Shrink To Fit
   NS_IMETHOD GetSTFPercent(float& aSTFPercent) = 0;
+
+  NS_IMETHOD IsInSelection(bool* aInSelection) = 0;
 };
 
 #endif /* nsIPageSequenceFrame_h___ */
-
-
--- a/layout/generic/nsSimplePageSequenceFrame.cpp
+++ b/layout/generic/nsSimplePageSequenceFrame.cpp
@@ -382,16 +382,23 @@ nsSimplePageSequenceFrame::GetPrintRange
   NS_ENSURE_ARG_POINTER(aFromPage);
   NS_ENSURE_ARG_POINTER(aToPage);
 
   *aFromPage = mFromPageNum;
   *aToPage   = mToPageNum;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::IsInSelection(bool* aInSelection)
+{
+  *aInSelection = mCurrentSelection.isSome();
+  return NS_OK;
+}
+
 // Helper Function
 void
 nsSimplePageSequenceFrame::SetPageNumberFormat(const char* aPropName, const char* aDefPropVal, bool aPageNumOnly)
 {
   // Doing this here so we only have to go get these formats once
   nsAutoString pageNumberFormat;
   // Now go get the Localized Page Formating String
   nsresult rv =
@@ -710,114 +717,127 @@ nsSimplePageSequenceFrame::PrintNextPage
 
   nsIFrame* currentPageFrame = GetCurrentPageFrame();
   if (!currentPageFrame) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = NS_OK;
 
-  DetermineWhetherToPrintPage();
+  if (!mCurrentSelection) {
+    DetermineWhetherToPrintPage();
+  }
 
   if (mPrintThisPage) {
     nsDeviceContext* dc = PresContext()->DeviceContext();
 
-    nsPageFrame * pf = static_cast<nsPageFrame*>(currentPageFrame);
-    pf->SetPageNumInfo(mPageNum, mTotalPages);
-    pf->SetSharedPageData(mPageData);
+    nsPageFrame* pf = static_cast<nsPageFrame*>(currentPageFrame);
+    if (!mCurrentSelection) {
+      pf->SetPageNumInfo(mPageNum, mTotalPages);
+      pf->SetSharedPageData(mPageData);
+    }
 
     // Only used if we're printing a selection:
     nsIFrame* selectionContentFrame = nullptr;
     nscoord pageContentHeight =
       PresContext()->GetPageSize().height - (mMargin.top + mMargin.bottom);
+
     nscoord selectionY = pageContentHeight;
     int32_t selectionCurrentPageNum = 1;
+    if (mCurrentSelection) {
+      selectionY = mCurrentSelection->mSelectionY;
+      selectionCurrentPageNum = mCurrentSelection->mCurrentPageNum;
+    }
     bool haveUnfinishedSelectionToPrint = false;
 
     if (mSelectionHeight >= 0) {
       selectionContentFrame = currentPageFrame->PrincipalChildList().FirstChild();
       MOZ_ASSERT(selectionContentFrame->IsPageContentFrame() &&
                  !selectionContentFrame->GetNextSibling(),
                  "Unexpected frame tree");
       // To print a selection we reposition the page content frame for each
       // page.  We can do this (and not have to bother resetting the position
       // after we're done) because we are printing from a static clone document
       // that is thrown away after we finish printing.
       selectionContentFrame->SetPosition(selectionContentFrame->GetPosition() +
                                          nsPoint(0, -mYSelOffset));
       nsContainerFrame::PositionChildViews(selectionContentFrame);
     }
 
-    do {
-      if (PresContext()->IsRootPaginatedDocument()) {
-        if (!mCalledBeginPage) {
-          // We must make sure BeginPage() has been called since some printing
-          // backends can't give us a valid rendering context for a [physical]
-          // page otherwise.
-          PR_PL(("\n"));
-          PR_PL(("***************** BeginPage *****************\n"));
-          rv = dc->BeginPage();
-          NS_ENSURE_SUCCESS(rv, rv);
-        }
-
-        // Reset this flag. We reset it early here because if we loop around to
-        // print another page's worth of selection we need to call BeginPage
-        // again:
-        mCalledBeginPage = false;
+    if (PresContext()->IsRootPaginatedDocument()) {
+      if (!mCalledBeginPage) {
+        // We must make sure BeginPage() has been called since some printing
+        // backends can't give us a valid rendering context for a [physical]
+        // page otherwise.
+        PR_PL(("\n"));
+        PR_PL(("***************** BeginPage *****************\n"));
+        rv = dc->BeginPage();
+        NS_ENSURE_SUCCESS(rv, rv);
       }
 
-      PR_PL(("SeqFr::PrintNextPage -> %p PageNo: %d", pf, mPageNum));
-
-      // CreateRenderingContext can fail
-      RefPtr<gfxContext> gCtx = dc->CreateRenderingContext();
-      NS_ENSURE_TRUE(gCtx, NS_ERROR_OUT_OF_MEMORY);
+      // Reset this flag. We reset it early here because if we loop around to
+      // print another page's worth of selection we need to call BeginPage
+      // again:
+      mCalledBeginPage = false;
+    }
 
-      nsRect drawingRect(nsPoint(0, 0), currentPageFrame->GetSize());
-      nsRegion drawingRegion(drawingRect);
-      nsLayoutUtils::PaintFrame(gCtx, currentPageFrame,
-                                drawingRegion, NS_RGBA(0,0,0,0),
-                                nsDisplayListBuilderMode::PAINTING,
-                                nsLayoutUtils::PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES);
+    PR_PL(("SeqFr::PrintNextPage -> %p PageNo: %d", pf, mPageNum));
+
+    // CreateRenderingContext can fail
+    RefPtr<gfxContext> gCtx = dc->CreateRenderingContext();
+    NS_ENSURE_TRUE(gCtx, NS_ERROR_OUT_OF_MEMORY);
 
-      if (mSelectionHeight >= 0) {
-        haveUnfinishedSelectionToPrint = (selectionY < mSelectionHeight);
-        if (haveUnfinishedSelectionToPrint) {
-          selectionY += pageContentHeight;
-          selectionCurrentPageNum++;
-          pf->SetPageNumInfo(selectionCurrentPageNum, mTotalPages);
-          selectionContentFrame->SetPosition(selectionContentFrame->GetPosition() +
-                                             nsPoint(0, -pageContentHeight));
-          nsContainerFrame::PositionChildViews(selectionContentFrame);
+    nsRect drawingRect(nsPoint(0, 0), currentPageFrame->GetSize());
+    nsRegion drawingRegion(drawingRect);
+    nsLayoutUtils::PaintFrame(
+      gCtx,
+      currentPageFrame,
+      drawingRegion,
+      NS_RGBA(0, 0, 0, 0),
+      nsDisplayListBuilderMode::PAINTING,
+      nsLayoutUtils::PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES);
 
-          // We're going to loop and call BeginPage to print another page's worth
-          // of selection so we need to call EndPage first.  (Otherwise, EndPage
-          // is called in DoEndPage.)
-          PR_PL(("***************** End Page (PrintNextPage) *****************\n"));
-          rv = dc->EndPage();
-          NS_ENSURE_SUCCESS(rv, rv);
-        }
+    if (mSelectionHeight >= 0) {
+      haveUnfinishedSelectionToPrint = (selectionY < mSelectionHeight);
+      if (haveUnfinishedSelectionToPrint) {
+        selectionY += pageContentHeight;
+        selectionCurrentPageNum++;
+        pf->SetPageNumInfo(selectionCurrentPageNum, mTotalPages);
+        selectionContentFrame->SetPosition(
+          selectionContentFrame->GetPosition() +
+          nsPoint(0, -pageContentHeight));
+        nsContainerFrame::PositionChildViews(selectionContentFrame);
+
+        mCurrentSelection =
+          Some(CurrentSelection(selectionY, selectionCurrentPageNum));
+      } else {
+        mCurrentSelection = Nothing();
       }
-    } while (haveUnfinishedSelectionToPrint);
+    } else {
+      mCurrentSelection = Nothing();
+    }
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsSimplePageSequenceFrame::DoPageEnd()
 {
   nsresult rv = NS_OK;
   if (PresContext()->IsRootPaginatedDocument() && mPrintThisPage) {
     PR_PL(("***************** End Page (DoPageEnd) *****************\n"));
     rv = PresContext()->DeviceContext()->EndPage();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  ResetPrintCanvasList();
+  if (!mCurrentSelection) {
+    ResetPrintCanvasList();
 
-  mPageNum++;
+    mPageNum++;
+  }
 
   return rv;
 }
 
 inline gfx::Matrix4x4
 ComputePageSequenceTransform(nsIFrame* aFrame, float aAppUnitsPerPixel)
 {
   float scale = aFrame->PresContext()->GetPrintPreviewScale();
--- a/layout/generic/nsSimplePageSequenceFrame.h
+++ b/layout/generic/nsSimplePageSequenceFrame.h
@@ -88,16 +88,17 @@ public:
   NS_IMETHOD PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone) override;
   NS_IMETHOD PrintNextPage() override;
   NS_IMETHOD ResetPrintCanvasList() override;
   NS_IMETHOD GetCurrentPageNum(int32_t* aPageNum) override;
   NS_IMETHOD GetNumPages(int32_t* aNumPages) override;
   NS_IMETHOD IsDoingPrintRange(bool* aDoing) override;
   NS_IMETHOD GetPrintRange(int32_t* aFromPage, int32_t* aToPage) override;
   NS_IMETHOD DoPageEnd() override;
+  NS_IMETHOD IsInSelection(bool* aInSelection) override;
 
   // We must allow Print Preview UI to have a background, no matter what the
   // user's settings
   bool HonorPrintBackgroundSettings() override { return false; }
 
   bool HasTransformGetter() const override { return true; }
 
   /**
@@ -144,16 +145,27 @@ protected:
   int32_t      mPageNum;
   int32_t      mTotalPages;
   int32_t      mPrintRangeType;
   int32_t      mFromPageNum;
   int32_t      mToPageNum;
   nsTArray<int32_t> mPageRanges;
   nsTArray<RefPtr<mozilla::dom::HTMLCanvasElement> > mCurrentCanvasList;
 
+  struct CurrentSelection
+  {
+    explicit CurrentSelection(nscoord aSelectionY, int32_t aCurrentPageNum)
+      : mSelectionY(aSelectionY)
+      , mCurrentPageNum(aCurrentPageNum){};
+
+    nscoord mSelectionY;
+    int32_t mCurrentPageNum;
+  };
+  mozilla::Maybe<CurrentSelection> mCurrentSelection;
+
   // Selection Printing Info
   nscoord      mSelectionHeight;
   nscoord      mYSelOffset;
 
   // Asynch Printing
   bool mPrintThisPage;
   bool mDoingPageRange;
 
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -2864,43 +2864,61 @@ nsPrintEngine::PrintPage(nsPrintObject* 
   }
 
   // XXX This is wrong, but the actual behavior in the presence of a print
   // range sucks.
   if (printData->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
     endPage = printData->mNumPrintablePages;
   }
 
-  printData->DoOnProgressChange(++printData->mNumPagesPrinted,
-                                endPage, false, 0);
-  if (NS_WARN_IF(mPrt != printData)) {
-    // If current printing is canceled or new print is started, let's return
-    // true to notify the caller of current printing is done.
+  bool isInSelection = false;
+  nsresult rv = pageSeqFrame->IsInSelection(&isInSelection);
+  if (NS_FAILED(rv)) {
+    FirePrintingErrorEvent(rv);
+    printData->mIsAborted = true;
     return true;
   }
+  if (!isInSelection) {
+    printData->DoOnProgressChange(
+      ++printData->mNumPagesPrinted, endPage, false, 0);
+    if (NS_WARN_IF(mPrt != printData)) {
+      // If current printing is canceled or new print is started, let's return
+      // true to notify the caller of current printing is done.
+      return true;
+    }
+  }
 
   // Print the Page
   // if a print job was cancelled externally, an EndPage or BeginPage may
   // fail and the failure is passed back here.
   // Returning true means we are done printing.
   //
   // When rv == NS_ERROR_ABORT, it means we want out of the
   // print job without displaying any error messages
-  nsresult rv = pageSeqFrame->PrintNextPage();
+  rv = pageSeqFrame->PrintNextPage();
   if (NS_FAILED(rv)) {
     if (rv != NS_ERROR_ABORT) {
       FirePrintingErrorEvent(rv);
       printData->mIsAborted = true;
     }
     return true;
   }
 
   pageSeqFrame->DoPageEnd();
 
-  return donePrinting;
+  rv = pageSeqFrame->IsInSelection(&isInSelection);
+  if (NS_FAILED(rv)) {
+    FirePrintingErrorEvent(rv);
+    printData->mIsAborted = true;
+    return true;
+  }
+
+  // If we're in a selection, we're not actually done until that whole selection
+  // is done.
+  return donePrinting && !isInSelection;
 }
 
 /** ---------------------------------------------------
  *  Find by checking frames type
  */
 nsresult
 nsPrintEngine::FindSelectionBoundsWithList(nsFrameList::Enumerator& aChildFrames,
                                            nsIFrame *      aParentFrame,