371135, oom crashers with big images, r=stuart

This commit is contained in:
vladimir@pobox.com 2007-05-10 12:58:09 -07:00
parent 2025358959
commit 1f64321330
7 changed files with 152 additions and 39 deletions

View File

@ -100,7 +100,10 @@ nsThebesImage::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth, nsMaskRequi
#ifdef XP_WIN
if (!ShouldUseImageSurfaces()) {
mWinSurface = new gfxWindowsSurface(gfxIntSize(mWidth, mHeight), format);
mImageSurface = mWinSurface->GetImageSurface();
if (mWinSurface && mWinSurface->Status() == 0) {
// no error
mImageSurface = mWinSurface->GetImageSurface();
}
}
if (!mImageSurface) {
@ -110,6 +113,13 @@ nsThebesImage::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth, nsMaskRequi
#else
mImageSurface = new gfxImageSurface(gfxIntSize(mWidth, mHeight), format);
#endif
if (!mImageSurface || mImageSurface->Status()) {
mImageSurface = nsnull;
// guess
return NS_ERROR_OUT_OF_MEMORY;
}
mStride = mImageSurface->Stride();
return NS_OK;
@ -286,7 +296,7 @@ nsThebesImage::LockImagePixels(PRBool aMaskPixels)
// Recover the pixels
mImageSurface = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
gfxImageSurface::ImageFormatARGB32);
if (!mImageSurface)
if (!mImageSurface || mImageSurface->Status())
return NS_ERROR_OUT_OF_MEMORY;
nsRefPtr<gfxContext> context = new gfxContext(mImageSurface);
if (!context) {
@ -464,6 +474,11 @@ nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
surface = new gfxImageSurface(gfxIntSize(width, height),
gfxASurface::ImageFormatARGB32);
if (!surface || surface->Status()) {
thebesContext->SetMatrix(savedCTM);
return NS_ERROR_OUT_OF_MEMORY;
}
tmpSurfaceGrip = surface;
nsRefPtr<gfxContext> tmpContext = new gfxContext(surface);

View File

@ -94,7 +94,10 @@ public:
static already_AddRefed<gfxASurface> Wrap(cairo_surface_t *csurf);
/*** this DOES NOT addref the surface */
cairo_surface_t *CairoSurface() { return mSurface; }
cairo_surface_t *CairoSurface() {
NS_ASSERTION(mSurface != nsnull, "gfxASurface::CairoSurface called with mSurface == nsnull!");
return mSurface;
}
gfxSurfaceType GetType() const;
@ -121,7 +124,17 @@ public:
virtual void Finish();
int Status();
/* Make sure that the given dimensions don't overflow a 32-bit signed int
* using 4 bytes per pixel; optionally, make sure that either dimension
* doesn't exceed the given limit.
*/
static PRBool CheckSurfaceSize(const gfxIntSize& sz, PRInt32 limit = 0);
protected:
gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mSurfaceValid(PR_FALSE) { }
static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf);
static void SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf);
@ -130,10 +143,13 @@ protected:
virtual ~gfxASurface() {
}
private:
cairo_surface_t *mSurface;
PRPackedBool mHasFloatingRef;
static void SurfaceDestroyFunc(void *data);
cairo_surface_t *mSurface;
PRInt32 mFloatingRefs;
protected:
PRPackedBool mSurfaceValid;
};
/**

View File

@ -65,30 +65,47 @@ gfxASurface::AddRef(void)
{
NS_PRECONDITION(mSurface != nsnull, "gfxASurface::AddRef without mSurface");
if (mHasFloatingRef) {
// eat the floating ref
mHasFloatingRef = PR_FALSE;
} else {
cairo_surface_reference(mSurface);
}
if (mSurfaceValid) {
if (mFloatingRefs) {
// eat a floating ref
mFloatingRefs--;
} else {
cairo_surface_reference(mSurface);
}
return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
} else {
// the surface isn't valid, but we still need to refcount
// the gfxASurface
return ++mFloatingRefs;
}
}
nsrefcnt
gfxASurface::Release(void)
{
NS_PRECONDITION(!mHasFloatingRef, "gfxASurface::Release while floating ref still outstanding!");
NS_PRECONDITION(mSurface != nsnull, "gfxASurface::Release without mSurface");
// Note that there is a destructor set on user data for mSurface,
// which will delete this gfxASurface wrapper when the surface's refcount goes
// out of scope.
nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface);
cairo_surface_destroy(mSurface);
// |this| may not be valid any more, don't use it!
if (mSurfaceValid) {
NS_ASSERTION(mFloatingRefs == 0, "gfxASurface::Release with floating refs still hanging around!");
return --refcnt;
// Note that there is a destructor set on user data for mSurface,
// which will delete this gfxASurface wrapper when the surface's refcount goes
// out of scope.
nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface);
cairo_surface_destroy(mSurface);
// |this| may not be valid any more, don't use it!
return --refcnt;
} else {
if (--mFloatingRefs == 0) {
delete this;
return 0;
}
return mFloatingRefs;
}
}
void
@ -157,14 +174,22 @@ gfxASurface::Wrap (cairo_surface_t *csurf)
void
gfxASurface::Init(cairo_surface_t* surface, PRBool existingSurface)
{
if (cairo_surface_status(surface)) {
// the surface has an error on it
mSurfaceValid = PR_FALSE;
cairo_surface_destroy(surface);
return;
}
SetSurfaceWrapper(surface, this);
mSurface = surface;
mSurfaceValid = PR_TRUE;
if (existingSurface) {
mHasFloatingRef = PR_FALSE;
mFloatingRefs = 0;
} else {
mHasFloatingRef = PR_TRUE;
mFloatingRefs = 1;
}
}
@ -215,7 +240,6 @@ gfxASurface::MarkDirty(const gfxRect& r)
(int) r.size.width, (int) r.size.height);
}
void
gfxASurface::SetData(const cairo_user_data_key_t *key,
void *user_data,
@ -235,3 +259,37 @@ gfxASurface::Finish()
{
cairo_surface_finish(mSurface);
}
int
gfxASurface::Status()
{
if (!mSurfaceValid)
return -1;
return cairo_surface_status(mSurface);
}
/* static */
PRBool
gfxASurface::CheckSurfaceSize(const gfxIntSize& sz, PRInt32 limit)
{
if (sz.width <= 0 || sz.height <= 0)
return PR_FALSE;
// check to make sure we don't overflow a 32-bit
PRInt32 tmp = sz.width * sz.height;
if (tmp / sz.height != sz.width)
return PR_FALSE;
// always assume 4-byte stride
tmp = tmp * 4;
if (tmp / 4 != sz.width * sz.height)
return PR_FALSE;
// reject images with sides bigger than limit
if (limit &&
(sz.width > limit || sz.height > limit))
return PR_FALSE;
return PR_TRUE;
}

View File

@ -35,6 +35,8 @@
*
* ***** END LICENSE BLOCK ***** */
#include "prmem.h"
#include "gfxImageSurface.h"
#include "cairo.h"
@ -42,8 +44,15 @@
gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format) :
mSize(size), mFormat(format)
{
long stride = ComputeStride();
mData = new unsigned char[mSize.height * stride];
mStride = ComputeStride();
if (!CheckSurfaceSize(size))
return;
mData = (unsigned char *) malloc(mSize.height * mStride);
if (!mData)
return;
mOwnsData = PR_TRUE;
cairo_surface_t *surface =
@ -51,9 +60,7 @@ gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format)
(cairo_format_t)format,
mSize.width,
mSize.height,
stride);
mStride = stride;
mStride);
Init(surface);
}
@ -71,8 +78,13 @@ gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)
gfxImageSurface::~gfxImageSurface()
{
if (mOwnsData)
delete[] mData;
if (!mSurfaceValid)
return;
if (mOwnsData) {
free(mData);
mData = nsnull;
}
}
long

View File

@ -42,6 +42,9 @@
gfxQuartzSurface::gfxQuartzSurface(const gfxSize& size, gfxImageFormat format)
: mSize(size)
{
if (!CheckSurfaceSize(size))
return;
cairo_surface_t *surf = cairo_quartz_surface_create
((cairo_format_t) format, floor(size.width), floor(size.height));

View File

@ -60,12 +60,11 @@ gfxWindowsSurface::gfxWindowsSurface(HDC dc, PRBool deleteDC) :
gfxWindowsSurface::gfxWindowsSurface(const gfxIntSize& size, gfxImageFormat imageFormat) :
mOwnsDC(PR_FALSE), mWnd(nsnull)
{
if (!CheckSurfaceSize(size))
return;
cairo_surface_t *surf = cairo_win32_surface_create_with_dib((cairo_format_t)imageFormat,
size.width, size.height);
if (!surf || cairo_surface_status(surf)) {
fprintf (stderr, "++++++++++++ gfxWindowsSurface: DIB surface creation failed!\n");
}
Init(surf);
mDC = cairo_win32_surface_get_dc(CairoSurface());
@ -74,12 +73,11 @@ gfxWindowsSurface::gfxWindowsSurface(const gfxIntSize& size, gfxImageFormat imag
gfxWindowsSurface::gfxWindowsSurface(HDC dc, const gfxIntSize& size, gfxImageFormat imageFormat) :
mOwnsDC(PR_FALSE), mWnd(nsnull)
{
if (!CheckSurfaceSize(size))
return;
cairo_surface_t *surf = cairo_win32_surface_create_with_ddb(dc, (cairo_format_t)imageFormat,
size.width, size.height);
if (!surf || cairo_surface_status(surf)) {
fprintf (stderr, "++++++++++++ gfxWindowsSurface: DDB surface creation failed!\n");
}
Init(surf);
mDC = cairo_win32_surface_get_dc(CairoSurface());

View File

@ -51,6 +51,8 @@ typedef struct {
static void pixmap_free_func (void *);
#define XLIB_IMAGE_SIDE_SIZE_LIMIT 0xffff
gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual)
: mPixmapTaken(PR_FALSE), mDisplay(dpy), mDrawable(drawable)
{
@ -62,6 +64,9 @@ gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual)
gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual, const gfxIntSize& size)
: mPixmapTaken(PR_FALSE), mDisplay(dpy), mDrawable(drawable), mSize(size)
{
if (!CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT))
return;
cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, mSize.width, mSize.height);
Init(surf);
}
@ -70,6 +75,9 @@ gfxXlibSurface::gfxXlibSurface(Display *dpy, Visual *visual, const gfxIntSize& s
: mPixmapTaken(PR_FALSE), mDisplay(dpy), mSize(size)
{
if (!CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT))
return;
mDrawable = (Drawable)XCreatePixmap(dpy,
RootWindow(dpy, DefaultScreen(dpy)),
mSize.width, mSize.height,
@ -85,6 +93,9 @@ gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, XRenderPictForma
const gfxIntSize& size)
: mPixmapTaken(PR_FALSE), mDisplay(dpy), mDrawable(drawable), mSize(size)
{
if (!CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT))
return;
cairo_surface_t *surf = cairo_xlib_surface_create_with_xrender_format(dpy, drawable,
ScreenOfDisplay(dpy,DefaultScreen(dpy)),
format, mSize.width, mSize.height);