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
--- 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();
}
}
}