Bug 1391645 - Make nsPrintEngine::SetupToPrintContent() proceed with the print even when mPrt->mPrintObject->mPresShell and mPrt->mPrintObject->mPresContext are nullptr but mIsCreatingPrintPreview is false r?dholbert draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 21 Aug 2017 14:56:07 +0900
changeset 652774 78baa1a919668b50e021a1652ac4fd60973c579d
parent 652773 3fcc122640ed2b744e83fb82dfeafa40cfa9e2cc
child 728175 44ae37f7002f68f7eb933b7930331e7a7365ef04
push id76147
push usermasayuki@d-toybox.com
push dateFri, 25 Aug 2017 07:24:02 +0000
reviewersdholbert
bugs1391645, 1376693
milestone57.0a1
Bug 1391645 - Make nsPrintEngine::SetupToPrintContent() proceed with the print even when mPrt->mPrintObject->mPresShell and mPrt->mPrintObject->mPresContext are nullptr but mIsCreatingPrintPreview is false r?dholbert Bug 1376693 added a null-check to bail from print operations if mPresShell/mPresContext are null, to avoid some null-deref crashes. However, it turns out it's possible for these variables to be null under normal conditions -- for example, when nsPrintEngine is printing documents, mPrt->mPrintObject->mPresShell and mPrt->mPrintObject->mPresShell can be nullptr if the document has <frameset> element and it's printing only content of a <frame> element or all <frame> elements separately. This special frameset-printing mode can cause these variables to be null; however, the crash occurred only when mIsCreatingPrintPreview is true. So, we should check the variables only when it's true. Fortunately: * the null-deref crashes that Bug 1376693 wanted to avoid were all in code that we only visit when mIsCreatingPrintPreview is true (i.e. during print preview). * this special frameset-printing mode (which causes these variables to be null) _cannot be used during print preview_. So, we can avoid the print-preview-specific crashes without breaking frameset-printing by simply making our null-check bail-out conditional on mIsCreatingPrintPreview. MozReview-Commit-ID: FJ3ynrXTxnI
layout/printing/nsPrintEngine.cpp
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -1639,16 +1639,24 @@ nsPrintEngine::ReconstructAndReflow(bool
   for (uint32_t i = 0; i < printData->mPrintDocList.Length(); ++i) {
     nsPrintObject* po = printData->mPrintDocList.ElementAt(i);
     NS_ASSERTION(po, "nsPrintObject can't be null!");
 
     if (po->mDontPrint || po->mInvisible) {
       continue;
     }
 
+    // When the print object has been marked as "print the document" (i.e,
+    // po->mDontPrint is false), mPresContext and mPresShell should be
+    // non-nullptr (i.e., should've been created for the print) since they
+    // are necessary to print the document.
+    MOZ_ASSERT(po->mPresContext && po->mPresShell,
+      "mPresContext and mPresShell shouldn't be nullptr when the print object "
+      "has been marked as \"print the document\"");
+
     UpdateZoomRatio(po, doSetPixelScale);
 
     po->mPresContext->SetPageScale(po->mZoomRatio);
 
     // Calculate scale factor from printer to screen
     float printDPI = float(printData->mPrintDC->AppUnitsPerCSSInch()) /
                        float(printData->mPrintDC->AppUnitsPerDevPixel());
     po->mPresContext->SetPrintPreviewScale(mScreenDPI / printDPI);
@@ -1693,25 +1701,45 @@ nsPrintEngine::ReconstructAndReflow(bool
 //-------------------------------------------------------
 nsresult
 nsPrintEngine::SetupToPrintContent()
 {
   // This method may be called while DoCommonPrint() initializes the instance
   // when its script blocker goes out of scope.  In such case, this cannot do
   // its job as expected because some objects in mPrt have not been initialized
   // yet but they are necessary.
-  // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we
-  // just check it for good measure, as we check its owner & members.
+  // Note: it shouldn't be possible for mPrt->mPrintObject to be null; we check
+  // it for good measure (after we check its owner) before we start
+  // dereferencing it below.
   if (NS_WARN_IF(!mPrt) ||
-      NS_WARN_IF(!mPrt->mPrintObject) ||
-      NS_WARN_IF(!mPrt->mPrintObject->mPresShell) ||
-      NS_WARN_IF(!mPrt->mPrintObject->mPresContext)) {
+      NS_WARN_IF(!mPrt->mPrintObject)) {
     return NS_ERROR_FAILURE;
   }
 
+  // If this is creating print preview, mPrt->mPrintObject->mPresContext and
+  // mPrt->mPrintObject->mPresShell need to be non-nullptr because this cannot
+  // initialize page sequence frame without them at end of this method since
+  // page sequence frame has already been destroyed or not been created yet.
+  if (mIsCreatingPrintPreview &&
+      (NS_WARN_IF(!mPrt->mPrintObject->mPresContext) ||
+       NS_WARN_IF(!mPrt->mPrintObject->mPresShell))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // If this is printing some documents (not print-previewing the documents),
+  // mPrt->mPrintObject->mPresContext and mPrt->mPrintObject->mPresShell can be
+  // nullptr only when mPrt->mPrintObject->mDontPrint is set to true.  E.g., if
+  // the document has a <frameset> element and it's printing only content in a
+  // <frame> element or all <frame> elements separately.
+  MOZ_ASSERT(
+    (!mIsCreatingPrintPreview && !mPrt->mPrintObject->IsPrintable()) ||
+    (mPrt->mPrintObject->mPresContext && mPrt->mPrintObject->mPresShell),
+    "mPresContext and mPresShell shouldn't be nullptr when printing the "
+    "document or creating print-preview");
+
   bool didReconstruction = false;
 
   // This method works with mPrt->mPrintObject.  So, we need to guarantee that
   // it won't be deleted in this method.  We achieve this by holding a strong
   // local reference to mPrt, which in turn keeps mPrintObject alive.
   RefPtr<nsPrintData> printData = mPrt;
 
   // If some new content got loaded since the initial reflow rebuild
@@ -2377,16 +2405,20 @@ void
 nsPrintEngine::CalcNumPrintablePages(int32_t& aNumPages)
 {
   aNumPages = 0;
   // Count the number of printable documents
   // and printable pages
   for (uint32_t i=0; i<mPrt->mPrintDocList.Length(); i++) {
     nsPrintObject* po = mPrt->mPrintDocList.ElementAt(i);
     NS_ASSERTION(po, "nsPrintObject can't be null!");
+    // Note: The po->mPresContext null-check below is necessary, because it's
+    // possible po->mPresContext might never have been set.  (e.g., if
+    // IsPrintable() returns false, ReflowPrintObject bails before setting
+    // mPresContext)
     if (po->mPresContext && po->mPresContext->IsRootPaginatedDocument()) {
       nsIPageSequenceFrame* pageSequence = po->mPresShell->GetPageSequenceFrame();
       nsIFrame * seqFrame = do_QueryFrame(pageSequence);
       if (seqFrame) {
         aNumPages += seqFrame->PrincipalChildList().GetLength();
       }
     }
   }