Bug 745025 - Part 2 - Adds layout/printing support for mozPrintCallback. r=roc
authorBrendan Dahl <bdahl@mozilla.com>
Thu, 30 Aug 2012 17:24:35 -0700
changeset 110520 206e0ad324c9701e88ce5efcf8bea4246356da84
parent 110519 87b00ac22aa8b1340acc1b11b8b466dfabbd468b
child 110521 8eee25223a3ed4bab80927eb920a211773fa1927
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs745025
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 745025 - Part 2 - Adds layout/printing support for mozPrintCallback. r=roc
layout/base/nsDocumentViewer.cpp
layout/generic/nsHTMLCanvasFrame.cpp
layout/generic/nsIPageSequenceFrame.h
layout/generic/nsSimplePageSequence.cpp
layout/generic/nsSimplePageSequence.h
layout/printing/nsPagePrintTimer.cpp
layout/printing/nsPagePrintTimer.h
layout/printing/nsPrintEngine.cpp
layout/printing/nsPrintEngine.h
layout/reftests/printing/745025-1-ref.html
layout/reftests/printing/745025-1.html
layout/reftests/printing/reftest.list
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -183,16 +183,17 @@ static PRLogModuleInfo * kPrintingLogMod
 #define PRT_YESNO(_p) ((_p)?"YES":"NO")
 #else
 #define PRT_YESNO(_p)
 #define PR_PL(_p1)
 #endif
 //-----------------------------------------------------
 
 class DocumentViewerImpl;
+class nsPrintEventDispatcher;
 
 // a small delegate class used to avoid circular references
 
 class nsDocViewerSelectionListener : public nsISelectionListener
 {
 public:
 
   // nsISupports interface...
@@ -431,16 +432,17 @@ protected:
   unsigned                         mPrintIsPending : 1;
   unsigned                         mPrintDocIsFullyLoaded : 1;
   nsCOMPtr<nsIPrintSettings>       mCachedPrintSettings;
   nsCOMPtr<nsIWebProgressListener> mCachedPrintWebProgressListner;
 
   nsCOMPtr<nsPrintEngine>          mPrintEngine;
   float                            mOriginalPrintPreviewScale;
   float                            mPrintPreviewZoom;
+  nsAutoPtr<nsPrintEventDispatcher> mBeforeAndAfterPrint;
 #endif // NS_PRINT_PREVIEW
 
 #ifdef DEBUG
   FILE* mDebugFile;
 #endif // DEBUG
 #endif // NS_PRINTING
 
   /* character set member data */
@@ -1500,16 +1502,17 @@ DocumentViewerImpl::Destroy()
   //
   // So we flip the bool to remember that the document is going away
   // and we can clean up and abort later after returning from the Print Dialog
   if (mPrintEngine) {
     if (mPrintEngine->CheckBeforeDestroy()) {
       return NS_OK;
     }
   }
+  mBeforeAndAfterPrint = nullptr;
 #endif
 
   // Don't let the document get unloaded while we are printing.
   // this could happen if we hit the back button during printing.
   // We also keep the viewer from being cached in session history, since
   // we require all documents there to be sanitized.
   if (mDestroyRefCount != 0) {
     --mDestroyRefCount;
@@ -3606,17 +3609,18 @@ DocumentViewerImpl::Print(nsIPrintSettin
   // the only time we can print more than one job at a time is the regression tests
   if (GetIsPrinting()) {
     // Let the user know we are not ready to print.
     rv = NS_ERROR_NOT_AVAILABLE;
     nsPrintEngine::ShowPrintErrorDialog(rv);
     return rv;
   }
 
-  nsPrintEventDispatcher beforeAndAfterPrint(mDocument);
+  nsAutoPtr<nsPrintEventDispatcher> beforeAndAfterPrint(
+    new nsPrintEventDispatcher(mDocument));
   NS_ENSURE_STATE(!GetIsPrinting());
   // If we are hosting a full-page plugin, tell it to print
   // first. It shows its own native print UI.
   nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(mDocument));
   if (pDoc)
     return pDoc->Print();
 
   if (!mPrintEngine) {
@@ -3634,17 +3638,19 @@ DocumentViewerImpl::Print(nsIPrintSettin
 #endif
                                   );
     if (NS_FAILED(rv)) {
       mPrintEngine->Destroy();
       mPrintEngine = nullptr;
       return rv;
     }
   }
-
+  if (mPrintEngine->HasPrintCallbackCanvas()) {
+    mBeforeAndAfterPrint = beforeAndAfterPrint;
+  }
   rv = mPrintEngine->Print(aPrintSettings, aWebProgressListener);
   if (NS_FAILED(rv)) {
     OnDonePrinting();
   }
   return rv;
 }
 
 NS_IMETHODIMP
@@ -3680,17 +3686,18 @@ DocumentViewerImpl::PrintPreview(nsIPrin
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIDOMDocument> domDoc;
   aChildDOMWin->GetDocument(getter_AddRefs(domDoc));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
   NS_ENSURE_STATE(doc);
 
-  nsPrintEventDispatcher beforeAndAfterPrint(doc);
+  nsAutoPtr<nsPrintEventDispatcher> beforeAndAfterPrint(
+    new nsPrintEventDispatcher(doc));
   NS_ENSURE_STATE(!GetIsPrinting());
   if (!mPrintEngine) {
     mPrintEngine = new nsPrintEngine();
 
     rv = mPrintEngine->Initialize(this, mContainer, doc,
                                   float(mDeviceContext->AppUnitsPerCSSInch()) /
                                   float(mDeviceContext->AppUnitsPerDevPixel()) /
                                   mPageZoom,
@@ -3701,17 +3708,19 @@ DocumentViewerImpl::PrintPreview(nsIPrin
 #endif
                                   );
     if (NS_FAILED(rv)) {
       mPrintEngine->Destroy();
       mPrintEngine = nullptr;
       return rv;
     }
   }
-
+  if (mPrintEngine->HasPrintCallbackCanvas()) {
+    mBeforeAndAfterPrint = beforeAndAfterPrint;
+  }
   rv = mPrintEngine->PrintPreview(aPrintSettings, aChildDOMWin, aWebProgressListener);
   mPrintPreviewZoomed = false;
   if (NS_FAILED(rv)) {
     OnDonePrinting();
   }
   return rv;
 #else
   return NS_ERROR_FAILURE;
@@ -4106,16 +4115,20 @@ DocumentViewerImpl::SetIsPrinting(bool a
   // Set all the docShells in the docshell tree to be printing.
   // that way if anyone of them tries to "navigate" it can't
   nsCOMPtr<nsIDocShellTreeNode> docShellTreeNode(do_QueryReferent(mContainer));
   if (docShellTreeNode || !aIsPrinting) {
     SetIsPrintingInDocShellTree(docShellTreeNode, aIsPrinting, true);
   } else {
     NS_WARNING("Did you close a window before printing?");
   }
+
+  if (!aIsPrinting) {
+    mBeforeAndAfterPrint = nullptr;
+  }
 #endif
 }
 
 //------------------------------------------------------------
 // The PrintEngine holds the current value
 // this called from inside the DocViewer.
 // XXX it always returns false for subdocuments
 bool
@@ -4136,16 +4149,19 @@ DocumentViewerImpl::SetIsPrintPreview(bo
 {
 #ifdef NS_PRINTING
   // Set all the docShells in the docshell tree to be printing.
   // that way if anyone of them tries to "navigate" it can't
   nsCOMPtr<nsIDocShellTreeNode> docShellTreeNode(do_QueryReferent(mContainer));
   if (docShellTreeNode || !aIsPrintPreview) {
     SetIsPrintingInDocShellTree(docShellTreeNode, aIsPrintPreview, true);
   }
+  if (!aIsPrintPreview) {
+    mBeforeAndAfterPrint = nullptr;
+  }
 #endif
   if (!aIsPrintPreview) {
     if (mPresShell) {
       DestroyPresShell();
     }
     mWindow = nullptr;
     mViewManager = nullptr;
     mPresContext = nullptr;
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -255,26 +255,28 @@ already_AddRefed<Layer>
 nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
                               nsDisplayItem* aItem)
 {
   nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame();
   nsHTMLCanvasElement* element = static_cast<nsHTMLCanvasElement*>(GetContent());
   nsIntSize canvasSize = GetCanvasSize();
 
+  nsPresContext* presContext = PresContext();
+  element->HandlePrintCallback(presContext->Type());
+
   if (canvasSize.width <= 0 || canvasSize.height <= 0 || area.IsEmpty())
     return nullptr;
 
   CanvasLayer* oldLayer = static_cast<CanvasLayer*>
     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aManager, aItem));
   nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
   if (!layer)
     return nullptr;
 
-  nsPresContext* presContext = PresContext();
   gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
                       presContext->AppUnitsToGfxUnits(area.y),
                       presContext->AppUnitsToGfxUnits(area.width),
                       presContext->AppUnitsToGfxUnits(area.height));
 
   // Transform the canvas into the right place
   gfxMatrix transform;
   transform.Translate(r.TopLeft());
--- a/layout/generic/nsIPageSequenceFrame.h
+++ b/layout/generic/nsIPageSequenceFrame.h
@@ -5,16 +5,17 @@
 #ifndef nsIPageSequenceFrame_h___
 #define nsIPageSequenceFrame_h___
 
 #include "nsQueryFrame.h"
 #include "nsRect.h"
 
 class nsPresContext;
 class nsIPrintSettings;
+class nsITimerCallback;
 
 /**
  * Interface for accessing special capabilities of the page sequence frame.
  *
  * Today all that exists are member functions for printing.
  */
 class nsIPageSequenceFrame : public nsQueryFrame
 {
@@ -33,17 +34,20 @@ public:
    *          NS_ERROR_INVALID_ARG if printing a range of pages (not all pages)
    *            and the start page is greater than the total number of pages
    *          NS_ERROR_FAILURE if there is an error
    */
   NS_IMETHOD StartPrint(nsPresContext*  aPresContext,
                         nsIPrintSettings* aPrintOptions,
                         PRUnichar* aDocTitle,
                         PRUnichar* aDocURL) = 0;
+
+  NS_IMETHOD PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone) = 0;
   NS_IMETHOD PrintNextPage() = 0;
+  NS_IMETHOD ResetPrintCanvasList() = 0;
   NS_IMETHOD GetCurrentPageNum(int32_t* aPageNum) = 0;
   NS_IMETHOD GetNumPages(int32_t* aNumPages) = 0;
   NS_IMETHOD IsDoingPrintRange(bool* aDoing) = 0;
   NS_IMETHOD GetPrintRange(int32_t* aFromPage, int32_t* aToPage) = 0;
 
   NS_IMETHOD DoPageEnd() = 0;
   NS_IMETHOD SetSelectionHeight(nscoord aYOffset, nscoord aHeight) = 0;
 
--- a/layout/generic/nsSimplePageSequence.cpp
+++ b/layout/generic/nsSimplePageSequence.cpp
@@ -7,22 +7,26 @@
 #include "nsSimplePageSequence.h"
 #include "nsPresContext.h"
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "nsGkAtoms.h"
 #include "nsIPresShell.h"
 #include "nsIPrintSettings.h"
 #include "nsPageFrame.h"
+#include "nsSubDocumentFrame.h"
 #include "nsStyleConsts.h"
 #include "nsRegion.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "mozilla/Preferences.h"
+#include "nsHTMLCanvasFrame.h"
+#include "nsHTMLCanvasElement.h"
+#include "nsICanvasRenderingContextInternal.h"
 
 // DateTime Includes
 #include "nsDateTimeFormatCID.h"
 
 #define OFFSET_NOT_SET -1
 
 // Print Options
 #include "nsIPrintOptions.h"
@@ -77,18 +81,20 @@ NS_NewSimplePageSequenceFrame(nsIPresShe
   return new (aPresShell) nsSimplePageSequenceFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSimplePageSequenceFrame)
 
 nsSimplePageSequenceFrame::nsSimplePageSequenceFrame(nsStyleContext* aContext) :
   nsContainerFrame(aContext),
   mTotalPages(-1),
+  mCurrentCanvasListSetup(false),
   mSelectionHeight(-1),
-  mYSelOffset(0)
+  mYSelOffset(0),
+  mCalledBeginPage(false)
 {
   nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5));
   mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch);
 
   // XXX Unsafe to assume successful allocation
   mPageData = new nsSharedPageData();
   mPageData->mHeadFootFont =
     new nsFont(*PresContext()->GetDefaultFont(kGenericFont_serif,
@@ -482,55 +488,77 @@ nsSimplePageSequenceFrame::StartPrint(ns
 
   if (mTotalPages == -1) {
     mTotalPages = totalPages;
   }
 
   return rv;
 }
 
-NS_IMETHODIMP
-nsSimplePageSequenceFrame::PrintNextPage()
+void
+GetPrintCanvasElementsInFrame(nsIFrame* aFrame, nsTArray<nsRefPtr<nsHTMLCanvasElement> >* aArr)
 {
-  // Print each specified page
-  // pageNum keeps track of the current page and what pages are printing
-  //
-  // printedPageNum keeps track of the current page number to be printed
-  // Note: When print al the pages or a page range the printed page shows the
-  // actual page number, when printing selection it prints the page number starting
-  // with the first page of the selection. For example if the user has a 
-  // selection that starts on page 2 and ends on page 3, the page numbers when
-  // print are 1 and then two (which is different than printing a page range, where
-  // the page numbers would have been 2 and then 3)
+  for (nsIFrame::ChildListIterator childLists(aFrame);
+    !childLists.IsDone(); childLists.Next()) {
+
+    nsFrameList children = childLists.CurrentList();
+    for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
+      nsIFrame* child = e.get();
+
+      // Check if child is a nsHTMLCanvasFrame.
+      nsHTMLCanvasFrame* canvasFrame = do_QueryFrame(child);
 
-  if (mCurrentPageFrame == nullptr) {
-    return NS_ERROR_FAILURE;
+      // If there is a canvasFrame, try to get actual canvas element.
+      if (canvasFrame) {
+        nsHTMLCanvasElement* canvas =
+          nsHTMLCanvasElement::FromContent(canvasFrame->GetContent());
+        nsCOMPtr<nsIPrintCallback> printCallback;
+        if (canvas &&
+            NS_SUCCEEDED(canvas->GetMozPrintCallback(getter_AddRefs(printCallback))) &&
+            printCallback) {
+          aArr->AppendElement(canvas);
+          continue;
+        }
+      }
+
+      if (!child->GetFirstPrincipalChild()) {
+        nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(child);
+        if (subdocumentFrame) {
+          // Descend into the subdocument
+          nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
+          child = root;
+        }
+      }
+      // The current child is not a nsHTMLCanvasFrame OR it is but there is
+      // no nsHTMLCanvasElement on it. Check if children of `child` might
+      // contain a nsHTMLCanvasElement.
+      GetPrintCanvasElementsInFrame(child, aArr);
+    }
   }
+}
 
+void
+nsSimplePageSequenceFrame::DetermineWhetherToPrintPage()
+{
+  // See whether we should print this page
+  mPrintThisPage = true;
   bool printEvenPages, printOddPages;
   mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintEvenPages, &printEvenPages);
   mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintOddPages, &printOddPages);
 
-  // Begin printing of the document
-  nsDeviceContext *dc = PresContext()->DeviceContext();
-
-  nsresult rv = NS_OK;
-
-  // See whether we should print this page
-  mPrintThisPage = true;
-
   // If printing a range of pages check whether the page number is in the
   // range of pages to print
   if (mDoingPageRange) {
     if (mPageNum < mFromPageNum) {
       mPrintThisPage = false;
     } else if (mPageNum > mToPageNum) {
       mPageNum++;
       mCurrentPageFrame = nullptr;
-      return NS_OK;
+      mPrintThisPage = false;
+      return;
     } else {
       int32_t length = mPageRanges.Length();
     
       // Page ranges are pairs (start, end)
       if (length && (length % 2 == 0)) {
         mPrintThisPage = false;
       
         int32_t i;
@@ -553,18 +581,153 @@ nsSimplePageSequenceFrame::PrintNextPage
     if (!printEvenPages) {
       mPrintThisPage = false;  // don't print even numbered page
     }
   }
   
   if (nsIPrintSettings::kRangeSelection == mPrintRangeType) {
     mPrintThisPage = true;
   }
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone)
+{
+  if (!mCurrentPageFrame) {
+    *aDone = true;
+    return NS_ERROR_FAILURE;
+  }
+  
+  DetermineWhetherToPrintPage();
+  // Nothing to do if the current page doesn't get printed OR rendering to
+  // preview. For preview, the `CallPrintCallback` is called from within the
+  // nsHTMLCanvasElement::HandlePrintCallback.
+  if (!mPrintThisPage || !PresContext()->IsRootPaginatedDocument()) {
+    *aDone = true;
+    return NS_OK;
+  }
+
+  // If the canvasList is null, then generate it and start the render
+  // process for all the canvas.
+  if (!mCurrentCanvasListSetup) {
+    mCurrentCanvasListSetup = true;
+    GetPrintCanvasElementsInFrame(mCurrentPageFrame, &mCurrentCanvasList);
+
+    if (mCurrentCanvasList.Length() != 0) {
+      nsresult rv = NS_OK;
+
+      // Begin printing of the document
+      nsDeviceContext *dc = PresContext()->DeviceContext();
+      PR_PL(("\n"));
+      PR_PL(("***************** BeginPage *****************\n"));
+      rv = dc->BeginPage();
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      mCalledBeginPage = true;
+      
+      nsRefPtr<nsRenderingContext> renderingContext;
+      dc->CreateRenderingContext(*getter_AddRefs(renderingContext));
+      NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY);
+
+      nsRefPtr<gfxASurface> renderingSurface =
+          renderingContext->ThebesContext()->CurrentSurface();
+      NS_ENSURE_TRUE(renderingSurface, NS_ERROR_OUT_OF_MEMORY);
+
+      for (PRInt32 i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+        nsHTMLCanvasElement* canvas = mCurrentCanvasList[i];
+        nsIntSize size = canvas->GetSize();
+
+        nsRefPtr<gfxASurface> printSurface = renderingSurface->
+           CreateSimilarSurface(
+             gfxASurface::CONTENT_COLOR_ALPHA,
+             size
+           );
+
+        nsICanvasRenderingContextInternal* ctx = canvas->GetContextAtIndex(0);
+
+        if (!ctx) {
+          continue;
+        }
+
+          // Initialize the context with the new printSurface.
+        ctx->InitializeWithSurface(NULL, printSurface, size.width, size.height);
+
+        // Start the rendering process.
+        nsWeakFrame weakFrame = this;
+        canvas->DispatchPrintCallback(aCallback);
+        NS_ENSURE_STATE(weakFrame.IsAlive());
+      }
+    }
+  }
+  PRInt32 doneCounter = 0;
+  for (PRInt32 i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+    nsHTMLCanvasElement* canvas = mCurrentCanvasList[i];
+
+    if (canvas->IsPrintCallbackDone()) {
+      doneCounter++;
+    }
+  }
+  // If all canvas have finished rendering, return true, otherwise false.
+  *aDone = doneCounter == mCurrentCanvasList.Length();
+
+  return NS_OK;
+}
+
+void
+nsSimplePageSequenceFrame::InvalidateInternal(const nsRect& aDamageRect,
+                                              nscoord aX, nscoord aY,
+                                              nsIFrame* aForChild,
+                                              PRUint32 aFlags)
+{
+  // xxx Invalidate the entire frame as otherwise invalidate of printCanvas
+  // don't work properly. This is hopefully no longer necessary once 539356
+  // lands.
+  nsContainerFrame::InvalidateInternal(
+      nsRect(nsPoint(0,0), GetSize()), 0, 0, aForChild, aFlags); 
+}
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::ResetPrintCanvasList()
+{
+  for (PRInt32 i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
+    nsHTMLCanvasElement* canvas = mCurrentCanvasList[i];
+    canvas->ResetPrintCallback();
+  }
+
+  mCurrentCanvasList.Clear();
+  mCurrentCanvasListSetup = false; 
+  return NS_OK;
+} 
+
+NS_IMETHODIMP
+nsSimplePageSequenceFrame::PrintNextPage()
+{
+  // Print each specified page
+  // pageNum keeps track of the current page and what pages are printing
+  //
+  // printedPageNum keeps track of the current page number to be printed
+  // Note: When print al the pages or a page range the printed page shows the
+  // actual page number, when printing selection it prints the page number starting
+  // with the first page of the selection. For example if the user has a 
+  // selection that starts on page 2 and ends on page 3, the page numbers when
+  // print are 1 and then two (which is different than printing a page range, where
+  // the page numbers would have been 2 and then 3)
+
+  if (!mCurrentPageFrame) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = NS_OK;
+
+  DetermineWhetherToPrintPage();
 
   if (mPrintThisPage) {
+    // Begin printing of the document
+    nsDeviceContext* dc = PresContext()->DeviceContext();
+
     // XXX This is temporary fix for printing more than one page of a selection
     // This does a poor man's "dump" pagination (see Bug 89353)
     // It has laid out as one long page and now we are just moving or view up/down 
     // one page at a time and printing the contents of what is exposed by the rect.
     // currently this does not work for IFrames
     // I will soon improve this to work with IFrames 
     bool    continuePrinting = true;
     nscoord width, height;
@@ -582,20 +745,24 @@ nsSimplePageSequenceFrame::PrintNextPage
     // cast the frame to be a page frame
     nsPageFrame * pf = static_cast<nsPageFrame*>(mCurrentPageFrame);
     pf->SetPageNumInfo(mPageNum, mTotalPages);
     pf->SetSharedPageData(mPageData);
 
     int32_t printedPageNum = 1;
     while (continuePrinting) {
       if (PresContext()->IsRootPaginatedDocument()) {
-        PR_PL(("\n"));
-        PR_PL(("***************** BeginPage *****************\n"));
-        rv = dc->BeginPage();
-        NS_ENSURE_SUCCESS(rv, rv);
+        if (!mCalledBeginPage) {
+          PR_PL(("\n"));
+          PR_PL(("***************** BeginPage *****************\n"));
+          rv = dc->BeginPage();
+          NS_ENSURE_SUCCESS(rv, rv);
+        } else {
+          mCalledBeginPage = false;
+        }
       }
 
       PR_PL(("SeqFr::PrintNextPage -> %p PageNo: %d", pf, mPageNum));
 
       nsRefPtr<nsRenderingContext> renderingContext;
       dc->CreateRenderingContext(*getter_AddRefs(renderingContext));
       NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY);
 
@@ -629,16 +796,18 @@ 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();
+
   mPageNum++;
 
   if (mCurrentPageFrame) {
     mCurrentPageFrame = mCurrentPageFrame->GetNextSibling();
   }
   
   return rv;
 }
--- a/layout/generic/nsSimplePageSequence.h
+++ b/layout/generic/nsSimplePageSequence.h
@@ -5,16 +5,17 @@
 #ifndef nsSimplePageSequence_h___
 #define nsSimplePageSequence_h___
 
 #include "nsIPageSequenceFrame.h"
 #include "nsContainerFrame.h"
 #include "nsIPrintSettings.h"
 #include "nsIPrintOptions.h"
 #include "nsIDateTimeFormat.h"
+#include "nsHTMLCanvasElement.h"
 
 //-----------------------------------------------
 // This class maintains all the data that 
 // is used by all the page frame
 // It lives while the nsSimplePageSequenceFrame lives
 class nsSharedPageData {
 public:
   nsSharedPageData();
@@ -71,34 +72,40 @@ public:
   // For Shrink To Fit
   NS_IMETHOD GetSTFPercent(float& aSTFPercent);
 
   // Async Printing
   NS_IMETHOD StartPrint(nsPresContext*  aPresContext,
                         nsIPrintSettings* aPrintSettings,
                         PRUnichar*        aDocTitle,
                         PRUnichar*        aDocURL);
+  NS_IMETHOD PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone);
   NS_IMETHOD PrintNextPage();
+  NS_IMETHOD ResetPrintCanvasList();
   NS_IMETHOD GetCurrentPageNum(int32_t* aPageNum);
   NS_IMETHOD GetNumPages(int32_t* aNumPages);
   NS_IMETHOD IsDoingPrintRange(bool* aDoing);
   NS_IMETHOD GetPrintRange(int32_t* aFromPage, int32_t* aToPage);
   NS_IMETHOD DoPageEnd();
 
   // We must allow Print Preview UI to have a background, no matter what the
   // user's settings
   virtual bool HonorPrintBackgroundSettings() { return false; }
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::sequenceFrame
    */
   virtual nsIAtom* GetType() const;
-  
+
+  virtual void InvalidateInternal(const nsRect& aDamageRect,
+                                  nscoord aX, nscoord aY,
+                                  nsIFrame* aForChild,
+                                  PRUint32 aFlags);
 #ifdef DEBUG
   NS_IMETHOD  GetFrameName(nsAString& aResult) const;
 #endif
 
   void PaintPageSequence(nsRenderingContext& aRenderingContext,
                          const nsRect&        aDirtyRect,
                          nsPoint              aPt);
 
@@ -113,38 +120,45 @@ protected:
   void SetPageNumberFormat(PRUnichar * aFormatStr, bool aForPageNumOnly);
 
   // Sets the frame desired size to the size of the viewport, or the given
   // nscoords, whichever is larger. Print scaling is applied in this function.
   void SetDesiredSize(nsHTMLReflowMetrics& aDesiredSize,
                       const nsHTMLReflowState& aReflowState,
                       nscoord aWidth, nscoord aHeight);
 
+  void         DetermineWhetherToPrintPage();
+
   nsMargin mMargin;
 
   // I18N date formatter service which we'll want to cache locally.
   nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
 
   nsSize       mSize;
   nsSharedPageData* mPageData; // data shared by all the nsPageFrames
 
   // Asynch Printing
   nsIFrame *   mCurrentPageFrame;
   int32_t      mPageNum;
   int32_t      mTotalPages;
   int32_t      mPrintRangeType;
   int32_t      mFromPageNum;
   int32_t      mToPageNum;
   nsTArray<int32_t> mPageRanges;
+  nsTArray<nsRefPtr<nsHTMLCanvasElement> > mCurrentCanvasList;
 
   // Selection Printing Info
   nscoord      mSelectionHeight;
   nscoord      mYSelOffset;
 
   // Asynch Printing
   bool mPrintThisPage;
   bool mDoingPageRange;
 
   bool mIsPrintingSelection;
+
+  bool mCalledBeginPage;
+
+  bool mCurrentCanvasListSetup;
 };
 
 #endif /* nsSimplePageSequence_h___ */
 
--- a/layout/printing/nsPagePrintTimer.cpp
+++ b/layout/printing/nsPagePrintTimer.cpp
@@ -47,49 +47,62 @@ nsPagePrintTimer::StartTimer(bool aUseDe
         delay = mDelay;
       }
     }
     mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
   }
   return result;
 }
 
+//nsRunnable
+NS_IMETHODIMP
+nsPagePrintTimer::Run() 
+{
+  bool initNewTimer = true;
+  // Check to see if we are done
+  // inRange will be true if a page is actually printed
+  bool inRange;
+  bool donePrinting;
 
+  // donePrinting will be true if it completed successfully or
+  // if the printing was cancelled
+  donePrinting = mPrintEngine->PrintPage(mPrintObj, inRange);
+  if (donePrinting) {
+    // now clean up print or print the next webshell
+    if (mPrintEngine->DonePrintingPages(mPrintObj, NS_OK)) {
+      initNewTimer = false;
+    }
+  }
+
+  // Note that the Stop() destroys this after the print job finishes
+  // (The PrintEngine stops holding a reference when DonePrintingPages
+  // returns true.)
+  Stop(); 
+  if (initNewTimer) {
+    ++mFiringCount;
+    nsresult result = StartTimer(inRange);
+    if (NS_FAILED(result)) {
+      donePrinting = true;     // had a failure.. we are finished..
+      mPrintEngine->SetIsPrinting(false);
+    }
+  }
+  return NS_OK;
+};
 
 // nsITimerCallback
 NS_IMETHODIMP
 nsPagePrintTimer::Notify(nsITimer *timer)
 {
   if (mDocViewerPrint) {
-    bool initNewTimer = true;
-    // Check to see if we are done
-    // inRange will be true if a page is actually printed
-    bool inRange;
-    // donePrinting will be true if it completed successfully or
-    // if the printing was cancelled
-    bool donePrinting = mPrintEngine->PrintPage(mPrintObj, inRange);
-    if (donePrinting) {
-      // now clean up print or print the next webshell
-      if (mPrintEngine->DonePrintingPages(mPrintObj, NS_OK)) {
-        initNewTimer = false;
-      }
+    bool donePrePrint = mPrintEngine->PrePrintPage();
+
+    if (donePrePrint) {
+      NS_DispatchToMainThread(this);
     }
 
-    // Note that the Stop() destroys this after the print job finishes
-    // (The PrintEngine stops holding a reference when DonePrintingPages
-    // returns true.)
-    Stop(); 
-    if (initNewTimer) {
-      ++mFiringCount;
-      nsresult result = StartTimer(inRange);
-      if (NS_FAILED(result)) {
-        donePrinting = true;     // had a failure.. we are finished..
-        mPrintEngine->SetIsPrinting(false);
-      }
-    }
   }
   return NS_OK;
 }
 
 void 
 nsPagePrintTimer::Init(nsPrintEngine*          aPrintEngine,
                        nsIDocumentViewerPrint* aDocViewerPrint,
                        uint32_t                aDelay)
--- a/layout/printing/nsPagePrintTimer.h
+++ b/layout/printing/nsPagePrintTimer.h
@@ -6,39 +6,43 @@
 #define nsPagePrintTimer_h___
 
 // Timer Includes
 #include "nsITimer.h"
 
 #include "nsIDocumentViewerPrint.h"
 #include "nsPrintObject.h"
 #include "mozilla/Attributes.h"
+#include "nsThreadUtils.h"
 
 class nsPrintEngine;
 
 //---------------------------------------------------
 //-- Page Timer Class
 //---------------------------------------------------
-class nsPagePrintTimer MOZ_FINAL : public nsITimerCallback
+class nsPagePrintTimer MOZ_FINAL : public nsITimerCallback,
+                                   public nsRunnable
 {
 public:
 
   NS_DECL_ISUPPORTS
 
   nsPagePrintTimer();
   ~nsPagePrintTimer();
 
   NS_DECL_NSITIMERCALLBACK
 
   void Init(nsPrintEngine*          aPrintEngine,
             nsIDocumentViewerPrint* aDocViewerPrint,
             uint32_t                aDelay);
 
   nsresult Start(nsPrintObject* aPO);
 
+  NS_IMETHOD Run();
+
   void Stop();
 
 private:
   nsresult StartTimer(bool aUseDelay);
 
   nsPrintEngine*             mPrintEngine;
   nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
   nsCOMPtr<nsITimer>         mTimer;
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -2388,18 +2388,104 @@ nsPrintEngine::ElipseLongString(PRUnicha
       newStr.SetLength(aLen-3);
       newStr.AppendLiteral("...");
       nsMemory::Free(aStr);
       aStr = ToNewUnicode(newStr);
     }
   }
 }
 
+static bool
+DocHasPrintCallbackCanvas(nsIDocument* aDoc, void* aData)
+{
+  if (!aDoc) {
+    return true;
+  }
+  Element* root = aDoc->GetRootElement();
+  nsRefPtr<nsContentList> canvases = NS_GetContentList(root,
+                                                       kNameSpaceID_XHTML,
+                                                       NS_LITERAL_STRING("canvas"));
+  PRUint32 canvasCount = canvases->Length(true);
+  for (PRUint32 i = 0; i < canvasCount; ++i) {
+    nsCOMPtr<nsIDOMHTMLCanvasElement> canvas = do_QueryInterface(canvases->Item(i, false));
+    nsCOMPtr<nsIPrintCallback> printCallback;
+    if (canvas && NS_SUCCEEDED(canvas->GetMozPrintCallback(getter_AddRefs(printCallback))) &&
+        printCallback) {
+      // This subdocument has a print callback. Set result and return false to
+      // stop iteration.
+      *static_cast<bool*>(aData) = true;
+      return false;
+    }
+  }
+  return true;
+}
+
+static bool
+DocHasPrintCallbackCanvas(nsIDocument* aDoc)
+{
+  bool result = false;
+  aDoc->EnumerateSubDocuments(&DocHasPrintCallbackCanvas, static_cast<void*>(&result));
+  return result;
+}
+
+/**
+ * Checks to see if the document this print engine is associated with has any
+ * canvases that have a mozPrintCallback.
+ */
+bool
+nsPrintEngine::HasPrintCallbackCanvas()
+{
+  if (!mDocument) {
+    return false;
+  }
+  // First check this mDocument.
+  bool result = false;
+  DocHasPrintCallbackCanvas(mDocument, static_cast<void*>(&result));
+  // Also check the sub documents.
+  return result || DocHasPrintCallbackCanvas(mDocument);
+}
+
 //-------------------------------------------------------
 bool
+nsPrintEngine::PrePrintPage()
+{
+  NS_ASSERTION(mPageSeqFrame,  "mPageSeqFrame is null!");
+  NS_ASSERTION(mPrt,           "mPrt is null!");
+
+  // Although these should NEVER be NULL
+  // This is added insurance, to make sure we don't crash in optimized builds
+  if (!mPrt || !mPageSeqFrame) {
+    return true; // means we are done preparing the page.
+  }
+
+  // Check setting to see if someone request it be cancelled
+  bool isCancelled = false;
+  mPrt->mPrintSettings->GetIsCancelled(&isCancelled);
+  if (isCancelled)
+    return true;
+
+  // Ask mPageSeqFrame if the page is ready to be printed.
+  // If the page doesn't get printed at all, the |done| will be |true|.
+  bool done = false;
+  nsresult rv = mPageSeqFrame->PrePrintNextPage(mPagePrintTimer, &done);
+  if (NS_FAILED(rv)) {
+    // ??? ::PrintPage doesn't set |mPrt->mIsAborted = true| if rv != NS_ERROR_ABORT,
+    // but I don't really understand why this should be the right thing to do?
+    // Shouldn't |mPrt->mIsAborted| set to true all the time if something
+    // wents wrong?
+    if (rv != NS_ERROR_ABORT) {
+      ShowPrintErrorDialog(rv);
+      mPrt->mIsAborted = true;
+    }
+    done = true;
+  }
+  return done;
+}
+
+bool
 nsPrintEngine::PrintPage(nsPrintObject*    aPO,
                          bool&           aInRange)
 {
   NS_ASSERTION(aPO,            "aPO is null!");
   NS_ASSERTION(mPageSeqFrame,  "mPageSeqFrame is null!");
   NS_ASSERTION(mPrt,           "mPrt is null!");
 
   // Although these should NEVER be NULL
@@ -2410,17 +2496,17 @@ nsPrintEngine::PrintPage(nsPrintObject* 
   }
 
   PR_PL(("-----------------------------------\n"));
   PR_PL(("------ In DV::PrintPage PO: %p (%s)\n", aPO, gFrameTypesStr[aPO->mFrameType]));
 
   // Check setting to see if someone request it be cancelled
   bool isCancelled = false;
   mPrt->mPrintSettings->GetIsCancelled(&isCancelled);
-  if (isCancelled)
+  if (isCancelled || mPrt->mIsAborted)
     return true;
 
   int32_t pageNum, numPages, endPage;
   mPageSeqFrame->GetCurrentPageNum(&pageNum);
   mPageSeqFrame->GetNumPages(&numPages);
 
   bool donePrinting;
   bool isDoingPrintRange;
@@ -2669,24 +2755,18 @@ nsPrintEngine::GetPageRangeForSelection(
 //-----------------------------------------------------------------
 
 //---------------------------------------------------------------------
 void nsPrintEngine::SetIsPrinting(bool aIsPrinting)
 { 
   mIsDoingPrinting = aIsPrinting;
   // Calling SetIsPrinting while in print preview confuses the document viewer
   // This is safe because we prevent exiting print preview while printing
-  if (!mIsDoingPrintPreview &&
-      mPrt && mPrt->mPrintObject && mPrt->mPrintObject->mDocShell) {
-    nsCOMPtr<nsIContentViewer> viewer;
-    mPrt->mPrintObject->mDocShell->GetContentViewer(getter_AddRefs(viewer));
-    nsCOMPtr<nsIDocumentViewerPrint> docViewerPrint = do_QueryInterface(viewer);
-    if (docViewerPrint) {
-      docViewerPrint->SetIsPrinting(aIsPrinting);
-    }
+  if (!mIsDoingPrintPreview && mDocViewerPrint) {
+    mDocViewerPrint->SetIsPrinting(aIsPrinting);
   }
   if (mPrt && aIsPrinting) {
     mPrt->mPreparingForPrint = true;
   }
 }
 
 //---------------------------------------------------------------------
 void nsPrintEngine::SetIsPrintPreview(bool aIsPrintPreview) 
@@ -2795,17 +2875,24 @@ nsPrintEngine::IsWindowsInOurSubTree(nsP
 
 //-------------------------------------------------------
 bool
 nsPrintEngine::DonePrintingPages(nsPrintObject* aPO, nsresult aResult)
 {
   //NS_ASSERTION(aPO, "Pointer is null!");
   PR_PL(("****** In DV::DonePrintingPages PO: %p (%s)\n", aPO, aPO?gFrameTypesStr[aPO->mFrameType]:""));
 
-  if (aPO != nullptr) {
+  // If there is a pageSeqFrame, make sure there are no more printCanvas active
+  // that might call |Notify| on the pagePrintTimer after things are cleaned up
+  // and printing was marked as being done.
+  if (mPageSeqFrame) {
+    mPageSeqFrame->ResetPrintCanvasList();
+  }
+
+  if (aPO && !mPrt->mIsAborted) {
     aPO->mHasBeenPrinted = true;
     nsresult rv;
     bool didPrint = PrintDocContent(mPrt->mPrintObject, rv);
     if (NS_SUCCEEDED(rv) && didPrint) {
       PR_PL(("****** In DV::DonePrintingPages PO: %p (%s) didPrint:%s (Not Done Printing)\n", aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint)));
       return false;
     }
   }
--- a/layout/printing/nsPrintEngine.h
+++ b/layout/printing/nsPrintEngine.h
@@ -8,16 +8,17 @@
 #include "mozilla/Attributes.h"
 
 #include "nsCOMPtr.h"
 
 #include "nsPrintObject.h"
 #include "nsPrintData.h"
 #include "nsFrameList.h"
 #include "mozilla/Attributes.h"
+#include "nsHTMLCanvasElement.h"
 
 // Interfaces
 #include "nsIDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIObserver.h"
 
 // Classes
 class nsPagePrintTimer;
@@ -98,16 +99,18 @@ public:
 
   void SetPrintPO(nsPrintObject* aPO, bool aPrint);
 
   void TurnScriptingOn(bool aDoTurnOn);
   bool CheckDocumentForPPCaching();
   void InstallPrintPreviewListener();
 
   // nsIDocumentViewerPrint Printing Methods
+  bool     HasPrintCallbackCanvas();
+  bool     PrePrintPage();
   bool     PrintPage(nsPrintObject* aPOect, bool& aInRange);
   bool     DonePrintingPages(nsPrintObject* aPO, nsresult aResult);
 
   //---------------------------------------------------------------------
   void BuildDocTree(nsIDocShellTreeNode *      aParentNode,
                     nsTArray<nsPrintObject*> * aDocList,
                     nsPrintObject *            aPO);
   nsresult ReflowDocList(nsPrintObject * aPO, bool aSetPixelScale);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/745025-1-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html class="reftest-print">
+<head>
+
+</head>
+<body>
+<!-- A 10x10 red image --><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFElEQVQYlWP4z8DwnxjMMKqQvgoBksPHOVp9kXEAAAAASUVORK5CYII=">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/printing/745025-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait reftest-print">
+<head>
+  <script type="text/javascript">
+    window.onload = function() { 
+      var canvas = document.getElementById('canvas');
+      canvas.mozPrintCallback = function(obj) {
+        var ctx = obj.context;
+        ctx.fillStyle = 'rgb(255, 0, 0)';
+        ctx.fillRect(0, 0, 10, 10);
+        obj.done();
+      };
+      setTimeout(function() {
+        document.documentElement.className = "reftest-print" 
+      }, 0);
+    };
+  </script>
+</head>
+<body>
+  <canvas id="canvas" width="10px" height="10px"></canvas>
+</body>
+</html>
--- a/layout/reftests/printing/reftest.list
+++ b/layout/reftests/printing/reftest.list
@@ -16,8 +16,9 @@
 == 626395-1a.html 626395-1-ref.html
 == 626395-1b.html 626395-1-ref.html
 == 626395-2a.html 626395-2-ref.html
 == 626395-2b.html 626395-2-ref.html
 == 626395-2c.html 626395-2-ref.html
 == 626395-2d.html 626395-2-ref.html
 == 652178-1.html 652178-1-ref.html
 == 652178-1.html 652178-1-ref2.html
+== 745025-1.html 745025-1-ref.html