1 /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /* This is a Cross-Platform ICO Decoder, which should work everywhere, including
7  * Big-Endian machines like the PowerPC. */
8 
9 #include "nsICODecoder.h"
10 
11 #include <stdlib.h>
12 
13 #include <utility>
14 
15 #include "RasterImage.h"
16 #include "mozilla/EndianUtils.h"
17 #include "mozilla/gfx/Swizzle.h"
18 
19 using namespace mozilla::gfx;
20 
21 namespace mozilla {
22 namespace image {
23 
24 // Constants.
25 static const uint32_t ICOHEADERSIZE = 6;
26 static const uint32_t BITMAPINFOSIZE = bmp::InfoHeaderLength::WIN_ICO;
27 
28 // ----------------------------------------
29 // Actual Data Processing
30 // ----------------------------------------
31 
32 // Obtains the number of colors from the bits per pixel
GetNumColors()33 uint16_t nsICODecoder::GetNumColors() {
34   uint16_t numColors = 0;
35   if (mBPP <= 8) {
36     switch (mBPP) {
37       case 1:
38         numColors = 2;
39         break;
40       case 4:
41         numColors = 16;
42         break;
43       case 8:
44         numColors = 256;
45         break;
46       default:
47         numColors = (uint16_t)-1;
48     }
49   }
50   return numColors;
51 }
52 
nsICODecoder(RasterImage * aImage)53 nsICODecoder::nsICODecoder(RasterImage* aImage)
54     : Decoder(aImage),
55       mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
56              Transition::TerminateSuccess()),
57       mDirEntry(nullptr),
58       mNumIcons(0),
59       mCurrIcon(0),
60       mBPP(0),
61       mMaskRowSize(0),
62       mCurrMaskLine(0),
63       mIsCursor(false),
64       mHasMaskAlpha(false) {}
65 
FinishInternal()66 nsresult nsICODecoder::FinishInternal() {
67   // We shouldn't be called in error cases
68   MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
69 
70   return GetFinalStateFromContainedDecoder();
71 }
72 
FinishWithErrorInternal()73 nsresult nsICODecoder::FinishWithErrorInternal() {
74   // No need to assert !mInFrame here because this condition is enforced by
75   // mContainedDecoder.
76   return GetFinalStateFromContainedDecoder();
77 }
78 
GetFinalStateFromContainedDecoder()79 nsresult nsICODecoder::GetFinalStateFromContainedDecoder() {
80   if (!mContainedDecoder) {
81     return NS_OK;
82   }
83 
84   // Let the contained decoder finish up if necessary.
85   FlushContainedDecoder();
86 
87   // Make our state the same as the state of the contained decoder.
88   mDecodeDone = mContainedDecoder->GetDecodeDone();
89   mProgress |= mContainedDecoder->TakeProgress();
90   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
91   mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
92 
93   // Finalize the frame which we deferred to ensure we could modify the final
94   // result (e.g. to apply the BMP mask).
95   MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames());
96   if (mCurrentFrame) {
97     mCurrentFrame->FinalizeSurface();
98   }
99 
100   // Propagate errors.
101   nsresult rv =
102       HasError() || mContainedDecoder->HasError() ? NS_ERROR_FAILURE : NS_OK;
103 
104   MOZ_ASSERT(NS_FAILED(rv) || !mCurrentFrame || mCurrentFrame->IsFinished());
105   return rv;
106 }
107 
ReadHeader(const char * aData)108 LexerTransition<ICOState> nsICODecoder::ReadHeader(const char* aData) {
109   // If the third byte is 1, this is an icon. If 2, a cursor.
110   if ((aData[2] != 1) && (aData[2] != 2)) {
111     return Transition::TerminateFailure();
112   }
113   mIsCursor = (aData[2] == 2);
114 
115   // The fifth and sixth bytes specify the number of resources in the file.
116   mNumIcons = LittleEndian::readUint16(aData + 4);
117   if (mNumIcons == 0) {
118     return Transition::TerminateSuccess();  // Nothing to do.
119   }
120 
121   // Downscale-during-decode can end up decoding different resources in the ICO
122   // file depending on the target size. Since the resources are not necessarily
123   // scaled versions of the same image, some may be transparent and some may not
124   // be. We could be precise about transparency if we decoded the metadata of
125   // every resource, but for now we don't and it's safest to assume that
126   // transparency could be present.
127   PostHasTransparency();
128 
129   return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
130 }
131 
FirstResourceOffset() const132 size_t nsICODecoder::FirstResourceOffset() const {
133   MOZ_ASSERT(mNumIcons > 0,
134              "Calling FirstResourceOffset before processing header");
135 
136   // The first resource starts right after the directory, which starts right
137   // after the ICO header.
138   return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
139 }
140 
ReadDirEntry(const char * aData)141 LexerTransition<ICOState> nsICODecoder::ReadDirEntry(const char* aData) {
142   mCurrIcon++;
143 
144   // Ensure the resource has an offset past the ICO headers.
145   uint32_t offset = LittleEndian::readUint32(aData + 12);
146   if (offset >= FirstResourceOffset()) {
147     // Read the directory entry.
148     IconDirEntryEx e;
149     e.mWidth = aData[0];
150     e.mHeight = aData[1];
151     e.mColorCount = aData[2];
152     e.mReserved = aData[3];
153     e.mPlanes = LittleEndian::readUint16(aData + 4);
154     e.mBitCount = LittleEndian::readUint16(aData + 6);
155     e.mBytesInRes = LittleEndian::readUint32(aData + 8);
156     e.mImageOffset = offset;
157     e.mSize = IntSize(e.mWidth, e.mHeight);
158 
159     // Only accept entries with sufficient resource data to actually contain
160     // some image data.
161     if (e.mBytesInRes > BITMAPINFOSIZE) {
162       if (e.mWidth == 0 || e.mHeight == 0) {
163         mUnsizedDirEntries.AppendElement(e);
164       } else {
165         mDirEntries.AppendElement(e);
166       }
167     }
168   }
169 
170   if (mCurrIcon == mNumIcons) {
171     if (mUnsizedDirEntries.IsEmpty()) {
172       return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
173     }
174     return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
175   }
176 
177   return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
178 }
179 
IterateUnsizedDirEntry()180 LexerTransition<ICOState> nsICODecoder::IterateUnsizedDirEntry() {
181   MOZ_ASSERT(!mUnsizedDirEntries.IsEmpty());
182 
183   if (!mDirEntry) {
184     // The first time we are here, there is no entry selected. We must prepare a
185     // new iterator for the contained decoder to advance as it wills. Cloning at
186     // this point ensures it will begin at the end of the dir entries.
187     mReturnIterator = mLexer.Clone(*mIterator, SIZE_MAX);
188     if (mReturnIterator.isNothing()) {
189       // If we cannot read further than this point, then there is no resource
190       // data to read.
191       return Transition::TerminateFailure();
192     }
193   } else {
194     // We have already selected an entry which means a metadata decoder has
195     // finished. Verify the size is valid and if so, add to the discovered
196     // resources.
197     if (mDirEntry->mSize.width > 0 && mDirEntry->mSize.height > 0) {
198       mDirEntries.AppendElement(*mDirEntry);
199     }
200 
201     // Remove the entry from the unsized list either way.
202     mDirEntry = nullptr;
203     mUnsizedDirEntries.RemoveElementAt(0);
204 
205     // Our iterator is at an unknown point, so reset it to the point that we
206     // saved.
207     mIterator = mLexer.Clone(*mReturnIterator, SIZE_MAX);
208     if (mIterator.isNothing()) {
209       MOZ_ASSERT_UNREACHABLE("Cannot re-clone return iterator");
210       return Transition::TerminateFailure();
211     }
212   }
213 
214   // There are no more unsized entries, so we can finally decide which entry to
215   // select for decoding.
216   if (mUnsizedDirEntries.IsEmpty()) {
217     mReturnIterator.reset();
218     return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
219   }
220 
221   // Move to the resource data to start metadata decoding.
222   mDirEntry = &mUnsizedDirEntries[0];
223   size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
224   return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
225                                   ICOState::SKIP_TO_RESOURCE, offsetToResource);
226 }
227 
FinishDirEntry()228 LexerTransition<ICOState> nsICODecoder::FinishDirEntry() {
229   MOZ_ASSERT(!mDirEntry);
230 
231   if (mDirEntries.IsEmpty()) {
232     return Transition::TerminateFailure();
233   }
234 
235   // If an explicit output size was specified, we'll try to select the resource
236   // that matches it best below.
237   const Maybe<IntSize> desiredSize = ExplicitOutputSize();
238 
239   // Determine the biggest resource. We always use the biggest resource for the
240   // intrinsic size, and if we don't have a specific desired size, we select it
241   // as the best resource as well.
242   int32_t bestDelta = INT32_MIN;
243   IconDirEntryEx* biggestEntry = nullptr;
244 
245   for (size_t i = 0; i < mDirEntries.Length(); ++i) {
246     IconDirEntryEx& e = mDirEntries[i];
247     mImageMetadata.AddNativeSize(e.mSize);
248 
249     if (!biggestEntry ||
250         (e.mBitCount >= biggestEntry->mBitCount &&
251          e.mSize.width * e.mSize.height >=
252              biggestEntry->mSize.width * biggestEntry->mSize.height)) {
253       biggestEntry = &e;
254 
255       if (!desiredSize) {
256         mDirEntry = &e;
257       }
258     }
259 
260     if (desiredSize) {
261       // Calculate the delta between this resource's size and the desired size,
262       // so we can see if it is better than our current-best option.  In the
263       // case of several equally-good resources, we use the last one. "Better"
264       // in this case is determined by |delta|, a measure of the difference in
265       // size between the entry we've found and the desired size. We will choose
266       // the smallest resource that is greater than or equal to the desired size
267       // (i.e. we assume it's better to downscale a larger icon than to upscale
268       // a smaller one).
269       int32_t delta = std::min(e.mSize.width - desiredSize->width,
270                                e.mSize.height - desiredSize->height);
271       if (!mDirEntry || (e.mBitCount >= mDirEntry->mBitCount &&
272                          ((bestDelta < 0 && delta >= bestDelta) ||
273                           (delta >= 0 && delta <= bestDelta)))) {
274         mDirEntry = &e;
275         bestDelta = delta;
276       }
277     }
278   }
279 
280   MOZ_ASSERT(mDirEntry);
281   MOZ_ASSERT(biggestEntry);
282 
283   // If this is a cursor, set the hotspot. We use the hotspot from the biggest
284   // resource since we also use that resource for the intrinsic size.
285   if (mIsCursor) {
286     mImageMetadata.SetHotspot(biggestEntry->mXHotspot, biggestEntry->mYHotspot);
287   }
288 
289   // We always report the biggest resource's size as the intrinsic size; this
290   // is necessary for downscale-during-decode to work since we won't even
291   // attempt to *upscale* while decoding.
292   PostSize(biggestEntry->mSize.width, biggestEntry->mSize.height);
293   if (HasError()) {
294     return Transition::TerminateFailure();
295   }
296 
297   if (IsMetadataDecode()) {
298     return Transition::TerminateSuccess();
299   }
300 
301   if (mDirEntry->mSize == OutputSize()) {
302     // If the resource we selected matches the output size perfectly, we don't
303     // need to do any downscaling.
304     MOZ_ASSERT_IF(desiredSize, mDirEntry->mSize == *desiredSize);
305     MOZ_ASSERT_IF(!desiredSize, mDirEntry->mSize == Size());
306   } else if (OutputSize().width < mDirEntry->mSize.width ||
307              OutputSize().height < mDirEntry->mSize.height) {
308     // Create a downscaler if we need to downscale.
309     //
310     // TODO(aosmond): This is the last user of Downscaler. We should switch this
311     // to SurfacePipe as well so we can remove the code from tree.
312     mDownscaler.emplace(OutputSize());
313   }
314 
315   size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
316   return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
317                                   ICOState::SKIP_TO_RESOURCE, offsetToResource);
318 }
319 
SniffResource(const char * aData)320 LexerTransition<ICOState> nsICODecoder::SniffResource(const char* aData) {
321   MOZ_ASSERT(mDirEntry);
322 
323   // We have BITMAPINFOSIZE bytes buffered at this point. We know an embedded
324   // BMP will have at least that many bytes by definition. We can also infer
325   // that any valid embedded PNG will contain that many bytes as well because:
326   //    BITMAPINFOSIZE
327   //      <
328   //    signature (8 bytes) +
329   //    IHDR (12 bytes header + 13 bytes data)
330   //    IDAT (12 bytes header)
331 
332   // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
333   // is a PNG or a BMP.
334   bool isPNG =
335       !memcmp(aData, nsPNGDecoder::pngSignatureBytes, PNGSIGNATURESIZE);
336   if (isPNG) {
337     if (mDirEntry->mBytesInRes <= BITMAPINFOSIZE) {
338       return Transition::TerminateFailure();
339     }
340 
341     // Prepare a new iterator for the contained decoder to advance as it wills.
342     // Cloning at the point ensures it will begin at the resource offset.
343     Maybe<SourceBufferIterator> containedIterator =
344         mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
345     if (containedIterator.isNothing()) {
346       return Transition::TerminateFailure();
347     }
348 
349     // Create a PNG decoder which will do the rest of the work for us.
350     bool metadataDecode = mReturnIterator.isSome();
351     Maybe<IntSize> expectedSize =
352         metadataDecode ? Nothing() : Some(mDirEntry->mSize);
353     mContainedDecoder = DecoderFactory::CreateDecoderForICOResource(
354         DecoderType::PNG, std::move(containedIterator.ref()), WrapNotNull(this),
355         metadataDecode, expectedSize);
356 
357     // Read in the rest of the PNG unbuffered.
358     size_t toRead = mDirEntry->mBytesInRes - BITMAPINFOSIZE;
359     return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
360                                     ICOState::READ_RESOURCE, toRead);
361   }
362 
363   // Make sure we have a sane size for the bitmap information header.
364   int32_t bihSize = LittleEndian::readUint32(aData);
365   if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
366     return Transition::TerminateFailure();
367   }
368 
369   // Read in the rest of the bitmap information header.
370   return ReadBIH(aData);
371 }
372 
ReadResource()373 LexerTransition<ICOState> nsICODecoder::ReadResource() {
374   if (!FlushContainedDecoder()) {
375     return Transition::TerminateFailure();
376   }
377 
378   return Transition::ContinueUnbuffered(ICOState::READ_RESOURCE);
379 }
380 
ReadBIH(const char * aData)381 LexerTransition<ICOState> nsICODecoder::ReadBIH(const char* aData) {
382   MOZ_ASSERT(mDirEntry);
383 
384   // Extract the BPP from the BIH header; it should be trusted over the one
385   // we have from the ICO header which is usually set to 0.
386   mBPP = LittleEndian::readUint16(aData + 14);
387 
388   // Check to make sure we have valid color settings.
389   uint16_t numColors = GetNumColors();
390   if (numColors == uint16_t(-1)) {
391     return Transition::TerminateFailure();
392   }
393 
394   // The color table is present only if BPP is <= 8.
395   MOZ_ASSERT_IF(mBPP > 8, numColors == 0);
396 
397   // The ICO format when containing a BMP does not include the 14 byte
398   // bitmap file header. So we create the BMP decoder via the constructor that
399   // tells it to skip this, and pass in the required data (dataOffset) that
400   // would have been present in the header.
401   uint32_t dataOffset =
402       bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE + 4 * numColors;
403 
404   // Prepare a new iterator for the contained decoder to advance as it wills.
405   // Cloning at the point ensures it will begin at the resource offset.
406   Maybe<SourceBufferIterator> containedIterator =
407       mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
408   if (containedIterator.isNothing()) {
409     return Transition::TerminateFailure();
410   }
411 
412   // Create a BMP decoder which will do most of the work for us; the exception
413   // is the AND mask, which isn't present in standalone BMPs.
414   bool metadataDecode = mReturnIterator.isSome();
415   Maybe<IntSize> expectedSize =
416       metadataDecode ? Nothing() : Some(mDirEntry->mSize);
417   mContainedDecoder = DecoderFactory::CreateDecoderForICOResource(
418       DecoderType::BMP, std::move(containedIterator.ref()), WrapNotNull(this),
419       metadataDecode, expectedSize, Some(dataOffset));
420 
421   RefPtr<nsBMPDecoder> bmpDecoder =
422       static_cast<nsBMPDecoder*>(mContainedDecoder.get());
423 
424   // Ensure the decoder has parsed at least the BMP's bitmap info header.
425   if (!FlushContainedDecoder()) {
426     return Transition::TerminateFailure();
427   }
428 
429   // If this is a metadata decode, FinishResource will any necessary checks.
430   if (mContainedDecoder->IsMetadataDecode()) {
431     return Transition::To(ICOState::FINISHED_RESOURCE, 0);
432   }
433 
434   // Do we have an AND mask on this BMP? If so, we need to read it after we read
435   // the BMP data itself.
436   uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
437   bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry->mBytesInRes;
438   ICOState afterBMPState =
439       hasANDMask ? ICOState::PREPARE_FOR_MASK : ICOState::FINISHED_RESOURCE;
440 
441   // Read in the rest of the BMP unbuffered.
442   return Transition::ToUnbuffered(afterBMPState, ICOState::READ_RESOURCE,
443                                   bmpDataLength);
444 }
445 
PrepareForMask()446 LexerTransition<ICOState> nsICODecoder::PrepareForMask() {
447   MOZ_ASSERT(mDirEntry);
448   MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
449 
450   // We have received all of the data required by the BMP decoder so flushing
451   // here guarantees the decode has finished.
452   if (!FlushContainedDecoder()) {
453     return Transition::TerminateFailure();
454   }
455 
456   MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
457 
458   RefPtr<nsBMPDecoder> bmpDecoder =
459       static_cast<nsBMPDecoder*>(mContainedDecoder.get());
460 
461   uint16_t numColors = GetNumColors();
462   MOZ_ASSERT(numColors != uint16_t(-1));
463 
464   // Determine the length of the AND mask.
465   uint32_t bmpLengthWithHeader =
466       BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
467   MOZ_ASSERT(bmpLengthWithHeader < mDirEntry->mBytesInRes);
468   uint32_t maskLength = mDirEntry->mBytesInRes - bmpLengthWithHeader;
469 
470   // If the BMP provides its own transparency, we ignore the AND mask.
471   if (bmpDecoder->HasTransparency()) {
472     return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
473                                     ICOState::SKIP_MASK, maskLength);
474   }
475 
476   // Compute the row size for the mask.
477   mMaskRowSize = ((mDirEntry->mSize.width + 31) / 32) * 4;  // + 31 to round up
478 
479   // If the expected size of the AND mask is larger than its actual size, then
480   // we must have a truncated (and therefore corrupt) AND mask.
481   uint32_t expectedLength = mMaskRowSize * mDirEntry->mSize.height;
482   if (maskLength < expectedLength) {
483     return Transition::TerminateFailure();
484   }
485 
486   // If we're downscaling, the mask is the wrong size for the surface we've
487   // produced, so we need to downscale the mask into a temporary buffer and then
488   // combine the mask's alpha values with the color values from the image.
489   if (mDownscaler) {
490     MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
491                mDownscaler->TargetSize().width *
492                    mDownscaler->TargetSize().height * sizeof(uint32_t));
493     mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
494     nsresult rv =
495         mDownscaler->BeginFrame(mDirEntry->mSize, Nothing(), mMaskBuffer.get(),
496                                 /* aHasAlpha = */ true,
497                                 /* aFlipVertically = */ true);
498     if (NS_FAILED(rv)) {
499       return Transition::TerminateFailure();
500     }
501   }
502 
503   mCurrMaskLine = mDirEntry->mSize.height;
504   return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
505 }
506 
ReadMaskRow(const char * aData)507 LexerTransition<ICOState> nsICODecoder::ReadMaskRow(const char* aData) {
508   MOZ_ASSERT(mDirEntry);
509 
510   mCurrMaskLine--;
511 
512   uint8_t sawTransparency = 0;
513 
514   // Get the mask row we're reading.
515   const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
516   const uint8_t* maskRowEnd = mask + mMaskRowSize;
517 
518   // Get the corresponding row of the mask buffer (if we're downscaling) or the
519   // decoded image data (if we're not).
520   uint32_t* decoded = nullptr;
521   if (mDownscaler) {
522     // Initialize the row to all white and fully opaque.
523     memset(mDownscaler->RowBuffer(), 0xFF,
524            mDirEntry->mSize.width * sizeof(uint32_t));
525 
526     decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
527   } else {
528     RefPtr<nsBMPDecoder> bmpDecoder =
529         static_cast<nsBMPDecoder*>(mContainedDecoder.get());
530     uint32_t* imageData = bmpDecoder->GetImageData();
531     if (!imageData) {
532       return Transition::TerminateFailure();
533     }
534 
535     decoded = imageData + mCurrMaskLine * mDirEntry->mSize.width;
536   }
537 
538   MOZ_ASSERT(decoded);
539   uint32_t* decodedRowEnd = decoded + mDirEntry->mSize.width;
540 
541   // Iterate simultaneously through the AND mask and the image data.
542   while (mask < maskRowEnd) {
543     uint8_t idx = *mask++;
544     sawTransparency |= idx;
545     for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
546       // Clear pixel completely for transparency.
547       if (idx & bit) {
548         *decoded = 0;
549       }
550       decoded++;
551     }
552   }
553 
554   if (mDownscaler) {
555     mDownscaler->CommitRow();
556   }
557 
558   // If any bits are set in sawTransparency, then we know at least one pixel was
559   // transparent.
560   if (sawTransparency) {
561     mHasMaskAlpha = true;
562   }
563 
564   if (mCurrMaskLine == 0) {
565     return Transition::To(ICOState::FINISH_MASK, 0);
566   }
567 
568   return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
569 }
570 
FinishMask()571 LexerTransition<ICOState> nsICODecoder::FinishMask() {
572   // If we're downscaling, we now have the appropriate alpha values in
573   // mMaskBuffer. We just need to transfer them to the image.
574   if (mDownscaler) {
575     // Retrieve the image data.
576     RefPtr<nsBMPDecoder> bmpDecoder =
577         static_cast<nsBMPDecoder*>(mContainedDecoder.get());
578     uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
579     if (!imageData) {
580       return Transition::TerminateFailure();
581     }
582 
583     // Iterate through the alpha values, copying from mask to image.
584     MOZ_ASSERT(mMaskBuffer);
585     MOZ_ASSERT(bmpDecoder->GetImageDataLength() > 0);
586     for (size_t i = 3; i < bmpDecoder->GetImageDataLength(); i += 4) {
587       imageData[i] = mMaskBuffer[i];
588     }
589     int32_t stride = mDownscaler->TargetSize().width * sizeof(uint32_t);
590     DebugOnly<bool> ret =
591         // We know the format is OS_RGBA because we always assume bmp's inside
592         // ico's are transparent.
593         PremultiplyData(imageData, stride, SurfaceFormat::OS_RGBA, imageData,
594                         stride, SurfaceFormat::OS_RGBA,
595                         mDownscaler->TargetSize());
596     MOZ_ASSERT(ret);
597   }
598 
599   return Transition::To(ICOState::FINISHED_RESOURCE, 0);
600 }
601 
FinishResource()602 LexerTransition<ICOState> nsICODecoder::FinishResource() {
603   MOZ_ASSERT(mDirEntry);
604 
605   // We have received all of the data required by the PNG/BMP decoder so
606   // flushing here guarantees the decode has finished.
607   if (!FlushContainedDecoder()) {
608     return Transition::TerminateFailure();
609   }
610 
611   MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
612 
613   // If it is a metadata decode, all we were trying to get was the size
614   // information missing from the dir entry.
615   if (mContainedDecoder->IsMetadataDecode()) {
616     if (mContainedDecoder->HasSize()) {
617       mDirEntry->mSize = mContainedDecoder->Size();
618     }
619     return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
620   }
621 
622   // Raymond Chen says that 32bpp only are valid PNG ICOs
623   // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
624   if (!mContainedDecoder->IsValidICOResource()) {
625     return Transition::TerminateFailure();
626   }
627 
628   // This size from the resource should match that from the dir entry.
629   MOZ_ASSERT_IF(mContainedDecoder->HasSize(),
630                 mContainedDecoder->Size() == mDirEntry->mSize);
631 
632   return Transition::TerminateSuccess();
633 }
634 
DoDecode(SourceBufferIterator & aIterator,IResumable * aOnResume)635 LexerResult nsICODecoder::DoDecode(SourceBufferIterator& aIterator,
636                                    IResumable* aOnResume) {
637   MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
638 
639   return mLexer.Lex(
640       aIterator, aOnResume,
641       [=](ICOState aState, const char* aData, size_t aLength) {
642         switch (aState) {
643           case ICOState::HEADER:
644             return ReadHeader(aData);
645           case ICOState::DIR_ENTRY:
646             return ReadDirEntry(aData);
647           case ICOState::FINISHED_DIR_ENTRY:
648             return FinishDirEntry();
649           case ICOState::ITERATE_UNSIZED_DIR_ENTRY:
650             return IterateUnsizedDirEntry();
651           case ICOState::SKIP_TO_RESOURCE:
652             return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
653           case ICOState::FOUND_RESOURCE:
654             return Transition::To(ICOState::SNIFF_RESOURCE, BITMAPINFOSIZE);
655           case ICOState::SNIFF_RESOURCE:
656             return SniffResource(aData);
657           case ICOState::READ_RESOURCE:
658             return ReadResource();
659           case ICOState::PREPARE_FOR_MASK:
660             return PrepareForMask();
661           case ICOState::READ_MASK_ROW:
662             return ReadMaskRow(aData);
663           case ICOState::FINISH_MASK:
664             return FinishMask();
665           case ICOState::SKIP_MASK:
666             return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
667           case ICOState::FINISHED_RESOURCE:
668             return FinishResource();
669           default:
670             MOZ_CRASH("Unknown ICOState");
671         }
672       });
673 }
674 
FlushContainedDecoder()675 bool nsICODecoder::FlushContainedDecoder() {
676   MOZ_ASSERT(mContainedDecoder);
677 
678   bool succeeded = true;
679 
680   // If we run out of data, the ICO decoder will get resumed when there's more
681   // data available, as usual, so we don't need the contained decoder to get
682   // resumed too. To avoid that, we provide an IResumable which just does
683   // nothing. All the caller needs to do is flush when there is new data.
684   LexerResult result = mContainedDecoder->Decode();
685   if (result == LexerResult(TerminalState::FAILURE)) {
686     succeeded = false;
687   }
688 
689   MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
690              "Unexpected yield");
691 
692   // Make our state the same as the state of the contained decoder, and
693   // propagate errors.
694   mProgress |= mContainedDecoder->TakeProgress();
695   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
696   if (mContainedDecoder->HasError()) {
697     succeeded = false;
698   }
699 
700   return succeeded;
701 }
702 
703 }  // namespace image
704 }  // namespace mozilla
705