gecko-dev/image/SurfacePipe.cpp
Andrew Osmond 42e6712830 Bug 1388332 - Fix a shutdown crash when destroying image decoders before initializing its SurfacePipe. r=tnikkel
A default constructed SurfacePipe contains a NullSurfaceSink as its
filter in mHead. This filter does nothing and is merely a placeholder.
Since most SurfacePipe objects are constructed with the default
constructor, and NullSurfaceSink has no (modified) state, we use a
singleton to represent it. Normally the SurfacePipe owns its filter, so
it needs to do a special check for NullSurfaceSink to ensure it doesn't
free it explicitly.

A Decoder object contains a default constructed SurfacePipe until it
needs to create the first frame from an image. This is a very brief
window because it does not take very long or much data to get to this
stage of decoding.

The NullSurfaceSink singleton is freed upon shutdown, however some
ISurfaceProvider objects may be lingering after this. If their Decoder
has yet to create the first frame, that means the SurfacePipe actually
contains a dangling pointer to the already freed singleton. To make
things worse, it actually tried to free the filter because it didn't
match the singleton (it got freed!).

As such, this change removes NullSurfaceSink entirely. We never use the
SurfacePipe before initializing it with a proper filter, and it would be
considered a programming error to do so. Instead let SurfacePipe::mHead
be null, and assert that it is not null when any operations are
performed on the SurfacePipe.
2017-08-09 06:54:55 -04:00

173 lines
5.1 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SurfacePipe.h"
#include <algorithm> // for min
#include "Decoder.h"
namespace mozilla {
namespace image {
using namespace gfx;
using std::min;
Maybe<SurfaceInvalidRect>
AbstractSurfaceSink::TakeInvalidRect()
{
if (mInvalidRect.IsEmpty()) {
return Nothing();
}
SurfaceInvalidRect invalidRect;
invalidRect.mInputSpaceRect = invalidRect.mOutputSpaceRect = mInvalidRect;
// Forget about the invalid rect we're returning.
mInvalidRect = IntRect();
return Some(invalidRect);
}
uint8_t*
AbstractSurfaceSink::DoResetToFirstRow()
{
mRow = 0;
return GetRowPointer();
}
uint8_t*
AbstractSurfaceSink::DoAdvanceRow()
{
if (mRow >= uint32_t(InputSize().height)) {
return nullptr;
}
// If we're vertically flipping the output, we need to flip the invalid rect. Since we're
// dealing with an axis-aligned rect, only the y coordinate needs to change.
int32_t invalidY = mFlipVertically
? InputSize().height - (mRow + 1)
: mRow;
mInvalidRect.UnionRect(mInvalidRect,
IntRect(0, invalidY, InputSize().width, 1));
mRow = min(uint32_t(InputSize().height), mRow + 1);
return mRow < uint32_t(InputSize().height) ? GetRowPointer()
: nullptr;
}
nsresult
SurfaceSink::Configure(const SurfaceConfig& aConfig)
{
// For non-paletted surfaces, the surface size is just the output size.
IntSize surfaceSize = aConfig.mOutputSize;
// Non-paletted surfaces should not have frame rects, so we just pass
// AllocateFrame() a frame rect which covers the entire surface.
IntRect frameRect(0, 0, surfaceSize.width, surfaceSize.height);
// Allocate the frame.
// XXX(seth): Once every Decoder subclass uses SurfacePipe, we probably want
// to allocate the frame directly here and get rid of Decoder::AllocateFrame
// altogether.
nsresult rv = aConfig.mDecoder->AllocateFrame(aConfig.mFrameNum,
surfaceSize,
frameRect,
aConfig.mFormat);
if (NS_FAILED(rv)) {
return rv;
}
mImageData = aConfig.mDecoder->mImageData;
mImageDataLength = aConfig.mDecoder->mImageDataLength;
mFlipVertically = aConfig.mFlipVertically;
MOZ_ASSERT(mImageData);
MOZ_ASSERT(mImageDataLength ==
uint32_t(surfaceSize.width * surfaceSize.height * sizeof(uint32_t)));
ConfigureFilter(surfaceSize, sizeof(uint32_t));
return NS_OK;
}
uint8_t*
SurfaceSink::GetRowPointer() const
{
// If we're flipping vertically, reverse the order in which we traverse the
// rows.
uint32_t row = mFlipVertically
? InputSize().height - (mRow + 1)
: mRow;
uint8_t* rowPtr = mImageData + row * InputSize().width * sizeof(uint32_t);
MOZ_ASSERT(rowPtr >= mImageData);
MOZ_ASSERT(rowPtr < mImageData + mImageDataLength);
MOZ_ASSERT(rowPtr + InputSize().width * sizeof(uint32_t) <=
mImageData + mImageDataLength);
return rowPtr;
}
nsresult
PalettedSurfaceSink::Configure(const PalettedSurfaceConfig& aConfig)
{
MOZ_ASSERT(aConfig.mFormat == SurfaceFormat::B8G8R8A8);
// For paletted surfaces, the surface size is the size of the frame rect.
IntSize surfaceSize = aConfig.mFrameRect.Size();
// Allocate the frame.
// XXX(seth): Once every Decoder subclass uses SurfacePipe, we probably want
// to allocate the frame directly here and get rid of Decoder::AllocateFrame
// altogether.
nsresult rv = aConfig.mDecoder->AllocateFrame(aConfig.mFrameNum,
aConfig.mOutputSize,
aConfig.mFrameRect,
aConfig.mFormat,
aConfig.mPaletteDepth);
if (NS_FAILED(rv)) {
return rv;
}
mImageData = aConfig.mDecoder->mImageData;
mImageDataLength = aConfig.mDecoder->mImageDataLength;
mFlipVertically = aConfig.mFlipVertically;
mFrameRect = aConfig.mFrameRect;
MOZ_ASSERT(mImageData);
MOZ_ASSERT(mImageDataLength ==
uint32_t(mFrameRect.width * mFrameRect.height * sizeof(uint8_t)));
ConfigureFilter(surfaceSize, sizeof(uint8_t));
return NS_OK;
}
uint8_t*
PalettedSurfaceSink::GetRowPointer() const
{
// If we're flipping vertically, reverse the order in which we traverse the
// rows.
uint32_t row = mFlipVertically
? InputSize().height - (mRow + 1)
: mRow;
uint8_t* rowPtr = mImageData + row * InputSize().width * sizeof(uint8_t);
MOZ_ASSERT(rowPtr >= mImageData);
MOZ_ASSERT(rowPtr < mImageData + mImageDataLength);
MOZ_ASSERT(rowPtr + InputSize().width * sizeof(uint8_t) <=
mImageData + mImageDataLength);
return rowPtr;
}
} // namespace image
} // namespace mozilla