Bug 379015: Fix nsPresShell::RenderOffscreen. r+sr=roc

This commit is contained in:
sharparrow1@yahoo.com 2007-05-22 17:11:05 -07:00
Родитель ed9bb3ac85
Коммит 14635c3543
4 изменённых файлов: 171 добавлений и 206 удалений

Просмотреть файл

@ -894,110 +894,6 @@ DocumentViewerImpl::InitInternal(nsIWidget* aParentWidget,
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)
//
@ -1092,17 +988,6 @@ DocumentViewerImpl::LoadComplete(nsresult aStatus)
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

Просмотреть файл

@ -97,14 +97,15 @@ 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
@ -672,7 +673,10 @@ public:
}
/**
* 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
@ -684,15 +688,12 @@ public:
* @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

Просмотреть файл

@ -199,6 +199,8 @@
// Content viewer interfaces
#include "nsIContentViewer.h"
#include "imgIEncoder.h"
#include "gfxPlatform.h"
#include "nsContentCID.h"
static NS_DEFINE_CID(kCSSStyleSheetCID, NS_CSS_STYLESHEET_CID);
@ -873,10 +875,10 @@ public:
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,
@ -2918,6 +2920,8 @@ nsIPresShell::GetRootScrollFrameAsScrollable() const
return nsnull;
nsIScrollableFrame* scrollableFrame = nsnull;
CallQueryInterface(frame, &scrollableFrame);
NS_ASSERTION(scrollableFrame,
"All scroll frames must implement nsIScrollableFrame");
return scrollableFrame;
}
@ -3082,6 +3086,7 @@ PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
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) {
@ -4633,83 +4638,76 @@ PresShell::ComputeRepaintRegionForCopy(nsIView* aRootView,
}
NS_IMETHODIMP
PresShell::RenderOffscreen(nsRect aRect, PRBool aUntrusted,
PRBool aIgnoreViewportScrolling,
nscolor aBackgroundColor,
nsIRenderingContext** aRenderedContext)
PresShell::RenderDocument(const nsRect& aRect, PRBool aUntrusted,
PRBool aIgnoreViewportScrolling,
nscolor aBackgroundColor,
gfxContext* aThebesContext)
{
nsIView* rootView;
mViewManager->GetRootView(rootView);
NS_ASSERTION(rootView, "No root view?");
nsIWidget* rootWidget = rootView->GetWidget();
NS_ASSERTION(rootWidget, "No root widget?");
NS_ENSURE_TRUE(!aUntrusted, NS_ERROR_NOT_IMPLEMENTED);
*aRenderedContext = nsnull;
aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ?
gfxASurface::CONTENT_COLOR :
gfxASurface::CONTENT_COLOR_ALPHA);
NS_ASSERTION(!aUntrusted, "We don't support untrusted yet");
if (aUntrusted)
return NS_ERROR_NOT_IMPLEMENTED;
aThebesContext->Save();
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;
// draw background color
if (NS_GET_A(aBackgroundColor) > 0) {
aThebesContext->SetColor(gfxRGBA(aBackgroundColor));
aThebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
aThebesContext->Paint();
}
// clipping and translation is set by CreateOffscreenContext
localcx->SetColor(aBackgroundColor);
localcx->FillRect(aRect);
// 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;
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();
}
}
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());
}
aThebesContext->Restore();
aThebesContext->PopGroupToSource();
aThebesContext->Paint();
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);
return NS_OK;
}
@ -6442,6 +6440,10 @@ CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame,
{
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;
@ -6706,12 +6708,16 @@ PresShell::CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult)
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);
@ -6729,6 +6735,66 @@ PresShell::CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult)
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
@ -6796,6 +6862,9 @@ PresShell::VerifyIncrementalReflow()
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");
}
@ -6818,6 +6887,25 @@ PresShell::VerifyIncrementalReflow()
}
}
#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) {

Просмотреть файл

@ -91,15 +91,6 @@ public:
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