Bug 1412643 - Part 3 - fixed print selection; r?bobowen draft
authorAlex Gaynor <agaynor@mozilla.com>
Wed, 01 Nov 2017 11:55:38 -0400
changeset 690234 178ee8361e88a4d106e807a95b9badb4cdff9714
parent 690233 81f2b3db7f68b8db5891aea73654df141b1ce0b2
child 738521 288f901fe0a489e15d84b481b93c28e6988896db
push id87244
push userbmo:agaynor@mozilla.com
push dateWed, 01 Nov 2017 19:14:41 +0000
reviewersbobowen
bugs1412643
milestone58.0a1
Bug 1412643 - Part 3 - fixed print selection; 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,