зеркало из https://github.com/mozilla/gecko-dev.git
Bug 378583 - Large pattern surfaces crash browser. r=tor,sr=roc
This commit is contained in:
Родитель
ab581cee9c
Коммит
f02cb58e85
|
@ -51,13 +51,6 @@
|
|||
#include "gfxContext.h"
|
||||
#include "gfxImageSurface.h"
|
||||
|
||||
// maximum dimension of a filter - choose so that
|
||||
// 4*(FILTER_RES_MAX^2) < UINT_MAX, it's small
|
||||
// enough that it could be computed in a reasonable
|
||||
// amount of time, and large enough for most content
|
||||
// window sizes.
|
||||
#define FILTER_RES_MAX 16384
|
||||
|
||||
nsIFrame*
|
||||
NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext)
|
||||
{
|
||||
|
@ -143,13 +136,6 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
|||
|
||||
nsCOMPtr<nsIDOMSVGMatrix> ctm = nsSVGUtils::GetCanvasTM(frame);
|
||||
|
||||
float s1, s2;
|
||||
ctm->GetA(&s1);
|
||||
ctm->GetD(&s2);
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "scales: %f %f\n", s1, s2);
|
||||
#endif
|
||||
|
||||
nsSVGElement *target = NS_STATIC_CAST(nsSVGElement*, frame->GetContent());
|
||||
|
||||
aTarget->SetMatrixPropagation(PR_FALSE);
|
||||
|
@ -187,39 +173,51 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
|||
height = nsSVGUtils::UserSpace(target, tmpHeight);
|
||||
}
|
||||
|
||||
PRInt32 filterResX = PRInt32(s1 * width + 0.5);
|
||||
PRInt32 filterResY = PRInt32(s2 * height + 0.5);
|
||||
PRBool resultOverflows;
|
||||
gfxIntSize filterRes;
|
||||
|
||||
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) {
|
||||
PRInt32 filterResX, filterResY;
|
||||
filter->mFilterResX->GetAnimVal(&filterResX);
|
||||
filter->mFilterResY->GetAnimVal(&filterResY);
|
||||
|
||||
filterRes =
|
||||
nsSVGUtils::ConvertToSurfaceSize(gfxSize(filterResX, filterResY),
|
||||
&resultOverflows);
|
||||
} else {
|
||||
float s1, s2;
|
||||
ctm->GetA(&s1);
|
||||
ctm->GetD(&s2);
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "scales: %f %f\n", s1, s2);
|
||||
#endif
|
||||
|
||||
filterRes =
|
||||
nsSVGUtils::ConvertToSurfaceSize(gfxSize(s1 * width, s2 * height),
|
||||
&resultOverflows);
|
||||
}
|
||||
|
||||
// filterRes = 0 disables rendering, < 0 is error
|
||||
if (filterResX <= 0.0f || filterResY <= 0.0f)
|
||||
return NS_OK;
|
||||
|
||||
// prevent filters from overflowing or generally using excessive memory
|
||||
filterResX = PR_MIN(FILTER_RES_MAX, filterResX);
|
||||
filterResY = PR_MIN(FILTER_RES_MAX, filterResY);
|
||||
// 0 disables rendering, < 0 is error
|
||||
if (filterRes.width <= 0 || filterRes.height <= 0)
|
||||
return NS_OK;
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "filter bbox: %f,%f %fx%f\n", x, y, width, height);
|
||||
fprintf(stderr, "filterRes: %u %u\n", filterResX, filterResY);
|
||||
fprintf(stderr, "filterRes: %u %u\n", filterRes.width, filterRes.height);
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIDOMSVGMatrix> filterTransform;
|
||||
NS_NewSVGMatrix(getter_AddRefs(filterTransform),
|
||||
filterResX/width, 0.0f,
|
||||
0.0f, filterResY/height,
|
||||
-x*filterResX/width, -y*filterResY/height);
|
||||
filterRes.width / width, 0.0f,
|
||||
0.0f, filterRes.height / height,
|
||||
-x * filterRes.width / width, -y * filterRes.height / height);
|
||||
aTarget->SetOverrideCTM(filterTransform);
|
||||
aTarget->NotifyCanvasTMChanged(PR_TRUE);
|
||||
|
||||
// paint the target geometry
|
||||
nsRefPtr<gfxImageSurface> tmpSurface =
|
||||
new gfxImageSurface(gfxIntSize(filterResX, filterResY),
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
new gfxImageSurface(filterRes, gfxASurface::ImageFormatARGB32);
|
||||
if (!tmpSurface || !tmpSurface->Data()) {
|
||||
FilterFailCleanup(aContext, aTarget);
|
||||
return NS_OK;
|
||||
|
@ -238,7 +236,7 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
|||
filter->mPrimitiveUnits->GetAnimVal(&primitiveUnits);
|
||||
nsSVGFilterInstance instance(target, bbox,
|
||||
x, y, width, height,
|
||||
filterResX, filterResY,
|
||||
filterRes.width, filterRes.height,
|
||||
primitiveUnits);
|
||||
nsSVGFilterInstance::ColorModel
|
||||
colorModel(nsSVGFilterInstance::ColorModel::SRGB,
|
||||
|
@ -246,8 +244,7 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
|||
|
||||
if (requirements & NS_FE_SOURCEALPHA) {
|
||||
nsRefPtr<gfxImageSurface> alpha =
|
||||
new gfxImageSurface(gfxIntSize(filterResX, filterResY),
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
new gfxImageSurface(filterRes, gfxASurface::ImageFormatARGB32);
|
||||
|
||||
if (!alpha || !alpha->Data()) {
|
||||
FilterFailCleanup(aContext, aTarget);
|
||||
|
@ -258,8 +255,8 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
|||
PRUint8 *alphaData = alpha->Data();
|
||||
PRUint32 stride = tmpSurface->Stride();
|
||||
|
||||
for (PRUint32 yy = 0; yy < PRUint32(filterResY); yy++)
|
||||
for (PRUint32 xx = 0; xx < PRUint32(filterResX); xx++) {
|
||||
for (PRInt32 yy = 0; yy < filterRes.height; yy++)
|
||||
for (PRInt32 xx = 0; xx < filterRes.width; xx++) {
|
||||
alphaData[stride*yy + 4*xx + GFX_ARGB32_OFFSET_B] = 0;
|
||||
alphaData[stride*yy + 4*xx + GFX_ARGB32_OFFSET_G] = 0;
|
||||
alphaData[stride*yy + 4*xx + GFX_ARGB32_OFFSET_R] = 0;
|
||||
|
@ -268,13 +265,15 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
|||
}
|
||||
|
||||
instance.DefineImage(NS_LITERAL_STRING("SourceAlpha"), alpha,
|
||||
nsRect(0, 0, filterResX, filterResY), colorModel);
|
||||
nsRect(0, 0, filterRes.width, filterRes.height),
|
||||
colorModel);
|
||||
}
|
||||
|
||||
// this always needs to be defined last because the default image
|
||||
// for the first filter element is supposed to be SourceGraphic
|
||||
instance.DefineImage(NS_LITERAL_STRING("SourceGraphic"), tmpSurface,
|
||||
nsRect(0, 0, filterResX, filterResY), colorModel);
|
||||
nsRect(0, 0, filterRes.width, filterRes.height),
|
||||
colorModel);
|
||||
|
||||
for (PRUint32 k=0; k<count; ++k) {
|
||||
nsIContent* child = mContent->GetChildAt(k);
|
||||
|
@ -299,8 +298,8 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
|
|||
|
||||
nsCOMPtr<nsIDOMSVGMatrix> scale, fini;
|
||||
NS_NewSVGMatrix(getter_AddRefs(scale),
|
||||
width/filterResX, 0.0f,
|
||||
0.0f, height/filterResY,
|
||||
width / filterRes.width, 0.0f,
|
||||
0.0f, height / filterRes.height,
|
||||
x, y);
|
||||
|
||||
ctm->Multiply(scale, getter_AddRefs(fini));
|
||||
|
|
|
@ -178,11 +178,22 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRenderState *aContext,
|
|||
clipExtents.Width(), clipExtents.Height());
|
||||
#endif
|
||||
|
||||
gfxIntSize clipSize(PRInt32(clipExtents.Width() + 0.5),
|
||||
PRInt32(clipExtents.Height() + 0.5));
|
||||
PRBool resultOverflows;
|
||||
gfxIntSize surfaceSize =
|
||||
nsSVGUtils::ConvertToSurfaceSize(gfxSize(clipExtents.Width(),
|
||||
clipExtents.Height()),
|
||||
&resultOverflows);
|
||||
|
||||
// 0 disables mask, < 0 is an error
|
||||
if (surfaceSize.width <= 0 || surfaceSize.height <= 0)
|
||||
return nsnull;
|
||||
|
||||
if (resultOverflows)
|
||||
return nsnull;
|
||||
|
||||
nsRefPtr<gfxImageSurface> image =
|
||||
new gfxImageSurface(clipSize, gfxASurface::ImageFormatARGB32);
|
||||
if (!image)
|
||||
new gfxImageSurface(surfaceSize, gfxASurface::ImageFormatARGB32);
|
||||
if (!image || !image->Data())
|
||||
return nsnull;
|
||||
|
||||
gfxContext transferCtx(image);
|
||||
|
@ -190,8 +201,8 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRenderState *aContext,
|
|||
transferCtx.SetSource(surface, -clipExtents.pos);
|
||||
transferCtx.Paint();
|
||||
|
||||
PRUint32 width = clipSize.width;
|
||||
PRUint32 height = clipSize.height;
|
||||
PRUint32 width = surfaceSize.width;
|
||||
PRUint32 height = surfaceSize.height;
|
||||
PRUint8 *data = image->Data();
|
||||
PRInt32 stride = image->Stride();
|
||||
|
||||
|
|
|
@ -281,17 +281,40 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
|
|||
|
||||
// Now that we have all of the necessary geometries, we can
|
||||
// create our surface.
|
||||
float surfaceWidth, surfaceHeight;
|
||||
bbox->GetWidth(&surfaceWidth);
|
||||
bbox->GetHeight(&surfaceHeight);
|
||||
float patternWidth, patternHeight;
|
||||
bbox->GetWidth(&patternWidth);
|
||||
bbox->GetHeight(&patternHeight);
|
||||
|
||||
PRBool resultOverflows;
|
||||
gfxIntSize surfaceSize =
|
||||
nsSVGUtils::ConvertToSurfaceSize(gfxSize(patternWidth, patternHeight),
|
||||
&resultOverflows);
|
||||
|
||||
// 0 disables rendering, < 0 is an error
|
||||
if (surfaceSize.width <= 0 || surfaceSize.height <= 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (resultOverflows) {
|
||||
// scale down drawing to new pattern surface size
|
||||
nsCOMPtr<nsIDOMSVGMatrix> tempTM, aCTM;
|
||||
NS_NewSVGMatrix(getter_AddRefs(tempTM),
|
||||
surfaceSize.width / patternWidth, 0.0f,
|
||||
0.0f, surfaceSize.height / patternHeight,
|
||||
0.0f, 0.0f);
|
||||
mCTM->Multiply(tempTM, getter_AddRefs(aCTM));
|
||||
aCTM.swap(mCTM);
|
||||
|
||||
// and magnify pattern to compensate
|
||||
patternMatrix->Scale(patternWidth / surfaceSize.width,
|
||||
patternHeight / surfaceSize.height);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_scooter
|
||||
printf("Creating %dX%d surface\n",(int)(surfaceWidth),(int)(surfaceHeight));
|
||||
printf("Creating %dX%d surface\n", int(surfaceSize.width), int(surfaceSize.height));
|
||||
#endif
|
||||
|
||||
nsRefPtr<gfxASurface> tmpSurface =
|
||||
gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(PRInt32(surfaceWidth),
|
||||
PRInt32(surfaceHeight)),
|
||||
gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize,
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
if (!tmpSurface)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
|
|
@ -1283,6 +1283,28 @@ nsSVGUtils::ToBoundingPixelRect(const gfxRect& rect)
|
|||
nscoord(ceil(rect.YMost()) - floor(rect.Y())));
|
||||
}
|
||||
|
||||
gfxIntSize
|
||||
nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize, PRBool *aResultOverflows)
|
||||
{
|
||||
gfxIntSize surfaceSize =
|
||||
gfxIntSize(PRInt32(aSize.width + 0.5), PRInt32(aSize.height + 0.5));
|
||||
|
||||
*aResultOverflows = (aSize.width >= PR_INT32_MAX + 0.5 ||
|
||||
aSize.height >= PR_INT32_MAX + 0.5 ||
|
||||
aSize.width <= PR_INT32_MIN - 0.5 ||
|
||||
aSize.height <= PR_INT32_MIN - 0.5);
|
||||
|
||||
if (*aResultOverflows ||
|
||||
!gfxASurface::CheckSurfaceSize(surfaceSize)) {
|
||||
surfaceSize.width = PR_MIN(NS_SVG_OFFSCREEN_MAX_DIMENSION,
|
||||
surfaceSize.width);
|
||||
surfaceSize.height = PR_MIN(NS_SVG_OFFSCREEN_MAX_DIMENSION,
|
||||
surfaceSize.height);
|
||||
*aResultOverflows = PR_TRUE;
|
||||
}
|
||||
return surfaceSize;
|
||||
}
|
||||
|
||||
gfxASurface *
|
||||
nsSVGUtils::GetThebesComputationalSurface()
|
||||
{
|
||||
|
|
|
@ -71,6 +71,8 @@ class gfxASurface;
|
|||
class nsIRenderingContext;
|
||||
struct gfxRect;
|
||||
struct gfxMatrix;
|
||||
struct gfxSize;
|
||||
struct gfxIntSize;
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
|
@ -109,6 +111,11 @@ struct gfxMatrix;
|
|||
#define GFX_ARGB32_OFFSET_B 0
|
||||
#endif
|
||||
|
||||
// maximum dimension of an offscreen surface - choose so that
|
||||
// the surface size doesn't overflow a 32-bit signed int using
|
||||
// 4 bytes per pixel; in line with gfxASurface::CheckSurfaceSize
|
||||
#define NS_SVG_OFFSCREEN_MAX_DIMENSION 16384
|
||||
|
||||
/*
|
||||
* Checks the svg enable preference and if a renderer could
|
||||
* successfully be created. Declared as a function instead of a
|
||||
|
@ -316,6 +323,17 @@ public:
|
|||
static nsRect
|
||||
ToBoundingPixelRect(const gfxRect& rect);
|
||||
|
||||
/*
|
||||
* Convert a surface size to an integer for use by thebes
|
||||
* possibly making it smaller in the process so the surface does not
|
||||
* use excessive memory.
|
||||
* @param aSize the desired surface size
|
||||
* @param aResultOverflows true if the desired surface size is too big
|
||||
* @return the surface size to use
|
||||
*/
|
||||
static gfxIntSize
|
||||
ConvertToSurfaceSize(const gfxSize& aSize, PRBool *aResultOverflows);
|
||||
|
||||
/*
|
||||
* Get a pointer to a surface that can be used to create thebes
|
||||
* contexts for various measurement purposes.
|
||||
|
|
Загрузка…
Ссылка в новой задаче