Bug 379015: Fix nsPresShell::RenderOffscreen. r+sr=roc
authorsharparrow1@yahoo.com
Tue, 22 May 2007 17:11:05 -0700
changeset 1735 d21e306f5c17970277d4be29da98c069d8505b75
parent 1734 c369cffc9e6ac50ebd7c6d256b7784603b927f95
child 1736 718125b1104ac7963547decae83e1ec545c33c31
push idunknown
push userunknown
push dateunknown
bugs379015
milestone1.9a5pre
Bug 379015: Fix nsPresShell::RenderOffscreen. r+sr=roc
layout/base/nsDocumentViewer.cpp
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
view/public/nsIViewObserver.h
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -889,120 +889,16 @@ DocumentViewerImpl::InitInternal(nsIWidg
     // MakeWindow())...
 
     rv = InitPresentationStuff(!makeCX);
   }
 
   return rv;
 }
 
-void
-DocumentViewerImpl::DumpContentToPPM(const char* aFileName)
-{
-  mDocument->FlushPendingNotifications(Flush_Display);
-
-  nsIScrollableView* scrollableView;
-  mViewManager->GetRootScrollableView(&scrollableView);
-  nsIView* view;
-  if (scrollableView) {
-    scrollableView->GetScrolledView(view);
-  } else {
-    mViewManager->GetRootView(view);
-  }
-  nsRect r = view->GetBounds() - view->GetPosition();
-  // Limit the bitmap size to 5000x5000
-  nscoord twipLimit = mPresContext->DevPixelsToAppUnits(5000);
-  if (r.height > twipLimit)
-    r.height = twipLimit;
-  if (r.width > twipLimit)
-    r.width = twipLimit;
-
-  const char* status;
-
-  if (r.IsEmpty()) {
-    status = "EMPTY";
-  } else {
-    nsCOMPtr<nsIRenderingContext> context;
-    nsresult rv = mPresShell->RenderOffscreen(r, PR_FALSE, PR_TRUE, 
-                                              NS_RGB(255, 255, 255),
-                                              getter_AddRefs(context));
-
-    if (NS_FAILED(rv)) {
-      status = "FAILEDRENDER";
-    } else {
-      nsIDrawingSurface* surface;
-      context->GetDrawingSurface(&surface);
-      if (!surface) {
-        status = "NOSURFACE";
-      } else {
-        PRUint32 width = mPresContext->AppUnitsToDevPixels(view->GetBounds().width);
-        PRUint32 height = mPresContext->AppUnitsToDevPixels(view->GetBounds().height);
-
-        PRUint8* data;
-        PRInt32 rowLen, rowSpan;
-        rv = surface->Lock(0, 0, width, height, (void**)&data, &rowSpan, &rowLen,
-                           NS_LOCK_SURFACE_READ_ONLY);
-        if (NS_FAILED(rv)) {
-          status = "FAILEDLOCK";
-        } else {
-          PRUint32 bytesPerPix = rowLen/width;
-          nsPixelFormat format;
-          surface->GetPixelFormat(&format);
-
-          PRUint8* buf = new PRUint8[3*width];
-          if (buf) {
-            FILE* f = fopen(aFileName, "wb");
-            if (!f) {
-              status = "FOPENFAILED";
-            } else {
-              fprintf(f, "P6\n%d\n%d\n255\n", width, height);
-              for (PRUint32 i = 0; i < height; ++i) {
-                PRUint8* src = data + i*rowSpan;
-                PRUint8* dest = buf;
-                for (PRUint32 j = 0; j < width; ++j) {
-                  /* v is the pixel value */
-#ifdef IS_BIG_ENDIAN
-                  PRUint32 v = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
-                  v >>= (32 - 8*bytesPerPix);
-#else
-                  PRUint32 v = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-#endif
-                  dest[0] = ((v & format.mRedMask) >> format.mRedShift) << (8 - format.mRedCount);
-                  dest[1] = ((v & format.mGreenMask) >> format.mGreenShift) << (8 - format.mGreenCount);
-                  dest[2] = ((v & format.mBlueMask) >> format.mBlueShift) << (8 - format.mBlueCount);
-                  src += bytesPerPix;
-                  dest += 3;
-                }
-                fwrite(buf, 3, width, f);
-              }
-              fclose(f);
-              status = "OK";
-            }
-            
-            delete[] buf;
-          }
-          else {
-            status = "OOM";
-          }
-          surface->Unlock();
-        }
-        context->DestroyDrawingSurface(surface);
-      }
-    }
-  }
-
-  nsIURI *uri = mDocument->GetDocumentURI();
-  nsCAutoString spec;
-  if (uri) {
-    uri->GetAsciiSpec(spec);
-  }
-  printf("GECKO: PAINT FORCED AFTER ONLOAD: %s %s (%s)\n", spec.get(), aFileName, status);
-  fflush(stdout);
-}
-
 //
 // LoadComplete(aStatus)
 //
 //   aStatus - The status returned from loading the document.
 //
 // This method is called by the container when the document has been
 // completely loaded.
 //
@@ -1087,27 +983,16 @@ DocumentViewerImpl::LoadComplete(nsresul
 
   // Now that the document has loaded, we can tell the presshell
   // to unsuppress painting.
   if (mPresShell && !mStopped) {
     nsCOMPtr<nsIPresShell> shellDeathGrip(mPresShell); // bug 378682
     mPresShell->UnsuppressPainting();
   }
 
-  static PRBool forcePaint
-    = PR_GetEnv("MOZ_FORCE_PAINT_AFTER_ONLOAD") != nsnull;
-  static PRUint32 index = 0;
-  if (forcePaint) {
-    nsCAutoString name(PR_GetEnv("MOZ_FORCE_PAINT_AFTER_ONLOAD"));
-    name.AppendLiteral("-");
-    ++index;
-    name.AppendInt(index);
-    DumpContentToPPM(name.get());
-  }
-
   nsJSContext::LoadEnd();
 
 #ifdef NS_PRINTING
   // Check to see if someone tried to print during the load
   if (mPrintIsPending) {
     mPrintIsPending        = PR_FALSE;
     mPrintDocIsFullyLoaded = PR_TRUE;
     Print(mCachedPrintSettings, mCachedPrintWebProgressListner);
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -92,24 +92,25 @@ class nsIRegion;
 class nsIStyleFrameConstruction;
 class nsIStyleSheet;
 class nsCSSFrameConstructor;
 class nsISelection;
 template<class E> class nsCOMArray;
 class nsWeakFrame;
 class nsIScrollableFrame;
 class gfxASurface;
+class gfxContext;
 
 typedef short SelectionType;
 typedef PRUint32 nsFrameState;
 
-// DC543B71-6F1A-4B9F-B4CF-693AEC4BA24A
+// 9562bb2b-990c-4875-aafd-bd46fc9a4fc1
 #define NS_IPRESSHELL_IID \
-{ 0xdc543b71, 0x6f1a, 0x4b9f, \
-  { 0xb4, 0xcf, 0x69, 0x3a, 0xec, 0x4b, 0xa2, 0x4a } }
+{ 0x9562bb2b, 0x990c, 0x4875, \
+  { 0xaa, 0xfd, 0xbd, 0x46, 0xfc, 0x9a, 0x4f, 0xc1 } }
 
 // Constants for ScrollContentIntoView() function
 #define NS_PRESSHELL_SCROLL_TOP      0
 #define NS_PRESSHELL_SCROLL_BOTTOM   100
 #define NS_PRESSHELL_SCROLL_LEFT     0
 #define NS_PRESSHELL_SCROLL_RIGHT    100
 #define NS_PRESSHELL_SCROLL_CENTER   50
 #define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -667,37 +668,37 @@ public:
    * It should not be used for any other purpose.
    */
   void SetForwardingContainer(nsWeakPtr aContainer)
   {
     mForwardingContainer = aContainer;
   }
   
   /**
-   * Dump window contents into a new offscreen rendering context.
+   * Render the document into an arbitrary gfxContext
+   * Designed for getting a picture of a document or a piece of a document
+   * Note that callers will generally want to call FlushPendingNotifications
+   * to get an up-to-date view of the document
    * @param aRect is the region to capture into the offscreen buffer, in the
    * root frame's coordinate system (if aIgnoreViewportScrolling is false)
    * or in the root scrolled frame's coordinate system
    * (if aIgnoreViewportScrolling is true)
    * @param aUntrusted set to PR_TRUE if the contents may be passed to malicious
    * agents. E.g. we might choose not to paint the contents of sensitive widgets
    * such as the file name in a file upload widget, and we might choose not
    * to paint themes.
    * @param aIgnoreViewportScrolling ignore clipping/scrolling/scrollbar painting
    * due to scrolling in the viewport
    * @param aBackgroundColor a background color to render onto
-   * @param aRenderedContext gets set to a rendering context whose offscreen
-   * buffer can be locked to get the data. The buffer's size will be aRect's size.
-   * In all cases the caller must clean it up by calling
-   * cx->DestroyDrawingSurface(cx->GetDrawingSurface()).
+   * @param aRenderedContext the gfxContext to render to
    */
-  NS_IMETHOD RenderOffscreen(nsRect aRect, PRBool aUntrusted,
-                             PRBool aIgnoreViewportScrolling,
-                             nscolor aBackgroundColor,
-                             nsIRenderingContext** aRenderedContext) = 0;
+  NS_IMETHOD RenderDocument(const nsRect& aRect, PRBool aUntrusted,
+                            PRBool aIgnoreViewportScrolling,
+                            nscolor aBackgroundColor,
+                            gfxContext* aRenderedContext) = 0;
 
   /**
    * Renders a node aNode to a surface and returns it. The aRegion may be used
    * to clip the rendering. This region is measured in device pixels from the
    * edge of the presshell area. The aPoint, aScreenRect and aSurface
    * arguments function in a similar manner as RenderSelection.
    */
   virtual already_AddRefed<gfxASurface> RenderNode(nsIDOMNode* aNode,
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -194,16 +194,18 @@
 #include "nsIMenuFrame.h"
 #include "nsITreeBoxObject.h"
 #endif
 #include "nsIMenuParent.h"
 #include "nsPlaceholderFrame.h"
 
 // Content viewer interfaces
 #include "nsIContentViewer.h"
+#include "imgIEncoder.h"
+#include "gfxPlatform.h"
 
 #include "nsContentCID.h"
 static NS_DEFINE_CID(kCSSStyleSheetCID, NS_CSS_STYLESHEET_CID);
 static NS_DEFINE_IID(kRangeCID,     NS_RANGE_CID);
 
 PRBool nsIPresShell::gIsAccessibilityActive = PR_FALSE;
 
 // convert a color value to a string, in the CSS format #RRGGBB
@@ -868,20 +870,20 @@ public:
   NS_IMETHOD GetEventTargetContent(nsEvent* aEvent, nsIContent** aContent);
 
   NS_IMETHOD IsReflowLocked(PRBool* aIsLocked);  
 
   virtual nsresult ReconstructFrames(void);
   virtual void Freeze();
   virtual void Thaw();
   
-  NS_IMETHOD RenderOffscreen(nsRect aRect, PRBool aUntrusted,
-                             PRBool aIgnoreViewportScrolling,
-                             nscolor aBackgroundColor,
-                             nsIRenderingContext** aRenderedContext);
+  NS_IMETHOD RenderDocument(const nsRect& aRect, PRBool aUntrusted,
+                            PRBool aIgnoreViewportScrolling,
+                            nscolor aBackgroundColor,
+                            gfxContext* aThebesContext);
 
   virtual already_AddRefed<gfxASurface> RenderNode(nsIDOMNode* aNode,
                                                    nsIRegion* aRegion,
                                                    nsPoint& aPoint,
                                                    nsRect* aScreenRect);
 
   virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
                                                         nsPoint& aPoint,
@@ -2913,16 +2915,18 @@ nsIPresShell::GetRootScrollFrame() const
 nsIScrollableFrame*
 nsIPresShell::GetRootScrollFrameAsScrollable() const
 {
   nsIFrame* frame = GetRootScrollFrame();
   if (!frame)
     return nsnull;
   nsIScrollableFrame* scrollableFrame = nsnull;
   CallQueryInterface(frame, &scrollableFrame);
+  NS_ASSERTION(scrollableFrame,
+               "All scroll frames must implement nsIScrollableFrame");
   return scrollableFrame;
 }
 
 NS_IMETHODIMP
 PresShell::GetPageSequenceFrame(nsIPageSequenceFrame** aResult) const
 {
   NS_PRECONDITION(nsnull != aResult, "null ptr");
   if (nsnull == aResult) {
@@ -3077,16 +3081,17 @@ PresShell::FrameNeedsReflow(nsIFrame *aF
   if (mIsDestroying)
     return NS_OK;
 
 #ifdef DEBUG
   //printf("gShellCounter: %d\n", gShellCounter++);
   if (mInVerifyReflow) {
     return NS_OK;
   }
+
   if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
     printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame);
     if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
       printf("Current content model:\n");
       nsIContent *rootContent = mDocument->GetRootContent();
       if (rootContent) {
         rootContent->List(stdout, 0);
       }
@@ -4628,93 +4633,86 @@ PresShell::ComputeRepaintRegionForCopy(n
 {
   return nsLayoutUtils::ComputeRepaintRegionForCopy(
       NS_STATIC_CAST(nsIFrame*, aRootView->GetClientData()),
       NS_STATIC_CAST(nsIFrame*, aMovingView->GetClientData()),
       aDelta, aCopyRect, aRepaintRegion);
 }
 
 NS_IMETHODIMP
-PresShell::RenderOffscreen(nsRect aRect, PRBool aUntrusted,
-                           PRBool aIgnoreViewportScrolling,
-                           nscolor aBackgroundColor,
-                           nsIRenderingContext** aRenderedContext)
-{
-  nsIView* rootView;
-  mViewManager->GetRootView(rootView);
-  NS_ASSERTION(rootView, "No root view?");
-  nsIWidget* rootWidget = rootView->GetWidget();
-  NS_ASSERTION(rootWidget, "No root widget?");
-
-  *aRenderedContext = nsnull;
-
-  NS_ASSERTION(!aUntrusted, "We don't support untrusted yet");
-  if (aUntrusted)
-    return NS_ERROR_NOT_IMPLEMENTED;
-
-  nsCOMPtr<nsIRenderingContext> tmpContext;
-  mPresContext->DeviceContext()->CreateRenderingContext(rootWidget, 
-      *getter_AddRefs(tmpContext));
-  if (!tmpContext)
-    return NS_ERROR_FAILURE;
-
-  nsRect bounds(nsPoint(0, 0), aRect.Size());
-  bounds.ScaleRoundOut(1.0f / mPresContext->AppUnitsPerDevPixel());
-  
-  nsIDrawingSurface* surface;
-  nsresult rv
-    = tmpContext->CreateDrawingSurface(bounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS,
-                                       surface);
-  if (NS_FAILED(rv))
-    return NS_ERROR_FAILURE;
-  nsCOMPtr<nsIRenderingContext> localcx;
-  rv = nsLayoutUtils::CreateOffscreenContext(mPresContext->DeviceContext(),
-      surface, aRect, getter_AddRefs(localcx));
-  if (NS_FAILED(rv)) {
-    tmpContext->DestroyDrawingSurface(surface);
-    return NS_ERROR_FAILURE;
-  }
-  // clipping and translation is set by CreateOffscreenContext
-
-  localcx->SetColor(aBackgroundColor);
-  localcx->FillRect(aRect);
+PresShell::RenderDocument(const nsRect& aRect, PRBool aUntrusted,
+                          PRBool aIgnoreViewportScrolling,
+                          nscolor aBackgroundColor,
+                          gfxContext* aThebesContext)
+{
+  NS_ENSURE_TRUE(!aUntrusted, NS_ERROR_NOT_IMPLEMENTED);
+
+  aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ?
+                            gfxASurface::CONTENT_COLOR :
+                            gfxASurface::CONTENT_COLOR_ALPHA);
+
+  aThebesContext->Save();
+
+  // draw background color
+  if (NS_GET_A(aBackgroundColor) > 0) {
+    aThebesContext->SetColor(gfxRGBA(aBackgroundColor));
+    aThebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
+    aThebesContext->Paint();
+  }
+
+  // we want the window to be composited as a single image using
+  // whatever operator was set, so set this to the default OVER;
+  // the original operator will be present when we PopGroup
+  aThebesContext->SetOperator(gfxContext::OPERATOR_OVER);
 
   nsIFrame* rootFrame = FrameManager()->GetRootFrame();
-  if (!rootFrame) {
-    localcx.swap(*aRenderedContext);
-    return NS_OK;
-  }
-  
-  nsDisplayListBuilder builder(rootFrame, PR_FALSE, PR_FALSE);
-  nsDisplayList list;
-  nsIScrollableView* scrollingView = nsnull;
-  mViewManager->GetRootScrollableView(&scrollingView);
-  nsRect r = aRect;
-  if (aIgnoreViewportScrolling && scrollingView) {
-    nscoord x, y;
-    scrollingView->GetScrollPosition(x, y);
-    localcx->Translate(x, y);
-    r.MoveBy(-x, -y);
-    builder.SetIgnoreScrollFrame(GetRootScrollFrame());
-  }
-
-  builder.EnterPresShell(rootFrame, r);
-
-  rv = rootFrame->BuildDisplayListForStackingContext(&builder, r, &list);
-
-  builder.LeavePresShell(rootFrame, r);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRegion region(r);
-  list.OptimizeVisibility(&builder, &region);
-  list.Paint(&builder, localcx, r);
-  // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
-  list.DeleteAll();
-
-  localcx.swap(*aRenderedContext);
+  if (rootFrame) {
+    nsDisplayListBuilder builder(rootFrame, PR_FALSE, PR_FALSE);
+    nsDisplayList list;
+
+    nsRect rect(aRect);
+    nsIFrame* rootScrollFrame = GetRootScrollFrame();
+    if (aIgnoreViewportScrolling && rootScrollFrame) {
+      nsPoint pos = GetRootScrollFrameAsScrollable()->GetScrollPosition();
+      rect.MoveBy(-pos);
+      builder.SetIgnoreScrollFrame(rootScrollFrame);
+    }
+
+    builder.EnterPresShell(rootFrame, rect);
+
+    nsresult rv = rootFrame->BuildDisplayListForStackingContext(&builder, rect, &list);   
+
+    builder.LeavePresShell(rootFrame, rect);
+
+    if (NS_SUCCEEDED(rv)) {
+      nscoord appUnitsPerDevPixel = mPresContext->AppUnitsPerDevPixel();
+      // Ensure that r.x,r.y gets drawn at (0,0)
+      aThebesContext->Save();
+      aThebesContext->Translate(gfxPoint(-NSAppUnitsToFloatPixels(rect.x,appUnitsPerDevPixel),
+                                         -NSAppUnitsToFloatPixels(rect.y,appUnitsPerDevPixel)));
+
+      nsIDeviceContext* devCtx = mPresContext->DeviceContext();
+      nsCOMPtr<nsIRenderingContext> rc;
+      devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
+      rc->Init(devCtx, aThebesContext);
+
+      nsRegion region(rect);
+      list.OptimizeVisibility(&builder, &region);
+      list.Paint(&builder, rc, rect);
+      // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
+      list.DeleteAll();
+
+      aThebesContext->Restore();
+    }
+  }
+
+  aThebesContext->Restore();
+  aThebesContext->PopGroupToSource();
+  aThebesContext->Paint();
+
   return NS_OK;
 }
 
 /*
  * Clip the display list aList to a range. Returns the clipped
  * rectangle surrounding the range.
  */
 nsRect
@@ -6437,16 +6435,20 @@ LogVerifyMessage(nsIFrame* k1, nsIFrame*
 }
 
 static PRBool
 CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame, 
              nsPresContext* aSecondPresContext, nsIFrame* aSecondFrame)
 {
   if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext || !aSecondFrame)
     return PR_TRUE;
+  // XXX Evil hack to reduce false positives; I can't seem to figure
+  // out how to flush scrollbar changes correctly
+  //if (aFirstFrame->GetType() == nsGkAtoms::scrollbarFrame)
+  //  return PR_TRUE;
   PRBool ok = PR_TRUE;
   nsIAtom* listName = nsnull;
   PRInt32 listIndex = 0;
   do {
     nsIFrame* k1 = aFirstFrame->GetFirstChild(listName);
     nsIFrame* k2 = aSecondFrame->GetFirstChild(listName);
     PRInt32 l1 = nsContainerFrame::LengthOf(k1);
     PRInt32 l2 = nsContainerFrame::LengthOf(k2);
@@ -6701,22 +6703,26 @@ PresShell::CloneStyleSet(nsStyleSet* aSe
 
   PRInt32 i, n = aSet->SheetCount(nsStyleSet::eOverrideSheet);
   for (i = 0; i < n; i++) {
     nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eOverrideSheet, i);
     if (ss)
       clone->AppendStyleSheet(nsStyleSet::eOverrideSheet, ss);
   }
 
+  // The document expects to insert document stylesheets itself
+#if 0
   n = aSet->SheetCount(nsStyleSet::eDocSheet);
   for (i = 0; i < n; i++) {
     nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eDocSheet, i);
     if (ss)
       clone->AddDocStyleSheet(ss, mDocument);
   }
+#endif
+
   n = aSet->SheetCount(nsStyleSet::eUserSheet);
   for (i = 0; i < n; i++) {
     nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eUserSheet, i);
     if (ss)
       clone->AppendStyleSheet(nsStyleSet::eUserSheet, ss);
   }
 
   n = aSet->SheetCount(nsStyleSet::eAgentSheet);
@@ -6724,16 +6730,76 @@ PresShell::CloneStyleSet(nsStyleSet* aSe
     nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
     if (ss)
       clone->AppendStyleSheet(nsStyleSet::eAgentSheet, ss);
   }
   *aResult = clone;
   return NS_OK;
 }
 
+#ifdef DEBUG_Eli
+static nsresult
+DumpToPNG(nsIPresShell* shell, nsAString& name) {
+  PRInt32 width=1000, height=1000;
+  nsRect r(0, 0, shell->GetPresContext()->DevPixelsToAppUnits(width),
+                 shell->GetPresContext()->DevPixelsToAppUnits(height));
+
+  nsRefPtr<gfxImageSurface> imgSurface =
+     new gfxImageSurface(gfxIntSize(width, height),
+                         gfxImageSurface::ImageFormatARGB32);
+  NS_ENSURE_TRUE(imgSurface, NS_ERROR_OUT_OF_MEMORY);
+
+  nsRefPtr<gfxContext> imgContext = new gfxContext(imgSurface);
+
+  nsRefPtr<gfxASurface> surface = 
+    gfxPlatform::GetPlatform()->
+    CreateOffscreenSurface(gfxIntSize(width, height),
+      gfxASurface::ImageFormatARGB32);
+  NS_ENSURE_TRUE(surface, NS_ERROR_OUT_OF_MEMORY);
+
+  nsRefPtr<gfxContext> context = new gfxContext(surface);
+  NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = shell->RenderDocument(r, PR_FALSE, PR_FALSE,
+                                      NS_RGB(255, 255, 0), context);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  imgContext->DrawSurface(surface, gfxSize(width, height));
+
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
+  NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE);
+  encoder->InitFromData(imgSurface->Data(), imgSurface->Stride() * height,
+                        width, height, imgSurface->Stride(),
+                        imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString());
+
+  // XXX not sure if this is the right way to write to a file
+  nsCOMPtr<nsILocalFile> file = do_CreateInstance("@mozilla.org/file/local;1");
+  NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
+  rv = file->InitWithPath(name);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 length;
+  encoder->Available(&length);
+
+  nsCOMPtr<nsIOutputStream> outputStream;
+  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIOutputStream> bufferedOutputStream;
+  rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
+                                  outputStream, length);
+
+  PRUint32 numWritten;
+  rv = bufferedOutputStream->WriteFrom(encoder, length, &numWritten);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+#endif
+
 // After an incremental reflow, we verify the correctness by doing a
 // full reflow into a fresh frame tree.
 PRBool
 PresShell::VerifyIncrementalReflow()
 {
    if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
      printf("Building Verification Tree...\n");
    }
@@ -6791,16 +6857,19 @@ PresShell::VerifyIncrementalReflow()
   sh->SetVerifyReflowEnable(PR_FALSE); // turn off verify reflow while we're reflowing the test frame tree
   vm->SetViewObserver((nsIViewObserver *)((PresShell*)sh.get()));
   WillCauseReflow();
   sh->InitialReflow(r.width, r.height);
   DidCauseReflow();
   mDocument->BindingManager()->ProcessAttachedQueue();
   sh->FlushPendingNotifications(Flush_Layout);
   sh->SetVerifyReflowEnable(PR_TRUE);  // turn on verify reflow again now that we're done reflowing the test frame tree
+  // Force the non-primary presshell to unsuppress; it doesn't want to normally
+  // because it thinks it's hidden
+  ((PresShell*)sh.get())->mPaintingSuppressed = PR_FALSE;
   if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
      printf("Verification Tree built, comparing...\n");
   }
 
   // Now that the document has been reflowed, use its frame tree to
   // compare against our frame tree.
   nsIFrame* root1 = FrameManager()->GetRootFrame();
   nsIFrame* root2 = sh->FrameManager()->GetRootFrame();
@@ -6813,16 +6882,35 @@ PresShell::VerifyIncrementalReflow()
       frameDebug->List(stdout, 0);
     }
     printf("Verification tree:\n");
     if (NS_SUCCEEDED(CallQueryInterface(root2, &frameDebug))) {
       frameDebug->List(stdout, 0);
     }
   }
 
+#ifdef DEBUG_Eli
+  // Sample code for dumping page to png
+  // XXX Needs to be made more flexible
+  if (!ok) {
+    nsString stra;
+    static int num = 0;
+    stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
+    stra.AppendInt(num);
+    stra.AppendLiteral(".png");
+    DumpToPNG(sh, stra);
+    nsString strb;
+    strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
+    strb.AppendInt(num);
+    strb.AppendLiteral(".png");
+    DumpToPNG(this, strb);
+    ++num;
+  }
+#endif
+
   sh->EndObservingDocument();
   sh->Destroy();
   if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
     printf("Finished Verifying Reflow...\n");
   }
 
   return ok;
 }
--- a/view/public/nsIViewObserver.h
+++ b/view/public/nsIViewObserver.h
@@ -86,25 +86,16 @@ public:
    * @param aHandled - whether the correct frame was found to
    *                   handle the event
    * @return error status
    */
   NS_IMETHOD HandleEvent(nsIView*       aView,
                          nsGUIEvent*    aEvent,
                          nsEventStatus* aEventStatus) = 0;
 
-  /**
-   * This is temporary until nsIViewManager::RenderOffscreen goes away (which
-   * will happen when views, and hence this entire interface, go away!).
-   */
-  NS_IMETHOD RenderOffscreen(nsRect aRect, PRBool aUntrusted,
-                             PRBool aIgnoreViewportScrolling,
-                             nscolor aBackgroundColor,
-                             nsIRenderingContext** aRenderedContext) = 0;
-
   /* called when the view has been resized and the
    * content within the view needs to be reflowed.
    * @param aWidth - new width of view
    * @param aHeight - new height of view
    * @return error status
    */
   NS_IMETHOD ResizeReflow(nsIView * aView, nscoord aWidth, nscoord aHeight) = 0;