Bug 285038. Support offscreen rendering API in nsIViewManager and provide MOZ_FORCE_PAINT_AFTER_ONLOAD environment variable hack to dump loaded documents to image files. r+sr=bzbarsky

This commit is contained in:
roc+%cs.cmu.edu 2005-03-07 01:24:22 +00:00
Родитель a3d6c24c4f
Коммит c8d3df631f
6 изменённых файлов: 232 добавлений и 34 удалений

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

@ -111,6 +111,7 @@
#ifdef MOZ_XUL
#include "nsIXULDocument.h"
#endif
#include "nsPrintfCString.h"
#include "nsIClipboardHelper.h"
@ -359,6 +360,8 @@ private:
nsresult GetPopupLinkNode(nsIDOMNode** aNode);
nsresult GetPopupImageNode(nsIImageLoadingContent** aNode);
void DumpContentToPPM(const char* aFileName);
void PrepareToStartLoad(void);
nsresult SyncParentSubDocMap();
@ -845,6 +848,107 @@ 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();
float p2t = mPresContext->PixelsToTwips();
// Limit the bitmap size to 5000x5000
nscoord twipLimit = NSIntPixelsToTwips(5000, p2t);
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 = mViewManager->RenderOffscreen(view, r,
PR_FALSE, 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 {
float t2p = mPresContext->TwipsToPixels();
PRUint32 width = NSTwipsToIntPixels(view->GetBounds().width, t2p);
PRUint32 height = NSTwipsToIntPixels(view->GetBounds().height, t2p);
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) {
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;
}
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)
//
@ -912,15 +1016,13 @@ DocumentViewerImpl::LoadComplete(nsresult aStatus)
static PRBool forcePaint
= PR_GetEnv("MOZ_FORCE_PAINT_AFTER_ONLOAD") != nsnull;
static PRUint32 index = 0;
if (forcePaint) {
mDocument->FlushPendingNotifications(Flush_Display);
nsIURI *uri = mDocument->GetDocumentURI();
nsCAutoString spec;
if (uri) {
uri->GetSpec(spec);
}
printf("GECKO: PAINT FORCED AFTER ONLOAD: %s\n", spec.get());
fflush(stdout);
nsCAutoString name(PR_GetEnv("MOZ_FORCE_PAINT_AFTER_ONLOAD"));
name.AppendLiteral("-");
++index;
name.AppendInt(index);
DumpContentToPPM(name.get());
}
#ifdef NS_PRINTING

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

@ -449,6 +449,27 @@ public:
//XXXbz how is this different from UpdateView(NS_VMREFRESH_IMMEDIATE)?
NS_IMETHOD Display(nsIView *aView, nscoord aX, nscoord aY, const nsRect& aClipRect) = 0;
/**
* Dump the specified view into a new offscreen rendering context.
* The view is treated as the display root and is not clipped by
* ancestor views, nor is ancestor or sibling content displayed even
* if it overlaps the view.
* @param aRect is the region to capture into the offscreen buffer, in the view's
* coordinate system
* @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 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()).
*/
NS_IMETHOD RenderOffscreen(nsIView* aView, nsRect aRect, PRBool aUntrusted,
nscolor aBackgroundColor,
nsIRenderingContext** aRenderedContext) = 0;
/**
* Add a listener to the view manager's composite listener list.
* @param aListener - new listener

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

@ -856,15 +856,20 @@ PRBool nsIView::ExternalIsRoot() const
return nsIView::IsRoot();
}
// Returns PR_TRUE iff we found a placeholder parent edge on the way
static PRBool ApplyClipRect(const nsView* aView, nsRect* aRect, PRBool aFollowPlaceholders)
/**
* @param aStopAtView clipping from ancestors of this view will not be applied
* @return PR_TRUE iff we found a placeholder parent edge on the way up the view tree
* from aView to aStopAtView
*/
static PRBool ApplyClipRect(const nsView* aView, nsRect* aRect, PRBool aFollowPlaceholders,
nsIView* aStopAtView)
{
// this records the offset from the origin of the current aView
// to the origin of the initial aView
nsPoint offset(0, 0);
PRBool lastViewIsFloating = aView->GetFloating();
PRBool foundPlaceholders = PR_FALSE;
while (PR_TRUE) {
while (aView != aStopAtView) {
const nsView* parentView = aView->GetParent();
nsPoint offsetFromParent = aView->GetPosition();
@ -874,7 +879,7 @@ static PRBool ApplyClipRect(const nsView* aView, nsRect* aRect, PRBool aFollowPl
if (aFollowPlaceholders) {
// correct offsetFromParent to account for the fact that we're
// switching parentView to ZParent
// Note tha the common case is that parentView is an ancestor of
// Note that the common case is that parentView is an ancestor of
// ZParent.
const nsView* zParentChain;
for (zParentChain = zParent;
@ -934,12 +939,12 @@ static PRBool ApplyClipRect(const nsView* aView, nsRect* aRect, PRBool aFollowPl
return foundPlaceholders;
}
nsRect nsView::GetClippedRect()
nsRect nsView::GetClippedRect(nsIView* aStopAtView)
{
nsRect clip = GetDimensions();
PRBool foundPlaceholders = ApplyClipRect(this, &clip, PR_FALSE);
PRBool foundPlaceholders = ApplyClipRect(this, &clip, PR_FALSE, aStopAtView);
if (foundPlaceholders && !clip.IsEmpty()) {
ApplyClipRect(this, &clip, PR_TRUE);
ApplyClipRect(this, &clip, PR_TRUE, aStopAtView);
}
return clip;
}

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

@ -226,8 +226,10 @@ public:
/**
* @return the view's dimensions after clipping by ancestors is applied
* (the rect is relative to the view's origin)
* @param aStopAtView do not consider clipping imposed by views above this view
* on the ancestor chain
*/
nsRect GetClippedRect();
nsRect GetClippedRect(nsIView* aStopAtView = nsnull);
// Helper function to get the view that's associated with a widget
static nsView* GetViewFor(nsIWidget* aWidget);

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

@ -884,7 +884,8 @@ void nsViewManager::Refresh(nsView *aView, nsIRenderingContext *aContext,
PLArenaPool displayArena;
PL_INIT_ARENA_POOL(&displayArena, "displayArena", 1024);
PRBool anyTransparentPixels
= BuildRenderingDisplayList(aView, damageRegion, &displayList, displayArena);
= BuildRenderingDisplayList(aView, damageRegion, &displayList, displayArena,
PR_FALSE, PR_FALSE);
PRBool needBlending = PR_FALSE;
for (PRInt32 i = 0; i < displayList.Count(); i++) {
DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, displayList.ElementAt(i));
@ -1288,14 +1289,18 @@ void nsViewManager::AddCoveringWidgetsToOpaqueRegion(nsRegion &aRgn, nsIDeviceCo
}
PRBool nsViewManager::BuildRenderingDisplayList(nsIView* aRootView,
const nsRegion& aRegion, nsVoidArray* aDisplayList, PLArenaPool &aPool)
const nsRegion& aRegion, nsVoidArray* aDisplayList, PLArenaPool &aPool,
PRBool aIgnoreCoveringWidgets, PRBool aIgnoreOutsideClipping)
{
BuildDisplayList(NS_STATIC_CAST(nsView*, aRootView),
aRegion.GetBounds(), PR_FALSE, PR_FALSE, aDisplayList, aPool);
aRegion.GetBounds(), PR_FALSE, aIgnoreOutsideClipping,
aDisplayList, aPool);
nsRegion opaqueRgn;
AddCoveringWidgetsToOpaqueRegion(opaqueRgn, mContext,
NS_STATIC_CAST(nsView*, aRootView));
if (!aIgnoreCoveringWidgets) {
AddCoveringWidgetsToOpaqueRegion(opaqueRgn, mContext,
NS_STATIC_CAST(nsView*, aRootView));
}
nsRect finalTransparentRect;
OptimizeDisplayList(aDisplayList, aRegion, finalTransparentRect, opaqueRgn, PR_FALSE);
@ -2332,9 +2337,11 @@ static PRBool ComputePlaceholderContainment(nsView* aView) {
aRect is the area in aView which we want to build a display list for.
Set aEventProcesing when the list is required for event processing.
Set aCaptured if the event is being captured by the given view.
Set aCaptured if the event or painting is being captured by the given view so
only views that are descended from the given view are considered.
*/
void nsViewManager::BuildDisplayList(nsView* aView, const nsRect& aRect, PRBool aEventProcessing,
void nsViewManager::BuildDisplayList(nsView* aView, const nsRect& aRect,
PRBool aEventProcessing,
PRBool aCaptured, nsVoidArray* aDisplayList,
PLArenaPool &aPool)
{
@ -3325,7 +3332,7 @@ nsViewManager::CreateRenderingContext(nsView &aView)
if (nsnull != win)
{
mContext->CreateRenderingContext(&aView, cx);
mContext->CreateRenderingContext(par, cx);
if (nsnull != cx)
cx->Translate(ax, ay);
@ -3437,6 +3444,54 @@ NS_IMETHODIMP nsViewManager::GetRootScrollableView(nsIScrollableView **aScrollab
return NS_OK;
}
NS_IMETHODIMP nsViewManager::RenderOffscreen(nsIView* aView, nsRect aRect,
PRBool aUntrusted,
nscolor aBackgroundColor,
nsIRenderingContext** aRenderedContext)
{
nsView* view = NS_STATIC_CAST(nsView*, aView);
*aRenderedContext = nsnull;
NS_ASSERTION(!aUntrusted, "We don't support untrusted yet");
if (aUntrusted)
return NS_ERROR_NOT_IMPLEMENTED;
nsCOMPtr<nsIRenderingContext> tmpContext
= CreateRenderingContext(*view);
if (!tmpContext)
return NS_ERROR_FAILURE;
nsRect bounds(nsPoint(0, 0), aRect.Size());
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 = NewOffscreenContext(mContext, surface, aRect, getter_AddRefs(localcx));
if (NS_FAILED(rv)) {
tmpContext->DestroyDrawingSurface(surface);
return NS_ERROR_FAILURE;
}
// clipping and translation is set by NewOffscreenContext
localcx->SetColor(aBackgroundColor);
localcx->FillRect(aRect);
nsAutoVoidArray displayList;
PLArenaPool displayArena;
PL_INIT_ARENA_POOL(&displayArena, "displayArena", 1024);
BuildRenderingDisplayList(view, nsRegion(aRect), &displayList, displayArena,
PR_TRUE, PR_TRUE);
RenderViews(view, *localcx, nsRegion(aRect), surface, displayList);
PL_FreeArenaPool(&displayArena);
PL_FinishArenaPool(&displayArena);
localcx.swap(*aRenderedContext);
}
NS_IMETHODIMP nsViewManager::Display(nsIView* aView, nscoord aX, nscoord aY, const nsRect& aClipRect)
{
nsView *view = NS_STATIC_CAST(nsView*, aView);
@ -3470,7 +3525,8 @@ NS_IMETHODIMP nsViewManager::Display(nsIView* aView, nscoord aX, nscoord aY, con
nsAutoVoidArray displayList;
PLArenaPool displayArena;
PL_INIT_ARENA_POOL(&displayArena, "displayArena", 1024);
BuildRenderingDisplayList(view, nsRegion(trect), &displayList, displayArena);
BuildRenderingDisplayList(view, nsRegion(trect), &displayList, displayArena,
PR_FALSE, PR_FALSE);
RenderViews(view, *localcx, nsRegion(trect), PR_FALSE, displayList);
PL_FreeArenaPool(&displayArena);
PL_FinishArenaPool(&displayArena);
@ -3650,7 +3706,8 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView,
// Add POP first because the z-tree is in reverse order
retval = AddToDisplayList(aView, aResult, bounds, bounds,
POP_FILTER, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool);
POP_FILTER, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool,
aTopView);
if (retval)
return retval;
@ -3667,7 +3724,8 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView,
// Add POP first because the z-tree is in reverse order
retval = AddToDisplayList(aView, aResult, bounds, bounds,
POP_CLIP, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool);
POP_CLIP, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool,
aTopView);
if (retval)
return retval;
@ -3710,7 +3768,8 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView,
flags |= VIEW_TRANSPARENT;
retval = AddToDisplayList(aView, aResult, bounds, irect, flags,
aX - aOriginX, aY - aOriginY,
aEventProcessing && aTopView == aView, aPool);
aEventProcessing && aTopView == aView, aPool,
aTopView);
// We're forcing AddToDisplayList to pick up the view only
// during event processing, and only when aView is back at the
// root of the tree of acceptable views (note that when event
@ -3735,7 +3794,8 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView,
bounds.y -= aOriginY;
if (AddToDisplayList(aView, aResult, bounds, bounds, PUSH_CLIP,
aX - aOriginX, aY - aOriginY, PR_TRUE, aPool)) {
aX - aOriginX, aY - aOriginY, PR_TRUE, aPool,
aTopView)) {
retval = PR_TRUE;
}
@ -3750,7 +3810,8 @@ PRBool nsViewManager::CreateDisplayList(nsView *aView,
bounds.y -= aOriginY;
retval = AddToDisplayList(aView, aResult, bounds, bounds,
PUSH_FILTER, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool);
PUSH_FILTER, aX - aOriginX, aY - aOriginY, PR_TRUE, aPool,
aTopView);
if (retval)
return retval;
@ -3766,9 +3827,10 @@ PRBool nsViewManager::AddToDisplayList(nsView *aView,
DisplayZTreeNode* &aParent, nsRect &aClipRect,
nsRect& aDirtyRect, PRUint32 aFlags,
nscoord aAbsX, nscoord aAbsY,
PRBool aAssumeIntersection, PLArenaPool &aPool)
PRBool aAssumeIntersection, PLArenaPool &aPool,
nsIView* aStopClippingAtView)
{
nsRect clipRect = aView->GetClippedRect();
nsRect clipRect = aView->GetClippedRect(aStopClippingAtView);
PRBool clipped = clipRect != aView->GetDimensions();
// get clipRect into the coordinate system of aView's parent. aAbsX and

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

@ -231,6 +231,10 @@ public:
NS_IMETHOD Display(nsIView *aView, nscoord aX, nscoord aY, const nsRect& aClipRect);
NS_IMETHOD RenderOffscreen(nsIView* aView, nsRect aRect, PRBool aUntrusted,
nscolor aBackgroundColor,
nsIRenderingContext** aRenderedContext);
NS_IMETHOD AddCompositeListener(nsICompositeListener *aListener);
NS_IMETHOD RemoveCompositeListener(nsICompositeListener *aListener);
@ -286,7 +290,8 @@ private:
*/
void DefaultRefresh(nsView* aView, const nsRect* aRect);
PRBool BuildRenderingDisplayList(nsIView* aRootView,
const nsRegion& aRegion, nsVoidArray* aDisplayList, PLArenaPool &aPool);
const nsRegion& aRegion, nsVoidArray* aDisplayList, PLArenaPool &aPool,
PRBool aIgnoreCoveringWidgets, PRBool aIgnoreOutsideClipping);
void RenderViews(nsView *aRootView, nsIRenderingContext& aRC,
const nsRegion& aRegion, nsIDrawingSurface* aRCSurface,
const nsVoidArray& aDisplayList);
@ -320,7 +325,8 @@ private:
PRBool AddToDisplayList(nsView *aView,
DisplayZTreeNode* &aParent, nsRect &aClipRect,
nsRect& aDirtyRect, PRUint32 aFlags, nscoord aAbsX, nscoord aAbsY,
PRBool aAssumeIntersection, PLArenaPool &aPool);
PRBool aAssumeIntersection, PLArenaPool &aPool,
nsIView* aStopClippingAtView);
void OptimizeDisplayList(const nsVoidArray* aDisplayList, const nsRegion& aDirtyRegion,
nsRect& aFinalTransparentRect, nsRegion& aOpaqueRgn,
PRBool aTreatUniformAsOpaque);