Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp

Issue 2618633004: Add support for Animated PNG (Closed)
Patch Set: Reject bad data. Cleanups Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "platform/image-decoders/png/PNGImageDecoder.h" 5 #include "platform/image-decoders/png/PNGImageDecoder.h"
6 6
7 #include "platform/image-decoders/ImageDecoderTestHelpers.h" 7 #include "platform/image-decoders/ImageDecoderTestHelpers.h"
8 #include "png.h"
8 #include "testing/gtest/include/gtest/gtest.h" 9 #include "testing/gtest/include/gtest/gtest.h"
9 #include <memory> 10 #include <memory>
10 11
12 // /LayoutTests/images/resources/png-animated-idat-part-of-animation.png
13 // is modified in multiple tests to simulate erroneous PNGs. As a reference,
14 // the table below shows how the file is structured.
15 //
16 // Offset | 8 33 95 133 172 210 241 279 314 352 422
17 // -------------------------------------------------------------------------
18 // Chunk | IHDR acTL fcTL IDAT fcTL fdAT fcTL fdAT fcTL fdAT IEND
19 //
20 // In between the acTL and fcTL there are two other chunks, PLTE and tRNS, but
21 // those are not specifically used in this test suite. The same holds for a
22 // tEXT chunk in between the last fdAT and IEND.
23 //
24 // In the current behavior of PNG image decoders, the 4 frames are detected when
25 // respectively 141, 249, 322 and 430 bytes are received. The first frame should
26 // be detected when the IDAT has been received, and non-first frames when the
27 // next fcTL or IEND chunk has been received. Note that all offsets are +8,
28 // because a chunk is identified by byte 4-7.
29
11 namespace blink { 30 namespace blink {
12 31
13 namespace { 32 namespace {
14 33
15 std::unique_ptr<ImageDecoder> createDecoder( 34 std::unique_ptr<ImageDecoder> createDecoder(
16 ImageDecoder::AlphaOption alphaOption) { 35 ImageDecoder::AlphaOption alphaOption) {
17 return WTF::wrapUnique(new PNGImageDecoder( 36 return WTF::wrapUnique(new PNGImageDecoder(
18 alphaOption, ColorBehavior::transformToTargetForTesting(), 37 alphaOption, ColorBehavior::transformToTargetForTesting(),
19 ImageDecoder::noDecodedImageByteLimit)); 38 ImageDecoder::noDecodedImageByteLimit));
20 } 39 }
21 40
22 std::unique_ptr<ImageDecoder> createDecoder() { 41 std::unique_ptr<ImageDecoder> createDecoder() {
23 return createDecoder(ImageDecoder::AlphaNotPremultiplied); 42 return createDecoder(ImageDecoder::AlphaNotPremultiplied);
24 } 43 }
25 44
26 std::unique_ptr<ImageDecoder> createDecoderWithPngData(const char* pngFile) { 45 std::unique_ptr<ImageDecoder> createDecoderWithPngData(const char* pngFile) {
27 auto decoder = createDecoder(); 46 auto decoder = createDecoder();
28 auto data = readFile(pngFile); 47 auto data = readFile(pngFile);
29 EXPECT_FALSE(data->isEmpty()); 48 EXPECT_FALSE(data->isEmpty());
30 decoder->setData(data.get(), true); 49 decoder->setData(data.get(), true);
31 return decoder; 50 return decoder;
32 } 51 }
33 52
34 void testSize(const char* pngFile, IntSize expectedSize) { 53 void testSize(const char* pngFile, IntSize expectedSize) {
35 auto decoder = createDecoderWithPngData(pngFile); 54 auto decoder = createDecoderWithPngData(pngFile);
36 EXPECT_TRUE(decoder->isSizeAvailable()); 55 EXPECT_TRUE(decoder->isSizeAvailable());
37 EXPECT_EQ(expectedSize, decoder->size()); 56 EXPECT_EQ(expectedSize, decoder->size());
38 } 57 }
39 58
59 // Test whether querying for the size of the image works if we present the
60 // data byte by byte.
61 void testSizeByteByByte(const char* pngFile,
62 size_t bytesNeededToDecodeSize,
63 IntSize expectedSize) {
64 auto decoder = createDecoder();
65 auto data = readFile(pngFile);
66 ASSERT_FALSE(data->isEmpty());
67 ASSERT_LT(bytesNeededToDecodeSize, data->size());
68
69 const char* source = data->data();
70 RefPtr<SharedBuffer> partialData = SharedBuffer::create();
71 for (size_t length = 1; length <= bytesNeededToDecodeSize; length++) {
72 partialData->append(source++, 1u);
73 decoder->setData(partialData.get(), false);
74
75 if (length < bytesNeededToDecodeSize) {
76 EXPECT_FALSE(decoder->isSizeAvailable());
77 EXPECT_TRUE(decoder->size().isEmpty());
78 EXPECT_FALSE(decoder->failed());
79 } else {
80 EXPECT_TRUE(decoder->isSizeAvailable());
81 EXPECT_EQ(expectedSize, decoder->size());
82 }
83 }
84 EXPECT_FALSE(decoder->failed());
85 }
86
87 void writeUint32(uint32_t val, png_byte* data) {
88 data[0] = val >> 24;
89 data[1] = val >> 16;
90 data[2] = val >> 8;
91 data[3] = val;
92 }
93
40 void testRepetitionCount(const char* pngFile, int expectedRepetitionCount) { 94 void testRepetitionCount(const char* pngFile, int expectedRepetitionCount) {
41 auto decoder = createDecoderWithPngData(pngFile); 95 auto decoder = createDecoderWithPngData(pngFile);
42 // Decoding the frame count sets the repetition count as well. 96 // Decoding the frame count sets the number of repetitions as well.
43 decoder->frameCount(); 97 decoder->frameCount();
44 EXPECT_FALSE(decoder->failed()); 98 EXPECT_FALSE(decoder->failed());
45 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount()); 99 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount());
46 } 100 }
47 101
102 struct PublicFrameInfo {
103 size_t duration;
104 IntRect frameRect;
105 ImageFrame::AlphaBlendSource alphaBlend;
106 ImageFrame::DisposalMethod disposalMethod;
107 };
108
109 // This is the frame data for the following PNG image:
110 // /LayoutTests/images/resources/png-animated-idat-part-of-animation.png
111 static PublicFrameInfo pngAnimatedFrameInfo[] = {
112 {500,
113 {IntPoint(0, 0), IntSize(5, 5)},
114 ImageFrame::BlendAtopBgcolor,
115 ImageFrame::DisposeKeep},
116 {900,
117 {IntPoint(1, 1), IntSize(3, 1)},
118 ImageFrame::BlendAtopBgcolor,
119 ImageFrame::DisposeOverwriteBgcolor},
120 {2000,
121 {IntPoint(1, 2), IntSize(3, 2)},
122 ImageFrame::BlendAtopPreviousFrame,
123 ImageFrame::DisposeKeep},
124 {1500,
125 {IntPoint(1, 2), IntSize(3, 1)},
126 ImageFrame::BlendAtopBgcolor,
127 ImageFrame::DisposeKeep},
128 };
129
130 void compareFrameWithExpectation(const PublicFrameInfo& expected,
131 const ImageFrame* frame) {
132 EXPECT_EQ(expected.duration, frame->duration());
133 EXPECT_EQ(expected.frameRect, frame->originalFrameRect());
134 EXPECT_EQ(expected.disposalMethod, frame->getDisposalMethod());
135 EXPECT_EQ(expected.alphaBlend, frame->getAlphaBlendSource());
136 }
137
138 // This function removes |length| bytes at |offset|, and then calls frameCount.
139 // It assumes the missing bytes should result in a failed decode because the
140 // parser jumps |length| bytes too far in the next chunk.
141 void testMissingDataBreaksDecoding(const char* pngFile,
142 size_t offset,
143 size_t length) {
144 auto decoder = createDecoder();
145 auto data = readFile(pngFile);
146 ASSERT_FALSE(data->isEmpty());
147
148 RefPtr<SharedBuffer> invalidData = SharedBuffer::create(data->data(), offset);
149 invalidData->append(data->data() + offset + length,
150 data->size() - offset - length);
151 ASSERT_EQ(data->size() - length, invalidData->size());
152
153 decoder->setData(invalidData, true);
154 decoder->frameCount();
155 EXPECT_TRUE(decoder->failed());
156 }
157
158 // Verify that a decoder with a parse error converts to a static image.
159 static void expectStatic(ImageDecoder* decoder) {
160 EXPECT_EQ(1u, decoder->frameCount());
161 EXPECT_FALSE(decoder->failed());
162
163 ImageFrame* frame = decoder->frameBufferAtIndex(0);
164 ASSERT_NE(nullptr, frame);
165 EXPECT_EQ(ImageFrame::FrameComplete, frame->getStatus());
166 EXPECT_FALSE(decoder->failed());
167 EXPECT_EQ(cAnimationNone, decoder->repetitionCount());
168 }
169
170 // Decode up to the indicated fcTL offset and then provide an fcTL with the
171 // wrong chunk size (20 instead of 26).
172 void testInvalidFctlSize(const char* pngFile,
173 size_t offsetFctl,
174 size_t expectedFrameCount,
175 bool shouldFail) {
176 auto data = readFile(pngFile);
177 ASSERT_FALSE(data->isEmpty());
178
179 auto decoder = createDecoder();
180 RefPtr<SharedBuffer> invalidData =
181 SharedBuffer::create(data->data(), offsetFctl);
182
183 // Test if this gives the correct frame count, before the fcTL is parsed.
184 decoder->setData(invalidData, false);
185 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
186 ASSERT_FALSE(decoder->failed());
187
188 // Append the wrong size to the data stream
189 png_byte sizeChunk[4];
190 writeUint32(20, sizeChunk);
191 invalidData->append(reinterpret_cast<char*>(sizeChunk), 4u);
192
193 // Skip the size in the original data, but provide a truncated fcTL,
194 // which is 4B of tag, 20B of data and 4B of CRC, totalling 28B.
195 invalidData->append(data->data() + offsetFctl + 4, 28u);
196 // Append the rest of the data
197 const size_t offsetPostFctl = offsetFctl + 38;
198 invalidData->append(data->data() + offsetPostFctl,
199 data->size() - offsetPostFctl);
200
201 decoder->setData(invalidData, false);
202 if (shouldFail) {
203 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
204 EXPECT_EQ(true, decoder->failed());
205 } else {
206 expectStatic(decoder.get());
207 }
208 }
209
48 // Verify that the decoder can successfully decode the first frame when 210 // Verify that the decoder can successfully decode the first frame when
49 // initially only half of the frame data is received, resulting in a partially 211 // initially only half of the frame data is received, resulting in a partially
50 // decoded image, and then the rest of the image data is received. Verify that 212 // decoded image, and then the rest of the image data is received. Verify that
51 // the bitmap hashes of the two stages are different. Also verify that the final 213 // the bitmap hashes of the two stages are different. Also verify that the final
52 // bitmap hash is equivalent to the hash when all data is provided at once. 214 // bitmap hash is equivalent to the hash when all data is provided at once.
53 // 215 //
54 // This verifies that decoder correctly keeps track of where it stopped 216 // This verifies that the decoder correctly keeps track of where it stopped
55 // decoding when the image was not yet fully received. 217 // decoding when the image was not yet fully received.
56 void testProgressiveDecodingContinuesAfterFullData(const char* pngFile, 218 void testProgressiveDecodingContinuesAfterFullData(const char* pngFile,
57 size_t offsetMidFirstFrame) { 219 size_t offsetMidFirstFrame) {
58 auto fullData = readFile(pngFile); 220 auto fullData = readFile(pngFile);
59 ASSERT_FALSE(fullData->isEmpty()); 221 ASSERT_FALSE(fullData->isEmpty());
60 222
61 auto decoderUpfront = createDecoder(); 223 auto decoderUpfront = createDecoder();
62 decoderUpfront->setData(fullData.get(), true); 224 decoderUpfront->setData(fullData.get(), true);
63 EXPECT_GE(1u, decoderUpfront->frameCount()); 225 EXPECT_GE(decoderUpfront->frameCount(), 1u);
64 const ImageFrame* const frameUpfront = decoderUpfront->frameBufferAtIndex(0); 226 const ImageFrame* const frameUpfront = decoderUpfront->frameBufferAtIndex(0);
65 ASSERT_EQ(ImageFrame::FrameComplete, frameUpfront->getStatus()); 227 ASSERT_EQ(ImageFrame::FrameComplete, frameUpfront->getStatus());
66 const unsigned hashUpfront = hashBitmap(frameUpfront->bitmap()); 228 const unsigned hashUpfront = hashBitmap(frameUpfront->bitmap());
67 229
68 auto decoder = createDecoder(); 230 auto decoder = createDecoder();
69 RefPtr<SharedBuffer> partialData = 231 RefPtr<SharedBuffer> partialData =
70 SharedBuffer::create(fullData->data(), offsetMidFirstFrame); 232 SharedBuffer::create(fullData->data(), offsetMidFirstFrame);
71 decoder->setData(partialData, false); 233 decoder->setData(partialData, false);
72 234
73 EXPECT_EQ(1u, decoder->frameCount()); 235 EXPECT_EQ(1u, decoder->frameCount());
74 const ImageFrame* frame = decoder->frameBufferAtIndex(0); 236 const ImageFrame* frame = decoder->frameBufferAtIndex(0);
75 EXPECT_EQ(frame->getStatus(), ImageFrame::FramePartial); 237 EXPECT_EQ(frame->getStatus(), ImageFrame::FramePartial);
76 const unsigned hashPartial = hashBitmap(frame->bitmap()); 238 const unsigned hashPartial = hashBitmap(frame->bitmap());
77 239
78 decoder->setData(fullData.get(), true); 240 decoder->setData(fullData.get(), true);
79 frame = decoder->frameBufferAtIndex(0); 241 frame = decoder->frameBufferAtIndex(0);
80 EXPECT_EQ(frame->getStatus(), ImageFrame::FrameComplete); 242 EXPECT_EQ(frame->getStatus(), ImageFrame::FrameComplete);
81 const unsigned hashFull = hashBitmap(frame->bitmap()); 243 const unsigned hashFull = hashBitmap(frame->bitmap());
82 244
83 EXPECT_FALSE(decoder->failed()); 245 EXPECT_FALSE(decoder->failed());
84 EXPECT_NE(hashFull, hashPartial); 246 EXPECT_NE(hashFull, hashPartial);
85 EXPECT_EQ(hashFull, hashUpfront); 247 EXPECT_EQ(hashFull, hashUpfront);
86 } 248 }
87 249
88 // Modify the frame data bytes for frame |frameIndex| so that decoding fails. 250 // Modify the frame data bytes for frame |frameIndex| so that decoding fails.
89 // Parsing should work fine, and is checked with |expectedFrameCountBefore|. If 251 // Parsing should work fine, and is checked with |expectedFrameCount|.
90 // the failure should invalidate the decoder, |expectFailure| should be set to
91 // true. If not, |expectedFrameCountAfter| should indicate the new frame count
92 // after the failure.
93 void testFailureDuringDecode(const char* file, 252 void testFailureDuringDecode(const char* file,
94 size_t idatOffset, 253 size_t idatOffset,
95 size_t frameIndex, 254 size_t frameIndex,
96 bool expectFailure, 255 size_t expectedFrameCount) {
97 size_t expectedFrameCountBefore,
98 size_t expectedFrameCountAfter = 0u) {
99 RefPtr<SharedBuffer> fullData = readFile(file); 256 RefPtr<SharedBuffer> fullData = readFile(file);
100 ASSERT_FALSE(fullData->isEmpty()); 257 ASSERT_FALSE(fullData->isEmpty());
101 258
102 // This is the offset where the frame data chunk frame |frameIndex| starts. 259 // This is the offset where the frame data chunk frame |frameIndex| starts.
103 RefPtr<SharedBuffer> data = 260 RefPtr<SharedBuffer> data =
104 SharedBuffer::create(fullData->data(), idatOffset + 8u); 261 SharedBuffer::create(fullData->data(), idatOffset + 8u);
105 // Repeat the first 8 bytes of the frame data. This should result in a 262 // Repeat the first 8 bytes of the frame data. This should result in a
106 // successful parse, since frame data is not analyzed in that step, but 263 // successful parse, since frame data is not analyzed in that step, but
107 // should give an error during decoding. 264 // should give an error during decoding.
108 data->append(fullData->data() + idatOffset, 8u); 265 data->append(fullData->data() + idatOffset, 8u);
109 data->append(fullData->data() + idatOffset + 16u, 266 data->append(fullData->data() + idatOffset + 16u,
110 fullData->size() - idatOffset - 16u); 267 fullData->size() - idatOffset - 16u);
111 268
112 auto decoder = createDecoder(); 269 auto decoder = createDecoder();
113 decoder->setData(data.get(), true); 270 decoder->setData(data.get(), true);
114 271
115 EXPECT_EQ(expectedFrameCountBefore, decoder->frameCount()); 272 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
116 273
117 const ImageFrame* const frame = decoder->frameBufferAtIndex(frameIndex); 274 decoder->frameBufferAtIndex(frameIndex);
118 EXPECT_EQ(expectFailure, decoder->failed()); 275 ASSERT_EQ(true, decoder->failed());
119 if (!expectFailure) { 276
120 EXPECT_EQ(expectedFrameCountAfter, decoder->frameCount()); 277 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
121 EXPECT_EQ(ImageFrame::FrameEmpty, frame->getStatus());
122 }
123 } 278 }
124 279
125 } // Anonymous namespace 280 } // Anonymous namespace
126 281
282 // Animated PNG Tests
283
284 TEST(AnimatedPNGTests, sizeTest) {
285 testSize(
286 "/LayoutTests/images/resources/"
287 "png-animated-idat-part-of-animation.png",
288 IntSize(5, 5));
289 testSize(
290 "/LayoutTests/images/resources/"
291 "png-animated-idat-not-part-of-animation.png",
292 IntSize(227, 35));
293 }
294
295 TEST(AnimatedPNGTests, repetitionCountTest) {
296 testRepetitionCount(
297 "/LayoutTests/images/resources/"
298 "png-animated-idat-part-of-animation.png",
299 6u);
300 // This is an "animated" image with only one frame, that is, the IDAT is
301 // ignored and there is one fdAT frame. so it should be considered
302 // non-animated.
303 testRepetitionCount(
304 "/LayoutTests/images/resources/"
305 "png-animated-idat-not-part-of-animation.png",
306 cAnimationNone);
307 }
308
309 // Test if the decoded metdata corresponds to the defined expectations
310 TEST(AnimatedPNGTests, MetaDataTest) {
311 const char* pngFile =
312 "/LayoutTests/images/resources/"
313 "png-animated-idat-part-of-animation.png";
314 constexpr size_t expectedFrameCount = 4;
315
316 auto decoder = createDecoderWithPngData(pngFile);
317 ASSERT_EQ(expectedFrameCount, decoder->frameCount());
318 for (size_t i = 0; i < expectedFrameCount; i++) {
319 compareFrameWithExpectation(pngAnimatedFrameInfo[i],
320 decoder->frameBufferAtIndex(i));
321 }
322 }
323
324 TEST(AnimatedPNGTests, EmptyFrame) {
325 const char* pngFile = "/LayoutTests/images/resources/empty-frame.png";
326 auto decoder = createDecoderWithPngData(pngFile);
327 // Frame 0 is empty. Ensure that decoding frame 1 (which depends on frame 0)
328 // fails (rather than crashing).
329 EXPECT_EQ(2u, decoder->frameCount());
330 EXPECT_FALSE(decoder->failed());
331
332 ImageFrame* frame = decoder->frameBufferAtIndex(1);
333 EXPECT_TRUE(decoder->failed());
334 ASSERT_NE(nullptr, frame);
335 EXPECT_EQ(ImageFrame::FrameEmpty, frame->getStatus());
336 }
337
338 TEST(AnimatedPNGTests, ByteByByteSizeAvailable) {
339 testSizeByteByByte(
340 "/LayoutTests/images/resources/"
341 "png-animated-idat-part-of-animation.png",
342 141u, IntSize(5, 5));
343 testSizeByteByByte(
344 "/LayoutTests/images/resources/"
345 "png-animated-idat-not-part-of-animation.png",
346 79u, IntSize(227, 35));
347 }
348
349 TEST(AnimatedPNGTests, ByteByByteMetaData) {
350 const char* pngFile =
351 "/LayoutTests/images/resources/"
352 "png-animated-idat-part-of-animation.png";
353 constexpr size_t expectedFrameCount = 4;
354
355 // These are the byte offsets where each frame should have been parsed.
356 // It boils down to the offset of the first fcTL / IEND after the last
357 // frame data chunk, plus 8 bytes for recognition. The exception on this is
358 // the first frame, which is reported when its first framedata is seen.
359 size_t frameOffsets[expectedFrameCount] = {141, 249, 322, 430};
360
361 auto decoder = createDecoder();
362 auto data = readFile(pngFile);
363 ASSERT_FALSE(data->isEmpty());
364 size_t framesParsed = 0;
365
366 const char* source = data->data();
367 RefPtr<SharedBuffer> partialData = SharedBuffer::create();
368 for (size_t length = 1; length <= frameOffsets[expectedFrameCount - 1];
369 length++) {
370 partialData->append(source++, 1u);
371 decoder->setData(partialData.get(), false);
372 EXPECT_FALSE(decoder->failed());
373 if (length < frameOffsets[framesParsed]) {
374 EXPECT_EQ(framesParsed, decoder->frameCount());
375 } else {
376 ASSERT_EQ(framesParsed + 1, decoder->frameCount());
377 compareFrameWithExpectation(pngAnimatedFrameInfo[framesParsed],
378 decoder->frameBufferAtIndex(framesParsed));
379 framesParsed++;
380 }
381 }
382 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
383 EXPECT_FALSE(decoder->failed());
384 }
385
386 TEST(AnimatedPNGTests, TestRandomFrameDecode) {
387 testRandomFrameDecode(&createDecoder,
388 "/LayoutTests/images/resources/"
389 "png-animated-idat-part-of-animation.png",
390 2u);
391 }
392
393 TEST(AnimatedPNGTests, TestDecodeAfterReallocation) {
394 testDecodeAfterReallocatingData(&createDecoder,
395 "/LayoutTests/images/resources/"
396 "png-animated-idat-part-of-animation.png");
397 }
398
399 TEST(AnimatedPNGTests, ProgressiveDecode) {
400 testProgressiveDecoding(&createDecoder,
401 "/LayoutTests/images/resources/"
402 "png-animated-idat-part-of-animation.png",
403 13u);
404 }
405
406 TEST(AnimatedPNGTests, ParseAndDecodeByteByByte) {
407 testByteByByteDecode(&createDecoder,
408 "/LayoutTests/images/resources/"
409 "png-animated-idat-part-of-animation.png",
410 4u, 6u);
411 }
412
413 TEST(AnimatedPNGTests, FailureDuringParsing) {
414 // Test the first fcTL in the stream. Because no frame data has been set at
415 // this point, the expected frame count is zero. 95 bytes is just before the
416 // first fcTL chunk, at which the first frame is detected. This is before the
417 // IDAT, so it should be treated as a static image.
418 testInvalidFctlSize(
419 "/LayoutTests/images/resources/"
420 "png-animated-idat-part-of-animation.png",
421 95u, 0u, false);
422
423 // Test for the third fcTL in the stream. This should see 1 frame before the
424 // fcTL, and then fail when parsing it.
425 testInvalidFctlSize(
426 "/LayoutTests/images/resources/"
427 "png-animated-idat-part-of-animation.png",
428 241u, 1u, true);
429 }
430
431 TEST(AnimatedPNGTests, ActlErrors) {
432 const char* pngFile =
433 "/LayoutTests/images/resources/"
434 "png-animated-idat-part-of-animation.png";
435 auto data = readFile(pngFile);
436 ASSERT_FALSE(data->isEmpty());
437
438 const size_t offsetActl = 33u;
439 const size_t acTLSize = 20u;
440 {
441 // Remove the acTL chunk from the stream. This results in a static image.
442 RefPtr<SharedBuffer> noActlData =
443 SharedBuffer::create(data->data(), offsetActl);
444 noActlData->append(data->data() + offsetActl + acTLSize,
445 data->size() - offsetActl - acTLSize);
446
447 auto decoder = createDecoder();
448 decoder->setData(noActlData, true);
449 EXPECT_EQ(1u, decoder->frameCount());
450 EXPECT_FALSE(decoder->failed());
451 EXPECT_EQ(cAnimationNone, decoder->repetitionCount());
452 }
453
454 // Store the acTL for more tests.
455 char acTL[acTLSize];
456 memcpy(acTL, data->data() + offsetActl, acTLSize);
457
458 // Insert an extra acTL at a couple of different offsets.
459 // Prior to the IDAT, this should result in a static image. After, this
460 // should fail.
461 const struct {
462 size_t offset;
463 bool shouldFail;
464 } gRecs[] = {{8u, false},
465 {offsetActl, false},
466 {133u, false},
467 {172u, true},
468 {422u, true}};
469 for (auto rec : gRecs) {
470 const size_t offset = rec.offset;
471 RefPtr<SharedBuffer> extraActlData =
472 SharedBuffer::create(data->data(), offset);
473 extraActlData->append(acTL, acTLSize);
474 extraActlData->append(data->data() + offset, data->size() - offset);
475 auto decoder = createDecoder();
476 decoder->setData(extraActlData, true);
477 EXPECT_EQ(rec.shouldFail ? 0u : 1u, decoder->frameCount());
478 EXPECT_EQ(rec.shouldFail, decoder->failed());
479 }
480
481 // An acTL after IDAT is ignored.
482 pngFile =
483 "/LayoutTests/images/resources/"
484 "cHRM_color_spin.png";
485 {
486 auto data2 = readFile(pngFile);
487 ASSERT_FALSE(data2->isEmpty());
488 const size_t postIDATOffset = 30971u;
489 for (size_t times = 0; times < 2; times++) {
490 RefPtr<SharedBuffer> extraActlData =
491 SharedBuffer::create(data2->data(), postIDATOffset);
492 for (size_t i = 0; i < times; i++)
493 extraActlData->append(acTL, acTLSize);
494 extraActlData->append(data2->data() + postIDATOffset,
495 data2->size() - postIDATOffset);
496
497 auto decoder = createDecoder();
498 decoder->setData(extraActlData, true);
499 EXPECT_EQ(1u, decoder->frameCount());
500 EXPECT_FALSE(decoder->failed());
501 EXPECT_EQ(cAnimationNone, decoder->repetitionCount());
502 EXPECT_NE(nullptr, decoder->frameBufferAtIndex(0));
503 EXPECT_FALSE(decoder->failed());
504 }
505 }
506 }
507
508 TEST(AnimatedPNGTests, fdatBeforeIdat) {
509 const char* pngFile =
510 "/LayoutTests/images/resources/"
511 "png-animated-idat-not-part-of-animation.png";
512 auto data = readFile(pngFile);
513 ASSERT_FALSE(data->isEmpty());
514
515 // Insert fcTL and fdAT prior to the IDAT
516 const size_t idatOffset = 71u;
517 RefPtr<SharedBuffer> modifiedData =
518 SharedBuffer::create(data->data(), idatOffset);
519 // Copy fcTL and fdAT
520 const size_t fctlPlusFdatSize = 38u + 1566u;
521 modifiedData->append(data->data() + 2519u, fctlPlusFdatSize);
522 // Copy IDAT
523 modifiedData->append(data->data() + idatOffset, 2448u);
524 // Copy the remaining
525 modifiedData->append(data->data() + 4123u, 39u + 12u);
526 // Data has just been rearranged.
527 ASSERT_EQ(data->size(), modifiedData->size());
528
529 {
530 // This broken APNG will be treated as a static png.
531 auto decoder = createDecoder();
532 decoder->setData(modifiedData.get(), true);
533 expectStatic(decoder.get());
534 }
535
536 {
537 // Remove the acTL from the modified image. It now has fdAT before
538 // IDAT, but no acTL, so fdAT should be ignored.
539 const size_t offsetActl = 33u;
540 const size_t acTLSize = 20u;
541 RefPtr<SharedBuffer> modifiedData2 =
542 SharedBuffer::create(modifiedData->data(), offsetActl);
543 modifiedData2->append(modifiedData->data() + offsetActl + acTLSize,
544 modifiedData->size() - offsetActl);
545 auto decoder = createDecoder();
546 decoder->setData(modifiedData2.get(), true);
547 expectStatic(decoder.get());
548
549 // Likewise, if an acTL follows the fdAT, it is ignored.
550 const size_t insertionOffset = idatOffset + fctlPlusFdatSize - acTLSize;
551 RefPtr<SharedBuffer> modifiedData3 =
552 SharedBuffer::create(modifiedData2->data(), insertionOffset);
553 modifiedData3->append(data->data() + offsetActl, acTLSize);
554 modifiedData3->append(modifiedData2->data() + insertionOffset,
555 modifiedData2->size() - insertionOffset);
556 decoder = createDecoder();
557 decoder->setData(modifiedData3.get(), true);
558 expectStatic(decoder.get());
559 }
560 }
561
562 TEST(AnimatedPNGTests, IdatSizeMismatch) {
563 // The default image must fill the image
564 const char* pngFile =
565 "/LayoutTests/images/resources/"
566 "png-animated-idat-part-of-animation.png";
567 auto data = readFile(pngFile);
568 ASSERT_FALSE(data->isEmpty());
569
570 const size_t fctlOffset = 95u;
571 RefPtr<SharedBuffer> modifiedData =
572 SharedBuffer::create(data->data(), fctlOffset);
573 const size_t fctlSize = 38u;
574 png_byte fctl[fctlSize];
575 memcpy(fctl, data->data() + fctlOffset, fctlSize);
576 // Set the height to a smaller value, so it does not fill the image.
577 writeUint32(3, fctl + 16);
578 // Correct the crc
579 writeUint32(3210324191, fctl + 34);
580 modifiedData->append((const char*)fctl, fctlSize);
581 const size_t afterFctl = fctlOffset + fctlSize;
582 modifiedData->append(data->data() + afterFctl, data->size() - afterFctl);
583
584 auto decoder = createDecoder();
585 decoder->setData(modifiedData.get(), true);
586 expectStatic(decoder.get());
587 }
588
589 // Originally, the third frame has an offset of (1,2) and a size of (3,2). By
590 // changing the offset to (4,4), the frame rect is no longer within the image
591 // size of 5x5. This results in a failure.
592 TEST(AnimatedPNGTests, VerifyFrameOutsideImageSizeFails) {
593 const char* pngFile =
594 "/LayoutTests/images/resources/"
595 "png-animated-idat-part-of-animation.png";
596 auto data = readFile(pngFile);
597 auto decoder = createDecoder();
598 ASSERT_FALSE(data->isEmpty());
599
600 const size_t offsetThirdFctl = 241;
601 RefPtr<SharedBuffer> modifiedData =
602 SharedBuffer::create(data->data(), offsetThirdFctl);
603 const size_t fctlSize = 38u;
604 png_byte fctl[fctlSize];
605 memcpy(fctl, data->data() + offsetThirdFctl, fctlSize);
606 // Modify offset and crc.
607 writeUint32(4, fctl + 20u);
608 writeUint32(4, fctl + 24u);
609 writeUint32(3700322018, fctl + 34u);
610
611 modifiedData->append(const_cast<const char*>(reinterpret_cast<char*>(fctl)),
612 fctlSize);
613 modifiedData->append(data->data() + offsetThirdFctl + fctlSize,
614 data->size() - offsetThirdFctl - fctlSize);
615
616 decoder->setData(modifiedData, true);
617
618 IntSize expectedSize(5, 5);
619 EXPECT_TRUE(decoder->isSizeAvailable());
620 EXPECT_EQ(expectedSize, decoder->size());
621
622 const size_t expectedFrameCount = 0;
623 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
624 EXPECT_TRUE(decoder->failed());
625 }
626
627 TEST(AnimatedPNGTests, ProgressiveDecodingContinuesAfterFullData) {
628 // 160u is a randomly chosen offset in the IDAT chunk of the first frame.
629 testProgressiveDecodingContinuesAfterFullData(
630 "/LayoutTests/images/resources/"
631 "png-animated-idat-part-of-animation.png",
632 160u);
633 }
634
635 TEST(AnimatedPNGTests, RandomDecodeAfterClearFrameBufferCache) {
636 testRandomDecodeAfterClearFrameBufferCache(
637 &createDecoder,
638 "/LayoutTests/images/resources/"
639 "png-animated-idat-part-of-animation.png",
640 2u);
641 }
642
643 TEST(AnimatedPNGTests, VerifyAlphaBlending) {
644 testAlphaBlending(&createDecoder,
645 "/LayoutTests/images/resources/"
646 "png-animated-idat-part-of-animation.png");
647 }
648
649 // This tests if the frame count gets set correctly when parsing frameCount
650 // fails in one of the parsing queries.
651 //
652 // First, enough data is provided such that two frames should be registered.
653 // The decoder should at this point not be in the failed status.
654 //
655 // Then, we provide the rest of the data except for the last IEND chunk, but
656 // tell the decoder that this is all the data we have. The frame count should
657 // be three, since one extra frame should be discovered. The fourth frame
658 // should *not* be registered since the reader should not be able to determine
659 // where the frame ends. The decoder should *not* be in the failed state since
660 // there are three frames which can be shown.
661 // Attempting to decode the third frame should fail, since the file is
662 // truncated.
663 TEST(AnimatedPNGTests, FailureMissingIendChunk) {
664 RefPtr<SharedBuffer> fullData = readFile(
665 "/LayoutTests/images/resources/"
666 "png-animated-idat-part-of-animation.png");
667 ASSERT_FALSE(fullData->isEmpty());
668 auto decoder = createDecoder();
669
670 const size_t offsetTwoFrames = 249;
671 const size_t expectedFramesAfter249Bytes = 2;
672 RefPtr<SharedBuffer> tempData =
673 SharedBuffer::create(fullData->data(), offsetTwoFrames);
674 decoder->setData(tempData.get(), false);
675 EXPECT_EQ(expectedFramesAfter249Bytes, decoder->frameCount());
676 EXPECT_FALSE(decoder->failed());
677
678 // Provide the rest of the data except for the last IEND chunk.
679 const size_t expectedFramesAfterAllExcept12Bytes = 3;
680 tempData = SharedBuffer::create(fullData->data(), fullData->size() - 12);
681 decoder->setData(tempData.get(), true);
682 ASSERT_EQ(expectedFramesAfterAllExcept12Bytes, decoder->frameCount());
683
684 for (size_t i = 0; i < expectedFramesAfterAllExcept12Bytes; i++) {
685 EXPECT_FALSE(decoder->failed());
686 decoder->frameBufferAtIndex(i);
687 }
688
689 EXPECT_TRUE(decoder->failed());
690 }
691
692 TEST(AnimatedPNGTests, FailureDuringDecodingInvalidatesDecoder) {
693 testFailureDuringDecode(
694 "/LayoutTests/images/resources/"
695 "png-animated-idat-part-of-animation.png",
696 291u, // fdat offset for frame index 2, plus 12 to move past sequence
697 // number.
698 2u, // try to decode frame index 2
699 4u); // expected frame count before failure
700
701 testFailureDuringDecode(
702 "/LayoutTests/images/resources/"
703 "png-animated-idat-part-of-animation.png",
704 133u, // idat offset for frame index 0
705 0u, // try to decode frame index 0
706 4u); // expected frame count before failure
707 }
708
709 // Verify that a malformatted PNG, where the IEND appears before any frame data
710 // (IDAT), invalidates the decoder.
711 TEST(AnimatedPNGTests, VerifyIENDBeforeIDATInvalidatesDecoder) {
712 RefPtr<SharedBuffer> fullData = readFile(
713 "/LayoutTests/images/resources/"
714 "png-animated-idat-part-of-animation.png");
715 ASSERT_FALSE(fullData->isEmpty());
716 auto decoder = createDecoder();
717
718 const size_t offsetIDAT = 133;
719 RefPtr<SharedBuffer> data =
720 SharedBuffer::create(fullData->data(), offsetIDAT);
721 data->append(fullData->data() + fullData->size() - 12u, 12u);
722 data->append(fullData->data() + offsetIDAT, fullData->size() - offsetIDAT);
723 decoder->setData(data.get(), true);
724
725 const size_t expectedFrameCount = 0u;
726 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
727 EXPECT_TRUE(decoder->failed());
728 }
729
730 // For animated images, frameIsCompleteAtIndex(i) should return true if the
731 // i-th frame is fully received. The frames don't need to be successfully
732 // decoded.
733 TEST(AnimatedPNGTests, VerifyFrameCompleteBehavior) {
734 const char* pngFile =
735 "/LayoutTests/images/resources/"
736 "png-animated-idat-part-of-animation.png";
737 RefPtr<SharedBuffer> fullData = readFile(pngFile);
738 ASSERT_FALSE(fullData->isEmpty());
739 auto decoder = createDecoder();
740
741 // When not all data for the first frame has been received,
742 // frameIsCompleteAtIndex(0) should return false.
743 const size_t offsetMidwayFirstFrame = 160;
744 RefPtr<SharedBuffer> data =
745 SharedBuffer::create(fullData->data(), offsetMidwayFirstFrame);
746 decoder->setData(data.get(), false);
747 EXPECT_EQ(1u, decoder->frameCount());
748 ASSERT_FALSE(decoder->failed());
749 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0));
750
751 // When all image data is received, every frame should be complete.
752 const size_t expectedFrameCount = 4;
753 decoder->setData(fullData.get(), true);
754 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
755 for (size_t index = 0; index < expectedFrameCount; index++)
756 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(index));
757 }
758
759 // All IDAT chunks must be before all fdAT chunks
760 TEST(AnimatedPNGTests, MixedDataChunks) {
761 const char* pngFile =
762 "/LayoutTests/images/resources/"
763 "png-animated-idat-part-of-animation.png";
764 RefPtr<SharedBuffer> fullData = readFile(pngFile);
765 ASSERT_FALSE(fullData->isEmpty());
766
767 // Add an extra fdAT after the first IDAT, skipping fcTL.
768 const size_t postIDAT = 172u;
769 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), postIDAT);
770 const size_t fcTLSize = 38u;
771 const size_t fdATSize = 31u;
772 png_byte fdAT[fdATSize];
773 memcpy(fdAT, fullData->data() + postIDAT + fcTLSize, fdATSize);
774 // Modify the sequence number
775 writeUint32(1u, fdAT + 8);
776 data->append((const char*)fdAT, fdATSize);
777 const size_t IENDOffset = 422u;
778 data->append(fullData->data() + IENDOffset, fullData->size() - IENDOffset);
779 auto decoder = createDecoder();
780 decoder->setData(data.get(), true);
781 decoder->frameCount();
782 EXPECT_TRUE(decoder->failed());
783
784 // Insert an IDAT after an fdAT.
785 const size_t postfdAT = postIDAT + fcTLSize + fdATSize;
786 data = SharedBuffer::create(fullData->data(), postfdAT);
787 const size_t IDATOffset = 133u;
788 data->append(fullData->data() + IDATOffset, postIDAT - IDATOffset);
789 // Append the rest.
790 data->append(fullData->data() + postIDAT, fullData->size() - postIDAT);
791 decoder = createDecoder();
792 decoder->setData(data.get(), true);
793 decoder->frameCount();
794 EXPECT_TRUE(decoder->failed());
795 }
796
797 // Verify that erroneous values for the disposal method and alpha blending
798 // cause the decoder to fail.
799 TEST(AnimatedPNGTests, VerifyInvalidDisposalAndBlending) {
800 const char* pngFile =
801 "/LayoutTests/images/resources/"
802 "png-animated-idat-part-of-animation.png";
803 RefPtr<SharedBuffer> fullData = readFile(pngFile);
804 ASSERT_FALSE(fullData->isEmpty());
805 auto decoder = createDecoder();
806
807 // The disposal byte in the frame control chunk is the 24th byte, alpha
808 // blending the 25th. |offsetDisposalOp| is 241 bytes to get to the third
809 // fctl chunk, 8 bytes to skip the length and tag bytes, and 24 bytes to get
810 // to the disposal op.
811 //
812 // Write invalid values to the disposal and alpha blending byte, correct the
813 // crc and append the rest of the buffer.
814 const size_t offsetDisposalOp = 241 + 8 + 24;
815 RefPtr<SharedBuffer> data =
816 SharedBuffer::create(fullData->data(), offsetDisposalOp);
817 png_byte disposalAndBlending[6u];
818 disposalAndBlending[0] = 7;
819 disposalAndBlending[1] = 9;
820 writeUint32(2408835439u, disposalAndBlending + 2u);
821 data->append(reinterpret_cast<char*>(disposalAndBlending), 6u);
822 data->append(fullData->data() + offsetDisposalOp + 6u,
823 fullData->size() - offsetDisposalOp - 6u);
824
825 decoder->setData(data.get(), true);
826 decoder->frameCount();
827 ASSERT_TRUE(decoder->failed());
828 }
829
830 // This test verifies that the following situation does not invalidate the
831 // decoder:
832 // - Frame 0 is decoded progressively, but there's not enough data to fully
833 // decode it.
834 // - The rest of the image data is received.
835 // - Frame X, with X > 0, and X does not depend on frame 0, is decoded.
836 // - Frame 0 is decoded.
837 // This is a tricky case since the decoder resets the png struct for each frame,
838 // and this test verifies that it does not break the decoding of frame 0, even
839 // though it already started in the first call.
840 TEST(AnimatedPNGTests, VerifySuccessfulFirstFrameDecodeAfterLaterFrame) {
841 const char* pngFile =
842 "/LayoutTests/images/resources/"
843 "png-animated-three-independent-frames.png";
844 auto decoder = createDecoder();
845 auto fullData = readFile(pngFile);
846 ASSERT_FALSE(fullData->isEmpty());
847
848 // 160u is a randomly chosen offset in the IDAT chunk of the first frame.
849 const size_t middleFirstFrame = 160u;
850 RefPtr<SharedBuffer> data =
851 SharedBuffer::create(fullData->data(), middleFirstFrame);
852 decoder->setData(data.get(), false);
853
854 ASSERT_EQ(1u, decoder->frameCount());
855 ASSERT_EQ(ImageFrame::FramePartial,
856 decoder->frameBufferAtIndex(0)->getStatus());
857
858 decoder->setData(fullData.get(), true);
859 ASSERT_EQ(3u, decoder->frameCount());
860 ASSERT_EQ(ImageFrame::FrameComplete,
861 decoder->frameBufferAtIndex(1)->getStatus());
862 // The point is that this call does not decode frame 0, which it won't do if
863 // it does not have it as its required previous frame.
864 ASSERT_EQ(kNotFound,
865 decoder->frameBufferAtIndex(1)->requiredPreviousFrameIndex());
866
867 EXPECT_EQ(ImageFrame::FrameComplete,
868 decoder->frameBufferAtIndex(0)->getStatus());
869 EXPECT_FALSE(decoder->failed());
870 }
871
127 // Static PNG tests 872 // Static PNG tests
128 873
129 TEST(StaticPNGTests, repetitionCountTest) { 874 TEST(StaticPNGTests, repetitionCountTest) {
130 testRepetitionCount("/LayoutTests/images/resources/png-simple.png", 875 testRepetitionCount("/LayoutTests/images/resources/png-simple.png",
131 cAnimationNone); 876 cAnimationNone);
132 } 877 }
133 878
134 TEST(StaticPNGTests, sizeTest) { 879 TEST(StaticPNGTests, sizeTest) {
135 testSize("/LayoutTests/images/resources/png-simple.png", IntSize(111, 29)); 880 testSize("/LayoutTests/images/resources/png-simple.png", IntSize(111, 29));
136 } 881 }
137 882
138 TEST(StaticPNGTests, MetaDataTest) { 883 TEST(StaticPNGTests, MetaDataTest) {
139 const size_t expectedFrameCount = 1; 884 const size_t expectedFrameCount = 1;
140 const size_t expectedDuration = 0; 885 const size_t expectedDuration = 0;
141 auto decoder = 886 auto decoder =
142 createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png"); 887 createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png");
143 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); 888 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
144 EXPECT_EQ(expectedDuration, decoder->frameDurationAtIndex(0)); 889 EXPECT_EQ(expectedDuration, decoder->frameDurationAtIndex(0));
145 } 890 }
146 891
892 TEST(StaticPNGTests, InvalidIHDRChunk) {
893 testMissingDataBreaksDecoding("/LayoutTests/images/resources/png-simple.png",
894 20u, 2u);
895 }
896
147 TEST(StaticPNGTests, ProgressiveDecoding) { 897 TEST(StaticPNGTests, ProgressiveDecoding) {
148 testProgressiveDecoding(&createDecoder, 898 testProgressiveDecoding(&createDecoder,
149 "/LayoutTests/images/resources/png-simple.png", 11u); 899 "/LayoutTests/images/resources/png-simple.png", 11u);
150 } 900 }
151 901
152 TEST(StaticPNGTests, ProgressiveDecodingContinuesAfterFullData) { 902 TEST(StaticPNGTests, ProgressiveDecodingContinuesAfterFullData) {
153 testProgressiveDecodingContinuesAfterFullData( 903 testProgressiveDecodingContinuesAfterFullData(
154 "/LayoutTests/images/resources/png-simple.png", 1000u); 904 "/LayoutTests/images/resources/png-simple.png", 1000u);
155 } 905 }
156 906
157 TEST(StaticPNGTests, FailureDuringDecodingInvalidatesDecoder) { 907 TEST(StaticPNGTests, FailureDuringDecodingInvalidatesDecoder) {
158 testFailureDuringDecode( 908 testFailureDuringDecode(
159 "/LayoutTests/images/resources/png-simple.png", 909 "/LayoutTests/images/resources/png-simple.png",
160 85u, // idat offset for frame index 0 910 85u, // idat offset for frame index 0
161 0u, // try to decode frame index 0 911 0u, // try to decode frame index 0
162 true, // expect the decoder to be invalidated after the failure
163 1u); // expected frame count before failure 912 1u); // expected frame count before failure
164 } 913 }
165 914
166 // For static images, frameIsCompleteAtIndex(0) should return true if and only 915 // For static images, frameIsCompleteAtIndex(0) should return true if and only
167 // if the frame is successfully decoded, not when it is fully received. 916 // if the frame is successfully decoded, not when it is fully received.
168 TEST(StaticPNGTests, VerifyFrameCompleteBehavior) { 917 TEST(StaticPNGTests, VerifyFrameCompleteBehavior) {
169 auto decoder = 918 auto decoder =
170 createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png"); 919 createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png");
171 EXPECT_EQ(1u, decoder->frameCount()); 920 EXPECT_EQ(1u, decoder->frameCount());
172 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0)); 921 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0));
173 EXPECT_EQ(ImageFrame::FrameComplete, 922 EXPECT_EQ(ImageFrame::FrameComplete,
174 decoder->frameBufferAtIndex(0)->getStatus()); 923 decoder->frameBufferAtIndex(0)->getStatus());
175 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); 924 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0));
176 } 925 }
177 926
178 }; // namespace blink 927 }; // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698