OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 ImageDecoder::AlphaOption alphaOption) { | 49 ImageDecoder::AlphaOption alphaOption) { |
50 return wrapUnique( | 50 return wrapUnique( |
51 new WEBPImageDecoder(alphaOption, ImageDecoder::ColorSpaceApplied, | 51 new WEBPImageDecoder(alphaOption, ImageDecoder::ColorSpaceApplied, |
52 ImageDecoder::noDecodedImageByteLimit)); | 52 ImageDecoder::noDecodedImageByteLimit)); |
53 } | 53 } |
54 | 54 |
55 std::unique_ptr<ImageDecoder> createDecoder() { | 55 std::unique_ptr<ImageDecoder> createDecoder() { |
56 return createDecoder(ImageDecoder::AlphaNotPremultiplied); | 56 return createDecoder(ImageDecoder::AlphaNotPremultiplied); |
57 } | 57 } |
58 | 58 |
59 void testRandomFrameDecode(const char* webpFile) { | |
60 SCOPED_TRACE(webpFile); | |
61 | |
62 RefPtr<SharedBuffer> fullData = readFile(webpFile); | |
63 ASSERT_TRUE(fullData.get()); | |
64 Vector<unsigned> baselineHashes; | |
65 createDecodingBaseline(&createDecoder, fullData.get(), &baselineHashes); | |
66 size_t frameCount = baselineHashes.size(); | |
67 | |
68 // Random decoding should get the same results as sequential decoding. | |
69 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | |
70 decoder->setData(fullData.get(), true); | |
71 const size_t skippingStep = 5; | |
72 for (size_t i = 0; i < skippingStep; ++i) { | |
73 for (size_t j = i; j < frameCount; j += skippingStep) { | |
74 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j); | |
75 ImageFrame* frame = decoder->frameBufferAtIndex(j); | |
76 EXPECT_EQ(baselineHashes[j], hashBitmap(frame->bitmap())); | |
77 } | |
78 } | |
79 | |
80 // Decoding in reverse order. | |
81 decoder = createDecoder(); | |
82 decoder->setData(fullData.get(), true); | |
83 for (size_t i = frameCount; i; --i) { | |
84 SCOPED_TRACE(testing::Message() << "Reverse i:" << i); | |
85 ImageFrame* frame = decoder->frameBufferAtIndex(i - 1); | |
86 EXPECT_EQ(baselineHashes[i - 1], hashBitmap(frame->bitmap())); | |
87 } | |
88 } | |
89 | |
90 void testRandomDecodeAfterClearFrameBufferCache(const char* webpFile) { | |
91 SCOPED_TRACE(webpFile); | |
92 | |
93 RefPtr<SharedBuffer> data = readFile(webpFile); | |
94 ASSERT_TRUE(data.get()); | |
95 Vector<unsigned> baselineHashes; | |
96 createDecodingBaseline(&createDecoder, data.get(), &baselineHashes); | |
97 size_t frameCount = baselineHashes.size(); | |
98 | |
99 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | |
100 decoder->setData(data.get(), true); | |
101 for (size_t clearExceptFrame = 0; clearExceptFrame < frameCount; | |
102 ++clearExceptFrame) { | |
103 decoder->clearCacheExceptFrame(clearExceptFrame); | |
104 const size_t skippingStep = 5; | |
105 for (size_t i = 0; i < skippingStep; ++i) { | |
106 for (size_t j = 0; j < frameCount; j += skippingStep) { | |
107 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j); | |
108 ImageFrame* frame = decoder->frameBufferAtIndex(j); | |
109 EXPECT_EQ(baselineHashes[j], hashBitmap(frame->bitmap())); | |
110 } | |
111 } | |
112 } | |
113 } | |
114 | |
115 void testDecodeAfterReallocatingData(const char* webpFile) { | |
116 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | |
117 RefPtr<SharedBuffer> data = readFile(webpFile); | |
118 ASSERT_TRUE(data.get()); | |
119 | |
120 // Parse from 'data'. | |
121 decoder->setData(data.get(), true); | |
122 size_t frameCount = decoder->frameCount(); | |
123 | |
124 // ... and then decode frames from 'reallocatedData'. | |
125 RefPtr<SharedBuffer> reallocatedData = data.get()->copy(); | |
126 ASSERT_TRUE(reallocatedData.get()); | |
127 data.clear(); | |
128 decoder->setData(reallocatedData.get(), true); | |
129 | |
130 for (size_t i = 0; i < frameCount; ++i) { | |
131 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
132 EXPECT_EQ(ImageFrame::FrameComplete, frame->getStatus()); | |
133 } | |
134 } | |
135 | |
136 void testByteByByteSizeAvailable(const char* webpFile, | |
137 size_t frameOffset, | |
138 bool hasColorSpace, | |
139 int expectedRepetitionCount) { | |
140 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | |
141 RefPtr<SharedBuffer> data = readFile(webpFile); | |
142 ASSERT_TRUE(data.get()); | |
143 EXPECT_LT(frameOffset, data->size()); | |
144 | |
145 // Send data to the decoder byte-by-byte and use the provided frame offset in | |
146 // the data to check that isSizeAvailable() changes state only when that | |
147 // offset is reached. Also check other decoder state. | |
148 for (size_t length = 1; length <= frameOffset; ++length) { | |
149 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), length); | |
150 decoder->setData(tempData.get(), false); | |
151 | |
152 if (length < frameOffset) { | |
153 EXPECT_FALSE(decoder->isSizeAvailable()); | |
154 EXPECT_TRUE(decoder->size().isEmpty()); | |
155 EXPECT_FALSE(decoder->hasEmbeddedColorSpace()); | |
156 EXPECT_EQ(0u, decoder->frameCount()); | |
157 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
158 EXPECT_FALSE(decoder->frameBufferAtIndex(0)); | |
159 } else { | |
160 EXPECT_TRUE(decoder->isSizeAvailable()); | |
161 EXPECT_FALSE(decoder->size().isEmpty()); | |
162 EXPECT_EQ(decoder->hasEmbeddedColorSpace(), hasColorSpace); | |
163 EXPECT_EQ(1u, decoder->frameCount()); | |
164 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount()); | |
165 } | |
166 | |
167 EXPECT_FALSE(decoder->failed()); | |
168 if (decoder->failed()) | |
169 return; | |
170 } | |
171 } | |
172 | |
173 // If 'parseErrorExpected' is true, error is expected during parse (frameCount() | 59 // If 'parseErrorExpected' is true, error is expected during parse (frameCount() |
174 // call); else error is expected during decode (frameBufferAtIndex() call). | 60 // call); else error is expected during decode (frameBufferAtIndex() call). |
175 void testInvalidImage(const char* webpFile, bool parseErrorExpected) { | 61 void testInvalidImage(const char* webpFile, bool parseErrorExpected) { |
176 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | 62 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
177 | 63 |
178 RefPtr<SharedBuffer> data = readFile(webpFile); | 64 RefPtr<SharedBuffer> data = readFile(webpFile); |
179 ASSERT_TRUE(data.get()); | 65 ASSERT_TRUE(data.get()); |
180 decoder->setData(data.get(), true); | 66 decoder->setData(data.get(), true); |
181 | 67 |
182 if (parseErrorExpected) { | 68 if (parseErrorExpected) { |
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 decoder->setData(fullData.get(), true); | 384 decoder->setData(fullData.get(), true); |
499 EXPECT_EQ(1u, decoder->frameCount()); | 385 EXPECT_EQ(1u, decoder->frameCount()); |
500 frame = decoder->frameBufferAtIndex(0); | 386 frame = decoder->frameBufferAtIndex(0); |
501 ASSERT_TRUE(frame); | 387 ASSERT_TRUE(frame); |
502 EXPECT_EQ(ImageFrame::FramePartial, frame->getStatus()); | 388 EXPECT_EQ(ImageFrame::FramePartial, frame->getStatus()); |
503 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | 389 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); |
504 EXPECT_TRUE(decoder->failed()); | 390 EXPECT_TRUE(decoder->failed()); |
505 } | 391 } |
506 | 392 |
507 TEST(AnimatedWebPTests, progressiveDecode) { | 393 TEST(AnimatedWebPTests, progressiveDecode) { |
508 RefPtr<SharedBuffer> fullData = | 394 testProgressiveDecoding( |
509 readFile("/LayoutTests/fast/images/resources/webp-animated.webp"); | 395 &createDecoder, "/LayoutTests/fast/images/resources/webp-animated.webp"); |
510 ASSERT_TRUE(fullData.get()); | |
511 const size_t fullLength = fullData->size(); | |
512 | |
513 std::unique_ptr<ImageDecoder> decoder; | |
514 ImageFrame* frame; | |
515 | |
516 Vector<unsigned> truncatedHashes; | |
517 Vector<unsigned> progressiveHashes; | |
518 | |
519 // Compute hashes when the file is truncated. | |
520 const size_t increment = 1; | |
521 for (size_t i = 1; i <= fullLength; i += increment) { | |
522 decoder = createDecoder(); | |
523 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
524 decoder->setData(data.get(), i == fullLength); | |
525 frame = decoder->frameBufferAtIndex(0); | |
526 if (!frame) { | |
527 truncatedHashes.append(0); | |
528 continue; | |
529 } | |
530 truncatedHashes.append(hashBitmap(frame->bitmap())); | |
531 } | |
532 | |
533 // Compute hashes when the file is progressively decoded. | |
534 decoder = createDecoder(); | |
535 for (size_t i = 1; i <= fullLength; i += increment) { | |
536 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
537 decoder->setData(data.get(), i == fullLength); | |
538 frame = decoder->frameBufferAtIndex(0); | |
539 if (!frame) { | |
540 progressiveHashes.append(0); | |
541 continue; | |
542 } | |
543 progressiveHashes.append(hashBitmap(frame->bitmap())); | |
544 } | |
545 | |
546 bool match = true; | |
547 for (size_t i = 0; i < truncatedHashes.size(); ++i) { | |
548 if (truncatedHashes[i] != progressiveHashes[i]) { | |
549 match = false; | |
550 break; | |
551 } | |
552 } | |
553 EXPECT_TRUE(match); | |
554 } | 396 } |
555 | 397 |
556 TEST(AnimatedWebPTests, frameIsCompleteAndDuration) { | 398 TEST(AnimatedWebPTests, frameIsCompleteAndDuration) { |
557 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | 399 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
558 | 400 |
559 RefPtr<SharedBuffer> data = | 401 RefPtr<SharedBuffer> data = |
560 readFile("/LayoutTests/fast/images/resources/webp-animated.webp"); | 402 readFile("/LayoutTests/fast/images/resources/webp-animated.webp"); |
561 ASSERT_TRUE(data.get()); | 403 ASSERT_TRUE(data.get()); |
562 | 404 |
563 ASSERT_GE(data->size(), 10u); | 405 ASSERT_GE(data->size(), 10u); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
609 decoder->frameBufferAtIndex(i)->requiredPreviousFrameIndex()); | 451 decoder->frameBufferAtIndex(i)->requiredPreviousFrameIndex()); |
610 | 452 |
611 decoder->setData(fullData.get(), true); | 453 decoder->setData(fullData.get(), true); |
612 for (size_t i = 0; i < frameCount; ++i) | 454 for (size_t i = 0; i < frameCount; ++i) |
613 EXPECT_EQ(kNotFound, | 455 EXPECT_EQ(kNotFound, |
614 decoder->frameBufferAtIndex(i)->requiredPreviousFrameIndex()); | 456 decoder->frameBufferAtIndex(i)->requiredPreviousFrameIndex()); |
615 } | 457 } |
616 | 458 |
617 TEST(AnimatedWebPTests, randomFrameDecode) { | 459 TEST(AnimatedWebPTests, randomFrameDecode) { |
618 testRandomFrameDecode( | 460 testRandomFrameDecode( |
619 "/LayoutTests/fast/images/resources/webp-animated.webp"); | 461 &createDecoder, "/LayoutTests/fast/images/resources/webp-animated.webp"); |
620 testRandomFrameDecode( | 462 testRandomFrameDecode( |
| 463 &createDecoder, |
621 "/LayoutTests/fast/images/resources/webp-animated-opaque.webp"); | 464 "/LayoutTests/fast/images/resources/webp-animated-opaque.webp"); |
622 testRandomFrameDecode( | 465 testRandomFrameDecode( |
| 466 &createDecoder, |
623 "/LayoutTests/fast/images/resources/webp-animated-large.webp"); | 467 "/LayoutTests/fast/images/resources/webp-animated-large.webp"); |
624 testRandomFrameDecode( | 468 testRandomFrameDecode( |
| 469 &createDecoder, |
625 "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); | 470 "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); |
626 } | 471 } |
627 | 472 |
628 TEST(AnimatedWebPTests, randomDecodeAfterClearFrameBufferCache) { | 473 TEST(AnimatedWebPTests, randomDecodeAfterClearFrameBufferCache) { |
629 testRandomDecodeAfterClearFrameBufferCache( | 474 testRandomDecodeAfterClearFrameBufferCache( |
630 "/LayoutTests/fast/images/resources/webp-animated.webp"); | 475 &createDecoder, "/LayoutTests/fast/images/resources/webp-animated.webp"); |
631 testRandomDecodeAfterClearFrameBufferCache( | 476 testRandomDecodeAfterClearFrameBufferCache( |
| 477 &createDecoder, |
632 "/LayoutTests/fast/images/resources/webp-animated-opaque.webp"); | 478 "/LayoutTests/fast/images/resources/webp-animated-opaque.webp"); |
633 testRandomDecodeAfterClearFrameBufferCache( | 479 testRandomDecodeAfterClearFrameBufferCache( |
| 480 &createDecoder, |
634 "/LayoutTests/fast/images/resources/webp-animated-large.webp"); | 481 "/LayoutTests/fast/images/resources/webp-animated-large.webp"); |
635 testRandomDecodeAfterClearFrameBufferCache( | 482 testRandomDecodeAfterClearFrameBufferCache( |
| 483 &createDecoder, |
636 "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); | 484 "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); |
637 } | 485 } |
638 | 486 |
639 TEST(AnimatedWebPTests, | 487 TEST(AnimatedWebPTests, |
640 DISABLED_resumePartialDecodeAfterClearFrameBufferCache) { | 488 DISABLED_resumePartialDecodeAfterClearFrameBufferCache) { |
641 RefPtr<SharedBuffer> fullData = | 489 RefPtr<SharedBuffer> fullData = |
642 readFile("/LayoutTests/fast/images/resources/webp-animated-large.webp"); | 490 readFile("/LayoutTests/fast/images/resources/webp-animated-large.webp"); |
643 ASSERT_TRUE(fullData.get()); | 491 ASSERT_TRUE(fullData.get()); |
644 Vector<unsigned> baselineHashes; | 492 Vector<unsigned> baselineHashes; |
645 createDecodingBaseline(&createDecoder, fullData.get(), &baselineHashes); | 493 createDecodingBaseline(&createDecoder, fullData.get(), &baselineHashes); |
(...skipping 20 matching lines...) Expand all Loading... |
666 decoder->clearCacheExceptFrame(kNotFound); | 514 decoder->clearCacheExceptFrame(kNotFound); |
667 | 515 |
668 // Resume decoding of the first frame. | 516 // Resume decoding of the first frame. |
669 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0); | 517 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0); |
670 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->getStatus()); | 518 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->getStatus()); |
671 EXPECT_EQ(baselineHashes[0], hashBitmap(firstFrame->bitmap())); | 519 EXPECT_EQ(baselineHashes[0], hashBitmap(firstFrame->bitmap())); |
672 } | 520 } |
673 | 521 |
674 TEST(AnimatedWebPTests, decodeAfterReallocatingData) { | 522 TEST(AnimatedWebPTests, decodeAfterReallocatingData) { |
675 testDecodeAfterReallocatingData( | 523 testDecodeAfterReallocatingData( |
676 "/LayoutTests/fast/images/resources/webp-animated.webp"); | 524 &createDecoder, "/LayoutTests/fast/images/resources/webp-animated.webp"); |
677 testDecodeAfterReallocatingData( | 525 testDecodeAfterReallocatingData( |
| 526 &createDecoder, |
678 "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); | 527 "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); |
679 } | 528 } |
680 | 529 |
681 TEST(AnimatedWebPTests, alphaBlending) { | 530 TEST(AnimatedWebPTests, alphaBlending) { |
682 testAlphaBlending("/LayoutTests/fast/images/resources/webp-animated.webp"); | 531 testAlphaBlending("/LayoutTests/fast/images/resources/webp-animated.webp"); |
683 testAlphaBlending( | 532 testAlphaBlending( |
684 "/LayoutTests/fast/images/resources/webp-animated-semitransparent1.webp"); | 533 "/LayoutTests/fast/images/resources/webp-animated-semitransparent1.webp"); |
685 testAlphaBlending( | 534 testAlphaBlending( |
686 "/LayoutTests/fast/images/resources/webp-animated-semitransparent2.webp"); | 535 "/LayoutTests/fast/images/resources/webp-animated-semitransparent2.webp"); |
687 testAlphaBlending( | 536 testAlphaBlending( |
688 "/LayoutTests/fast/images/resources/webp-animated-semitransparent3.webp"); | 537 "/LayoutTests/fast/images/resources/webp-animated-semitransparent3.webp"); |
689 testAlphaBlending( | 538 testAlphaBlending( |
690 "/LayoutTests/fast/images/resources/webp-animated-semitransparent4.webp"); | 539 "/LayoutTests/fast/images/resources/webp-animated-semitransparent4.webp"); |
691 } | 540 } |
692 | 541 |
693 TEST(AnimatedWebPTests, isSizeAvailable) { | 542 TEST(AnimatedWebPTests, isSizeAvailable) { |
694 testByteByByteSizeAvailable( | 543 testByteByByteSizeAvailable( |
695 "/LayoutTests/fast/images/resources/webp-animated.webp", 142u, false, | 544 &createDecoder, "/LayoutTests/fast/images/resources/webp-animated.webp", |
696 cAnimationLoopInfinite); | 545 142u, false, cAnimationLoopInfinite); |
697 // FIXME: Add color profile support for animated webp images. | 546 // FIXME: Add color profile support for animated webp images. |
698 testByteByByteSizeAvailable( | 547 testByteByByteSizeAvailable( |
| 548 &createDecoder, |
699 "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp", 1404u, | 549 "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp", 1404u, |
700 false, 32000); | 550 false, 32000); |
701 } | 551 } |
702 | 552 |
703 TEST(StaticWebPTests, truncatedImage) { | 553 TEST(StaticWebPTests, truncatedImage) { |
704 // VP8 data is truncated. | 554 // VP8 data is truncated. |
705 testInvalidImage("/LayoutTests/fast/images/resources/truncated.webp", false); | 555 testInvalidImage("/LayoutTests/fast/images/resources/truncated.webp", false); |
706 // Chunk size in RIFF header doesn't match the file size. | 556 // Chunk size in RIFF header doesn't match the file size. |
707 testInvalidImage("/LayoutTests/fast/images/resources/truncated2.webp", true); | 557 testInvalidImage("/LayoutTests/fast/images/resources/truncated2.webp", true); |
708 } | 558 } |
709 | 559 |
710 // Regression test for a bug where some valid images were failing to decode | 560 // Regression test for a bug where some valid images were failing to decode |
711 // incrementally. | 561 // incrementally. |
712 TEST(StaticWebPTests, incrementalDecode) { | 562 TEST(StaticWebPTests, incrementalDecode) { |
713 testByteByByteDecode(&createDecoder, | 563 testByteByByteDecode(&createDecoder, |
714 "/LayoutTests/fast/images/resources/crbug.364830.webp", | 564 "/LayoutTests/fast/images/resources/crbug.364830.webp", |
715 1u, cAnimationNone); | 565 1u, cAnimationNone); |
716 } | 566 } |
717 | 567 |
718 TEST(StaticWebPTests, isSizeAvailable) { | 568 TEST(StaticWebPTests, isSizeAvailable) { |
719 testByteByByteSizeAvailable( | 569 testByteByByteSizeAvailable( |
| 570 &createDecoder, |
720 "/LayoutTests/fast/images/resources/webp-color-profile-lossy.webp", 520u, | 571 "/LayoutTests/fast/images/resources/webp-color-profile-lossy.webp", 520u, |
721 true, cAnimationNone); | 572 true, cAnimationNone); |
722 testByteByByteSizeAvailable("/LayoutTests/fast/images/resources/test.webp", | 573 testByteByByteSizeAvailable(&createDecoder, |
| 574 "/LayoutTests/fast/images/resources/test.webp", |
723 30u, false, cAnimationNone); | 575 30u, false, cAnimationNone); |
724 } | 576 } |
725 | 577 |
726 TEST(StaticWebPTests, notAnimated) { | 578 TEST(StaticWebPTests, notAnimated) { |
727 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | 579 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
728 RefPtr<SharedBuffer> data = readFile( | 580 RefPtr<SharedBuffer> data = readFile( |
729 "/LayoutTests/fast/images/resources/webp-color-profile-lossy.webp"); | 581 "/LayoutTests/fast/images/resources/webp-color-profile-lossy.webp"); |
730 ASSERT_TRUE(data.get()); | 582 ASSERT_TRUE(data.get()); |
731 decoder->setData(data.get(), true); | 583 decoder->setData(data.get(), true); |
732 EXPECT_EQ(1u, decoder->frameCount()); | 584 EXPECT_EQ(1u, decoder->frameCount()); |
733 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); | 585 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); |
734 } | 586 } |
735 | 587 |
736 } // namespace blink | 588 } // namespace blink |
OLD | NEW |