summaryrefslogtreecommitdiffstats
path: root/image/decoders/nsICODecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'image/decoders/nsICODecoder.cpp')
-rw-r--r--image/decoders/nsICODecoder.cpp673
1 files changed, 673 insertions, 0 deletions
diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp
new file mode 100644
index 000000000..633bb1255
--- /dev/null
+++ b/image/decoders/nsICODecoder.cpp
@@ -0,0 +1,673 @@
+/* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
+/* 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/. */
+
+/* This is a Cross-Platform ICO Decoder, which should work everywhere, including
+ * Big-Endian machines like the PowerPC. */
+
+#include "nsICODecoder.h"
+
+#include <stdlib.h>
+
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Move.h"
+
+#include "RasterImage.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace image {
+
+// Constants.
+static const uint32_t ICOHEADERSIZE = 6;
+static const uint32_t BITMAPINFOSIZE = bmp::InfoHeaderLength::WIN_ICO;
+
+// ----------------------------------------
+// Actual Data Processing
+// ----------------------------------------
+
+// Obtains the number of colors from the bits per pixel
+uint16_t
+nsICODecoder::GetNumColors()
+{
+ uint16_t numColors = 0;
+ if (mBPP <= 8) {
+ switch (mBPP) {
+ case 1:
+ numColors = 2;
+ break;
+ case 4:
+ numColors = 16;
+ break;
+ case 8:
+ numColors = 256;
+ break;
+ default:
+ numColors = (uint16_t)-1;
+ }
+ }
+ return numColors;
+}
+
+nsICODecoder::nsICODecoder(RasterImage* aImage)
+ : Decoder(aImage)
+ , mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
+ Transition::TerminateSuccess())
+ , mBiggestResourceColorDepth(0)
+ , mBestResourceDelta(INT_MIN)
+ , mBestResourceColorDepth(0)
+ , mNumIcons(0)
+ , mCurrIcon(0)
+ , mBPP(0)
+ , mMaskRowSize(0)
+ , mCurrMaskLine(0)
+ , mIsCursor(false)
+ , mHasMaskAlpha(false)
+{ }
+
+nsresult
+nsICODecoder::FinishInternal()
+{
+ // We shouldn't be called in error cases
+ MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
+
+ return GetFinalStateFromContainedDecoder();
+}
+
+nsresult
+nsICODecoder::FinishWithErrorInternal()
+{
+ return GetFinalStateFromContainedDecoder();
+}
+
+nsresult
+nsICODecoder::GetFinalStateFromContainedDecoder()
+{
+ if (!mContainedDecoder) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mContainedSourceBuffer,
+ "Should have a SourceBuffer if we have a decoder");
+
+ // Let the contained decoder finish up if necessary.
+ if (!mContainedSourceBuffer->IsComplete()) {
+ mContainedSourceBuffer->Complete(NS_OK);
+ mContainedDecoder->Decode();
+ }
+
+ // Make our state the same as the state of the contained decoder.
+ mDecodeDone = mContainedDecoder->GetDecodeDone();
+ mProgress |= mContainedDecoder->TakeProgress();
+ mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
+ mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
+
+ // Propagate errors.
+ nsresult rv = HasError() || mContainedDecoder->HasError()
+ ? NS_ERROR_FAILURE
+ : NS_OK;
+
+ MOZ_ASSERT(NS_FAILED(rv) || !mCurrentFrame || mCurrentFrame->IsFinished());
+ return rv;
+}
+
+bool
+nsICODecoder::CheckAndFixBitmapSize(int8_t* aBIH)
+{
+ // Get the width from the BMP file information header. This is
+ // (unintuitively) a signed integer; see the documentation at:
+ //
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
+ //
+ // However, we reject negative widths since they aren't meaningful.
+ const int32_t width = LittleEndian::readInt32(aBIH + 4);
+ if (width <= 0 || width > 256) {
+ return false;
+ }
+
+ // Verify that the BMP width matches the width we got from the ICO directory
+ // entry. If not, decoding fails, because if we were to allow it to continue
+ // the intrinsic size of the image wouldn't match the size of the decoded
+ // surface.
+ if (width != int32_t(GetRealWidth())) {
+ return false;
+ }
+
+ // Get the height from the BMP file information header. This is also signed,
+ // but in this case negative values are meaningful; see below.
+ int32_t height = LittleEndian::readInt32(aBIH + 8);
+ if (height == 0) {
+ return false;
+ }
+
+ // BMPs can be stored inverted by having a negative height.
+ // XXX(seth): Should we really be writing the absolute value into the BIH
+ // below? Seems like this could be problematic for inverted BMPs.
+ height = abs(height);
+
+ // The height field is double the actual height of the image to account for
+ // the AND mask. This is true even if the AND mask is not present.
+ height /= 2;
+ if (height > 256) {
+ return false;
+ }
+
+ // Verify that the BMP height matches the height we got from the ICO directory
+ // entry. If not, again, decoding fails.
+ if (height != int32_t(GetRealHeight())) {
+ return false;
+ }
+
+ // Fix the BMP height in the BIH so that the BMP decoder, which does not know
+ // about the AND mask that may follow the actual bitmap, can work properly.
+ LittleEndian::writeInt32(aBIH + 8, GetRealHeight());
+
+ return true;
+}
+
+LexerTransition<ICOState>
+nsICODecoder::ReadHeader(const char* aData)
+{
+ // If the third byte is 1, this is an icon. If 2, a cursor.
+ if ((aData[2] != 1) && (aData[2] != 2)) {
+ return Transition::TerminateFailure();
+ }
+ mIsCursor = (aData[2] == 2);
+
+ // The fifth and sixth bytes specify the number of resources in the file.
+ mNumIcons = LittleEndian::readUint16(aData + 4);
+ if (mNumIcons == 0) {
+ return Transition::TerminateSuccess(); // Nothing to do.
+ }
+
+ // Downscale-during-decode can end up decoding different resources in the ICO
+ // file depending on the target size. Since the resources are not necessarily
+ // scaled versions of the same image, some may be transparent and some may not
+ // be. We could be precise about transparency if we decoded the metadata of
+ // every resource, but for now we don't and it's safest to assume that
+ // transparency could be present.
+ PostHasTransparency();
+
+ return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
+}
+
+size_t
+nsICODecoder::FirstResourceOffset() const
+{
+ MOZ_ASSERT(mNumIcons > 0,
+ "Calling FirstResourceOffset before processing header");
+
+ // The first resource starts right after the directory, which starts right
+ // after the ICO header.
+ return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
+}
+
+LexerTransition<ICOState>
+nsICODecoder::ReadDirEntry(const char* aData)
+{
+ mCurrIcon++;
+
+ // Read the directory entry.
+ IconDirEntry e;
+ e.mWidth = aData[0];
+ e.mHeight = aData[1];
+ e.mColorCount = aData[2];
+ e.mReserved = aData[3];
+ e.mPlanes = LittleEndian::readUint16(aData + 4);
+ e.mBitCount = LittleEndian::readUint16(aData + 6);
+ e.mBytesInRes = LittleEndian::readUint32(aData + 8);
+ e.mImageOffset = LittleEndian::readUint32(aData + 12);
+
+ // If an explicit output size was specified, we'll try to select the resource
+ // that matches it best below.
+ const Maybe<IntSize> desiredSize = ExplicitOutputSize();
+
+ // Determine if this is the biggest resource we've seen so far. We always use
+ // the biggest resource for the intrinsic size, and if we don't have a
+ // specific desired size, we select it as the best resource as well.
+ IntSize entrySize(GetRealWidth(e), GetRealHeight(e));
+ if (e.mBitCount >= mBiggestResourceColorDepth &&
+ entrySize.width * entrySize.height >=
+ mBiggestResourceSize.width * mBiggestResourceSize.height) {
+ mBiggestResourceSize = entrySize;
+ mBiggestResourceColorDepth = e.mBitCount;
+ mBiggestResourceHotSpot = IntSize(e.mXHotspot, e.mYHotspot);
+
+ if (!desiredSize) {
+ mDirEntry = e;
+ }
+ }
+
+ if (desiredSize) {
+ // Calculate the delta between this resource's size and the desired size, so
+ // we can see if it is better than our current-best option. In the case of
+ // several equally-good resources, we use the last one. "Better" in this
+ // case is determined by |delta|, a measure of the difference in size
+ // between the entry we've found and the desired size. We will choose the
+ // smallest resource that is greater than or equal to the desired size (i.e.
+ // we assume it's better to downscale a larger icon than to upscale a
+ // smaller one).
+ int32_t delta = std::min(entrySize.width - desiredSize->width,
+ entrySize.height - desiredSize->height);
+ if (e.mBitCount >= mBestResourceColorDepth &&
+ ((mBestResourceDelta < 0 && delta >= mBestResourceDelta) ||
+ (delta >= 0 && delta <= mBestResourceDelta))) {
+ mBestResourceDelta = delta;
+ mBestResourceColorDepth = e.mBitCount;
+ mDirEntry = e;
+ }
+ }
+
+ if (mCurrIcon == mNumIcons) {
+ // Ensure the resource we selected has an offset past the ICO headers.
+ if (mDirEntry.mImageOffset < FirstResourceOffset()) {
+ return Transition::TerminateFailure();
+ }
+
+ // If this is a cursor, set the hotspot. We use the hotspot from the biggest
+ // resource since we also use that resource for the intrinsic size.
+ if (mIsCursor) {
+ mImageMetadata.SetHotspot(mBiggestResourceHotSpot.width,
+ mBiggestResourceHotSpot.height);
+ }
+
+ // We always report the biggest resource's size as the intrinsic size; this
+ // is necessary for downscale-during-decode to work since we won't even
+ // attempt to *upscale* while decoding.
+ PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
+ if (IsMetadataDecode()) {
+ return Transition::TerminateSuccess();
+ }
+
+ // If the resource we selected matches the output size perfectly, we don't
+ // need to do any downscaling.
+ if (GetRealSize() == OutputSize()) {
+ MOZ_ASSERT_IF(desiredSize, GetRealSize() == *desiredSize);
+ MOZ_ASSERT_IF(!desiredSize, GetRealSize() == Size());
+ mDownscaler.reset();
+ }
+
+ size_t offsetToResource = mDirEntry.mImageOffset - FirstResourceOffset();
+ return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
+ ICOState::SKIP_TO_RESOURCE,
+ offsetToResource);
+ }
+
+ return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
+}
+
+LexerTransition<ICOState>
+nsICODecoder::SniffResource(const char* aData)
+{
+ // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
+ // is a PNG or a BMP.
+ bool isPNG = !memcmp(aData, nsPNGDecoder::pngSignatureBytes,
+ PNGSIGNATURESIZE);
+ if (isPNG) {
+ // Create a PNG decoder which will do the rest of the work for us.
+ mContainedSourceBuffer = new SourceBuffer();
+ mContainedSourceBuffer->ExpectLength(mDirEntry.mBytesInRes);
+ mContainedDecoder =
+ DecoderFactory::CreateDecoderForICOResource(DecoderType::PNG,
+ WrapNotNull(mContainedSourceBuffer),
+ WrapNotNull(this));
+
+ if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
+ return Transition::TerminateFailure();
+ }
+
+ if (mDirEntry.mBytesInRes <= PNGSIGNATURESIZE) {
+ return Transition::TerminateFailure();
+ }
+
+ // Read in the rest of the PNG unbuffered.
+ size_t toRead = mDirEntry.mBytesInRes - PNGSIGNATURESIZE;
+ return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
+ ICOState::READ_PNG,
+ toRead);
+ } else {
+ // Make sure we have a sane size for the bitmap information header.
+ int32_t bihSize = LittleEndian::readUint32(aData);
+ if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
+ return Transition::TerminateFailure();
+ }
+
+ // Buffer the first part of the bitmap information header.
+ memcpy(mBIHraw, aData, PNGSIGNATURESIZE);
+
+ // Read in the rest of the bitmap information header.
+ return Transition::To(ICOState::READ_BIH,
+ BITMAPINFOSIZE - PNGSIGNATURESIZE);
+ }
+}
+
+LexerTransition<ICOState>
+nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
+{
+ if (!WriteToContainedDecoder(aData, aLen)) {
+ return Transition::TerminateFailure();
+ }
+
+ // Raymond Chen says that 32bpp only are valid PNG ICOs
+ // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
+ if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
+ return Transition::TerminateFailure();
+ }
+
+ return Transition::ContinueUnbuffered(ICOState::READ_PNG);
+}
+
+LexerTransition<ICOState>
+nsICODecoder::ReadBIH(const char* aData)
+{
+ // Buffer the rest of the bitmap information header.
+ memcpy(mBIHraw + PNGSIGNATURESIZE, aData, BITMAPINFOSIZE - PNGSIGNATURESIZE);
+
+ // Extract the BPP from the BIH header; it should be trusted over the one
+ // we have from the ICO header which is usually set to 0.
+ mBPP = LittleEndian::readUint16(mBIHraw + 14);
+
+ // The ICO format when containing a BMP does not include the 14 byte
+ // bitmap file header. So we create the BMP decoder via the constructor that
+ // tells it to skip this, and pass in the required data (dataOffset) that
+ // would have been present in the header.
+ uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE;
+ if (mDirEntry.mBitCount <= 8) {
+ // The color table is present only if BPP is <= 8.
+ uint16_t numColors = GetNumColors();
+ if (numColors == (uint16_t)-1) {
+ return Transition::TerminateFailure();
+ }
+ dataOffset += 4 * numColors;
+ }
+
+ // Create a BMP decoder which will do most of the work for us; the exception
+ // is the AND mask, which isn't present in standalone BMPs.
+ mContainedSourceBuffer = new SourceBuffer();
+ mContainedSourceBuffer->ExpectLength(mDirEntry.mBytesInRes);
+ mContainedDecoder =
+ DecoderFactory::CreateDecoderForICOResource(DecoderType::BMP,
+ WrapNotNull(mContainedSourceBuffer),
+ WrapNotNull(this),
+ Some(dataOffset));
+ RefPtr<nsBMPDecoder> bmpDecoder =
+ static_cast<nsBMPDecoder*>(mContainedDecoder.get());
+
+ // Verify that the BIH width and height values match the ICO directory entry,
+ // and fix the BIH height value to compensate for the fact that the underlying
+ // BMP decoder doesn't know about AND masks.
+ if (!CheckAndFixBitmapSize(reinterpret_cast<int8_t*>(mBIHraw))) {
+ return Transition::TerminateFailure();
+ }
+
+ // Write out the BMP's bitmap info header.
+ if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
+ return Transition::TerminateFailure();
+ }
+
+ // Check to make sure we have valid color settings.
+ uint16_t numColors = GetNumColors();
+ if (numColors == uint16_t(-1)) {
+ return Transition::TerminateFailure();
+ }
+
+ // Do we have an AND mask on this BMP? If so, we need to read it after we read
+ // the BMP data itself.
+ uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
+ bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry.mBytesInRes;
+ ICOState afterBMPState = hasANDMask ? ICOState::PREPARE_FOR_MASK
+ : ICOState::FINISHED_RESOURCE;
+
+ // Read in the rest of the BMP unbuffered.
+ return Transition::ToUnbuffered(afterBMPState,
+ ICOState::READ_BMP,
+ bmpDataLength);
+}
+
+LexerTransition<ICOState>
+nsICODecoder::ReadBMP(const char* aData, uint32_t aLen)
+{
+ if (!WriteToContainedDecoder(aData, aLen)) {
+ return Transition::TerminateFailure();
+ }
+
+ return Transition::ContinueUnbuffered(ICOState::READ_BMP);
+}
+
+LexerTransition<ICOState>
+nsICODecoder::PrepareForMask()
+{
+ RefPtr<nsBMPDecoder> bmpDecoder =
+ static_cast<nsBMPDecoder*>(mContainedDecoder.get());
+
+ uint16_t numColors = GetNumColors();
+ MOZ_ASSERT(numColors != uint16_t(-1));
+
+ // Determine the length of the AND mask.
+ uint32_t bmpLengthWithHeader =
+ BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
+ MOZ_ASSERT(bmpLengthWithHeader < mDirEntry.mBytesInRes);
+ uint32_t maskLength = mDirEntry.mBytesInRes - bmpLengthWithHeader;
+
+ // If the BMP provides its own transparency, we ignore the AND mask. We can
+ // also obviously ignore it if the image has zero width or zero height.
+ if (bmpDecoder->HasTransparency() ||
+ GetRealWidth() == 0 || GetRealHeight() == 0) {
+ return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
+ ICOState::SKIP_MASK,
+ maskLength);
+ }
+
+ // Compute the row size for the mask.
+ mMaskRowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
+
+ // If the expected size of the AND mask is larger than its actual size, then
+ // we must have a truncated (and therefore corrupt) AND mask.
+ uint32_t expectedLength = mMaskRowSize * GetRealHeight();
+ if (maskLength < expectedLength) {
+ return Transition::TerminateFailure();
+ }
+
+ // If we're downscaling, the mask is the wrong size for the surface we've
+ // produced, so we need to downscale the mask into a temporary buffer and then
+ // combine the mask's alpha values with the color values from the image.
+ if (mDownscaler) {
+ MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
+ mDownscaler->TargetSize().width *
+ mDownscaler->TargetSize().height *
+ sizeof(uint32_t));
+ mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
+ nsresult rv = mDownscaler->BeginFrame(GetRealSize(), Nothing(),
+ mMaskBuffer.get(),
+ /* aHasAlpha = */ true,
+ /* aFlipVertically = */ true);
+ if (NS_FAILED(rv)) {
+ return Transition::TerminateFailure();
+ }
+ }
+
+ mCurrMaskLine = GetRealHeight();
+ return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
+}
+
+
+LexerTransition<ICOState>
+nsICODecoder::ReadMaskRow(const char* aData)
+{
+ mCurrMaskLine--;
+
+ uint8_t sawTransparency = 0;
+
+ // Get the mask row we're reading.
+ const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
+ const uint8_t* maskRowEnd = mask + mMaskRowSize;
+
+ // Get the corresponding row of the mask buffer (if we're downscaling) or the
+ // decoded image data (if we're not).
+ uint32_t* decoded = nullptr;
+ if (mDownscaler) {
+ // Initialize the row to all white and fully opaque.
+ memset(mDownscaler->RowBuffer(), 0xFF, GetRealWidth() * sizeof(uint32_t));
+
+ decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
+ } else {
+ RefPtr<nsBMPDecoder> bmpDecoder =
+ static_cast<nsBMPDecoder*>(mContainedDecoder.get());
+ uint32_t* imageData = bmpDecoder->GetImageData();
+ if (!imageData) {
+ return Transition::TerminateFailure();
+ }
+
+ decoded = imageData + mCurrMaskLine * GetRealWidth();
+ }
+
+ MOZ_ASSERT(decoded);
+ uint32_t* decodedRowEnd = decoded + GetRealWidth();
+
+ // Iterate simultaneously through the AND mask and the image data.
+ while (mask < maskRowEnd) {
+ uint8_t idx = *mask++;
+ sawTransparency |= idx;
+ for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
+ // Clear pixel completely for transparency.
+ if (idx & bit) {
+ *decoded = 0;
+ }
+ decoded++;
+ }
+ }
+
+ if (mDownscaler) {
+ mDownscaler->CommitRow();
+ }
+
+ // If any bits are set in sawTransparency, then we know at least one pixel was
+ // transparent.
+ if (sawTransparency) {
+ mHasMaskAlpha = true;
+ }
+
+ if (mCurrMaskLine == 0) {
+ return Transition::To(ICOState::FINISH_MASK, 0);
+ }
+
+ return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
+}
+
+LexerTransition<ICOState>
+nsICODecoder::FinishMask()
+{
+ // If we're downscaling, we now have the appropriate alpha values in
+ // mMaskBuffer. We just need to transfer them to the image.
+ if (mDownscaler) {
+ // Retrieve the image data.
+ RefPtr<nsBMPDecoder> bmpDecoder =
+ static_cast<nsBMPDecoder*>(mContainedDecoder.get());
+ uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
+ if (!imageData) {
+ return Transition::TerminateFailure();
+ }
+
+ // Iterate through the alpha values, copying from mask to image.
+ MOZ_ASSERT(mMaskBuffer);
+ MOZ_ASSERT(bmpDecoder->GetImageDataLength() > 0);
+ for (size_t i = 3 ; i < bmpDecoder->GetImageDataLength() ; i += 4) {
+ imageData[i] = mMaskBuffer[i];
+ }
+ }
+
+ return Transition::To(ICOState::FINISHED_RESOURCE, 0);
+}
+
+LexerTransition<ICOState>
+nsICODecoder::FinishResource()
+{
+ // Make sure the actual size of the resource matches the size in the directory
+ // entry. If not, we consider the image corrupt.
+ if (mContainedDecoder->HasSize() &&
+ mContainedDecoder->Size() != GetRealSize()) {
+ return Transition::TerminateFailure();
+ }
+
+ return Transition::TerminateSuccess();
+}
+
+LexerResult
+nsICODecoder::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
+{
+ MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
+
+ return mLexer.Lex(aIterator, aOnResume,
+ [=](ICOState aState, const char* aData, size_t aLength) {
+ switch (aState) {
+ case ICOState::HEADER:
+ return ReadHeader(aData);
+ case ICOState::DIR_ENTRY:
+ return ReadDirEntry(aData);
+ case ICOState::SKIP_TO_RESOURCE:
+ return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
+ case ICOState::FOUND_RESOURCE:
+ return Transition::To(ICOState::SNIFF_RESOURCE, PNGSIGNATURESIZE);
+ case ICOState::SNIFF_RESOURCE:
+ return SniffResource(aData);
+ case ICOState::READ_PNG:
+ return ReadPNG(aData, aLength);
+ case ICOState::READ_BIH:
+ return ReadBIH(aData);
+ case ICOState::READ_BMP:
+ return ReadBMP(aData, aLength);
+ case ICOState::PREPARE_FOR_MASK:
+ return PrepareForMask();
+ case ICOState::READ_MASK_ROW:
+ return ReadMaskRow(aData);
+ case ICOState::FINISH_MASK:
+ return FinishMask();
+ case ICOState::SKIP_MASK:
+ return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
+ case ICOState::FINISHED_RESOURCE:
+ return FinishResource();
+ default:
+ MOZ_CRASH("Unknown ICOState");
+ }
+ });
+}
+
+bool
+nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount)
+{
+ MOZ_ASSERT(mContainedDecoder);
+ MOZ_ASSERT(mContainedSourceBuffer);
+
+ // Append the provided data to the SourceBuffer that the contained decoder is
+ // reading from.
+ mContainedSourceBuffer->Append(aBuffer, aCount);
+
+ bool succeeded = true;
+
+ // Write to the contained decoder. If we run out of data, the ICO decoder will
+ // get resumed when there's more data available, as usual, so we don't need
+ // the contained decoder to get resumed too. To avoid that, we provide an
+ // IResumable which just does nothing.
+ LexerResult result = mContainedDecoder->Decode();
+ if (result == LexerResult(TerminalState::FAILURE)) {
+ succeeded = false;
+ }
+
+ MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
+ "Unexpected yield");
+
+ // Make our state the same as the state of the contained decoder, and
+ // propagate errors.
+ mProgress |= mContainedDecoder->TakeProgress();
+ mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
+ if (mContainedDecoder->HasError()) {
+ succeeded = false;
+ }
+
+ return succeeded;
+}
+
+} // namespace image
+} // namespace mozilla