// Copyright 2016 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/351564777): Remove this and convert code to safer constructs. #pragma allow_unsafe_buffers #endif #include "third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.h" #include <memory> #include "base/logging.h" #include "base/time/time.h" #include "png.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h" #include "third_party/blink/renderer/platform/wtf/shared_buffer.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/skia/include/core/SkColorPriv.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkRefCnt.h" // web_tests/images/resources/png-animated-idat-part-of-animation.png // is modified in multiple tests to simulate erroneous PNGs. As a reference, // the table below shows how the file is structured. // // Offset | 8 33 95 133 172 210 241 279 314 352 422 // ------------------------------------------------------------------------- // Chunk | IHDR acTL fcTL IDAT fcTL fdAT fcTL fdAT fcTL fdAT IEND // // In between the acTL and fcTL there are two other chunks, PLTE and tRNS, but // those are not specifically used in this test suite. The same holds for a // tEXT chunk in between the last fdAT and IEND. // // In the current behavior of PNG image decoders, the 4 frames are detected when // respectively 141, 249, 322 and 430 bytes are received. The first frame should // be detected when the IDAT has been received, and non-first frames when the // next fcTL or IEND chunk has been received. Note that all offsets are +8, // because a chunk is identified by byte 4-7. namespace blink { namespace { std::unique_ptr<ImageDecoder> CreatePNGDecoder( ImageDecoder::AlphaOption alpha_option) { … } std::unique_ptr<ImageDecoder> CreatePNGDecoder() { … } std::unique_ptr<ImageDecoder> Create16BitPNGDecoder() { … } std::unique_ptr<ImageDecoder> CreatePNGDecoderWithPngData( const char* png_file) { … } void TestSize(const char* png_file, gfx::Size expected_size) { … } // Test whether querying for the size of the image works if we present the // data byte by byte. void TestSizeByteByByte(const char* png_file, size_t bytes_needed_to_decode_size, gfx::Size expected_size) { … } void WriteUint32(uint32_t val, png_byte* data) { … } void TestRepetitionCount(const char* png_file, int expected_repetition_count) { … } struct PublicFrameInfo { … }; // This is the frame data for the following PNG image: // web_tests/images/resources/png-animated-idat-part-of-animation.png static PublicFrameInfo g_png_animated_frame_info[] = …; void CompareFrameWithExpectation(const PublicFrameInfo& expected, ImageDecoder* decoder, size_t index) { … } // This function removes |length| bytes at |offset|, and then calls FrameCount. // It assumes the missing bytes should result in a failed decode because the // parser jumps |length| bytes too far in the next chunk. void TestMissingDataBreaksDecoding(const char* png_file, size_t offset, size_t length) { … } // Verify that a decoder with a parse error converts to a static image. static void ExpectStatic(ImageDecoder* decoder) { … } // Decode up to the indicated fcTL offset and then provide an fcTL with the // wrong chunk size (20 instead of 26). void TestInvalidFctlSize(const char* png_file, size_t offset_fctl, size_t expected_frame_count, bool should_fail) { … } // Verify that the decoder can successfully decode the first frame when // initially only half of the frame data is received, resulting in a partially // decoded image, and then the rest of the image data is received. Verify that // the bitmap hashes of the two stages are different. Also verify that the final // bitmap hash is equivalent to the hash when all data is provided at once. // // This verifies that the decoder correctly keeps track of where it stopped // decoding when the image was not yet fully received. void TestProgressiveDecodingContinuesAfterFullData( const char* png_file, size_t offset_mid_first_frame) { … } } // Anonymous namespace // Animated PNG Tests TEST(AnimatedPNGTests, sizeTest) { … } TEST(AnimatedPNGTests, repetitionCountTest) { … } // Test if the decoded metdata corresponds to the defined expectations TEST(AnimatedPNGTests, MetaDataTest) { … } TEST(AnimatedPNGTests, EmptyFrame) { … } TEST(AnimatedPNGTests, ByteByByteSizeAvailable) { … } TEST(AnimatedPNGTests, ByteByByteMetaData) { … } TEST(AnimatedPNGTests, TestRandomFrameDecode) { … } TEST(AnimatedPNGTests, TestDecodeAfterReallocation) { … } TEST(AnimatedPNGTests, ProgressiveDecode) { … } TEST(AnimatedPNGTests, ParseAndDecodeByteByByte) { … } TEST(AnimatedPNGTests, FailureDuringParsing) { … } TEST(AnimatedPNGTests, ActlErrors) { … } TEST(AnimatedPNGTests, fdatBeforeIdat) { … } TEST(AnimatedPNGTests, FrameOverflowX) { … } // This test is exactly the same as above, except it changes y_offset. TEST(AnimatedPNGTests, FrameOverflowY) { … } TEST(AnimatedPNGTests, IdatSizeMismatch) { … } TEST(AnimatedPNGTests, EmptyFdatFails) { … } // Originally, the third frame has an offset of (1,2) and a size of (3,2). By // changing the offset to (4,4), the frame rect is no longer within the image // size of 5x5. This results in a failure. TEST(AnimatedPNGTests, VerifyFrameOutsideImageSizeFails) { … } TEST(AnimatedPNGTests, ProgressiveDecodingContinuesAfterFullData) { … } TEST(AnimatedPNGTests, RandomDecodeAfterClearFrameBufferCache) { … } TEST(AnimatedPNGTests, VerifyAlphaBlending) { … } // This tests if the frame count gets set correctly when parsing FrameCount // fails in one of the parsing queries. // // First, enough data is provided such that two frames should be registered. // The decoder should at this point not be in the failed status. // // Then, we provide the rest of the data except for the last IEND chunk, but // tell the decoder that this is all the data we have. The frame count should // be three, since one extra frame should be discovered. The fourth frame // should *not* be registered since the reader should not be able to determine // where the frame ends. The decoder should *not* be in the failed state since // there are three frames which can be shown. // Attempting to decode the third frame should fail, since the file is // truncated. TEST(AnimatedPNGTests, FailureMissingIendChunk) { … } // Verify that a malformatted PNG, where the IEND appears before any frame data // (IDAT), invalidates the decoder. TEST(AnimatedPNGTests, VerifyIENDBeforeIDATInvalidatesDecoder) { … } // All IDAT chunks must be before all fdAT chunks TEST(AnimatedPNGTests, MixedDataChunks) { … } // Verify that erroneous values for the disposal method and alpha blending // cause the decoder to fail. TEST(AnimatedPNGTests, VerifyInvalidDisposalAndBlending) { … } // This test verifies that the following situation does not invalidate the // decoder: // - Frame 0 is decoded progressively, but there's not enough data to fully // decode it. // - The rest of the image data is received. // - Frame X, with X > 0, and X does not depend on frame 0, is decoded. // - Frame 0 is decoded. // This is a tricky case since the decoder resets the png struct for each frame, // and this test verifies that it does not break the decoding of frame 0, even // though it already started in the first call. TEST(AnimatedPNGTests, VerifySuccessfulFirstFrameDecodeAfterLaterFrame) { … } // If the decoder attempts to decode a non-first frame which is subset and // independent, it needs to discard its png_struct so it can use a modified // IHDR. Test this by comparing a decode of frame 1 after frame 0 to a decode // of frame 1 without decoding frame 0. TEST(AnimatedPNGTests, DecodeFromIndependentFrame) { … } // If the first frame is subset from IHDR (only allowed if the first frame is // not the default image), the decoder has to destroy the png_struct it used // for parsing so it can use a modified IHDR. TEST(AnimatedPNGTests, SubsetFromIHDR) { … } TEST(AnimatedPNGTests, Offset) { … } TEST(AnimatedPNGTests, ExtraChunksBeforeIHDR) { … } // Static PNG tests TEST(StaticPNGTests, repetitionCountTest) { … } TEST(StaticPNGTests, sizeTest) { … } TEST(StaticPNGTests, MetaDataTest) { … } // circle-trns-before-plte.png is of color type 2 (PNG_COLOR_TYPE_RGB) and has // a tRNS chunk before a PLTE chunk. The image has an opaque blue circle on a // transparent green background. // // The PNG specification version 1.2 says: // When present, the tRNS chunk must precede the first IDAT chunk, and must // follow the PLTE chunk, if any. // Therefore, in the default libpng configuration (which defines the // PNG_READ_OPT_PLTE_SUPPORTED macro), the tRNS chunk is considered invalid and // ignored. However, png_get_valid(png, info, PNG_INFO_tRNS) still returns a // nonzero value, so an application may call png_set_tRNS_to_alpha(png) and // assume libpng's output has alpha, resulting in memory errors. See // https://github.com/glennrp/libpng/issues/482. // // Since Chromium chooses to undefine PNG_READ_OPT_PLTE_SUPPORTED in // pnglibconf.h, it is not affected by this potential bug. For extra assurance, // this test decodes this image and makes sure there are no errors. TEST(StaticPNGTests, ColorType2TrnsBeforePlte) { … } TEST(StaticPNGTests, InvalidIHDRChunk) { … } TEST(StaticPNGTests, ProgressiveDecoding) { … } TEST(StaticPNGTests, ProgressiveDecodingContinuesAfterFullData) { … } struct PNGSample { … }; static void TestHighBitDepthPNGDecoding(const PNGSample& png_sample, ImageDecoder* decoder) { … } static void FillPNGSamplesSourcePixels(Vector<PNGSample>& png_samples) { … } static Vector<PNGSample> GetPNGSamplesInfo(bool include_8bit_pngs) { … } TEST(StaticPNGTests, DecodeHighBitDepthPngToHalfFloat) { … } TEST(StaticPNGTests, ImageIsHighBitDepth) { … } TEST(PNGTests, VerifyFrameCompleteBehavior) { … } TEST(PNGTests, sizeMayOverflow) { … } TEST(PNGTests, truncated) { … } TEST(PNGTests, crbug827754) { … } TEST(PNGTests, cicp) { … } TEST(PNGTests, HDRMetadata) { … } TEST(AnimatedPNGTests, TrnsMeansAlpha) { … } TEST(PNGTests, CriticalPrivateChunkBeforeIHDR) { … } } // namespace blink