From f02cb58e859a9021c623bf66d22d90b78fb70f63 Mon Sep 17 00:00:00 2001 From: "longsonr@gmail.com" Date: Wed, 13 Jun 2007 02:02:48 -0700 Subject: [PATCH] Bug 378583 - Large pattern surfaces crash browser. r=tor,sr=roc --- layout/svg/base/src/nsSVGFilterFrame.cpp | 73 +++++++++++------------ layout/svg/base/src/nsSVGMaskFrame.cpp | 23 +++++-- layout/svg/base/src/nsSVGPatternFrame.cpp | 35 +++++++++-- layout/svg/base/src/nsSVGUtils.cpp | 22 +++++++ layout/svg/base/src/nsSVGUtils.h | 18 ++++++ 5 files changed, 122 insertions(+), 49 deletions(-) diff --git a/layout/svg/base/src/nsSVGFilterFrame.cpp b/layout/svg/base/src/nsSVGFilterFrame.cpp index df154855acfb..aeacd026125d 100644 --- a/layout/svg/base/src/nsSVGFilterFrame.cpp +++ b/layout/svg/base/src/nsSVGFilterFrame.cpp @@ -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 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 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 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 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; kGetChildAt(k); @@ -299,8 +298,8 @@ nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext, nsCOMPtr 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)); diff --git a/layout/svg/base/src/nsSVGMaskFrame.cpp b/layout/svg/base/src/nsSVGMaskFrame.cpp index c386894eb83a..cb7c17fcb729 100644 --- a/layout/svg/base/src/nsSVGMaskFrame.cpp +++ b/layout/svg/base/src/nsSVGMaskFrame.cpp @@ -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 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(); diff --git a/layout/svg/base/src/nsSVGPatternFrame.cpp b/layout/svg/base/src/nsSVGPatternFrame.cpp index 85e5cd591484..9972df1e9957 100644 --- a/layout/svg/base/src/nsSVGPatternFrame.cpp +++ b/layout/svg/base/src/nsSVGPatternFrame.cpp @@ -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 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 tmpSurface = - gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(PRInt32(surfaceWidth), - PRInt32(surfaceHeight)), + gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize, gfxASurface::ImageFormatARGB32); if (!tmpSurface) return NS_ERROR_FAILURE; diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 51695c75d436..b9cc2d63f9b3 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -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() { diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index db15529f3d41..83096aad6c15 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -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.