1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include "gtest/gtest.h"
6 
7 #include "Common.h"
8 #include "AnimationSurfaceProvider.h"
9 #include "DecodePool.h"
10 #include "Decoder.h"
11 #include "DecoderFactory.h"
12 #include "decoders/nsBMPDecoder.h"
13 #include "IDecodingTask.h"
14 #include "ImageOps.h"
15 #include "imgIContainer.h"
16 #include "ImageFactory.h"
17 #include "mozilla/gfx/2D.h"
18 #include "nsComponentManagerUtils.h"
19 #include "nsCOMPtr.h"
20 #include "nsIInputStream.h"
21 #include "mozilla/RefPtr.h"
22 #include "nsStreamUtils.h"
23 #include "nsString.h"
24 #include "nsThreadUtils.h"
25 #include "ProgressTracker.h"
26 #include "SourceBuffer.h"
27 
28 using namespace mozilla;
29 using namespace mozilla::gfx;
30 using namespace mozilla::image;
31 
CheckDecoderState(const ImageTestCase & aTestCase,image::Decoder * aDecoder)32 static already_AddRefed<SourceSurface> CheckDecoderState(
33     const ImageTestCase& aTestCase, image::Decoder* aDecoder) {
34   // image::Decoder should match what we asked for in the MIME type.
35   EXPECT_NE(aDecoder->GetType(), DecoderType::UNKNOWN);
36   EXPECT_EQ(aDecoder->GetType(),
37             DecoderFactory::GetDecoderType(aTestCase.mMimeType));
38 
39   EXPECT_TRUE(aDecoder->GetDecodeDone());
40   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR), aDecoder->HasError());
41 
42   // Verify that the decoder made the expected progress.
43   Progress progress = aDecoder->TakeProgress();
44   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR),
45             bool(progress & FLAG_HAS_ERROR));
46 
47   if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
48     return nullptr;  // That's all we can check for bad images.
49   }
50 
51   EXPECT_TRUE(bool(progress & FLAG_SIZE_AVAILABLE));
52   EXPECT_TRUE(bool(progress & FLAG_DECODE_COMPLETE));
53   EXPECT_TRUE(bool(progress & FLAG_FRAME_COMPLETE));
54   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT),
55             bool(progress & FLAG_HAS_TRANSPARENCY));
56   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
57             bool(progress & FLAG_IS_ANIMATED));
58 
59   // The decoder should get the correct size.
60   IntSize size = aDecoder->Size();
61   EXPECT_EQ(aTestCase.mSize.width, size.width);
62   EXPECT_EQ(aTestCase.mSize.height, size.height);
63 
64   // Get the current frame, which is always the first frame of the image
65   // because CreateAnonymousDecoder() forces a first-frame-only decode.
66   RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
67   RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
68 
69   // Verify that the resulting surfaces matches our expectations.
70   EXPECT_TRUE(surface->IsDataSourceSurface());
71   EXPECT_TRUE(surface->GetFormat() == SurfaceFormat::OS_RGBX ||
72               surface->GetFormat() == SurfaceFormat::OS_RGBA);
73   EXPECT_EQ(aTestCase.mOutputSize, surface->GetSize());
74 
75   return surface.forget();
76 }
77 
CheckDecoderResults(const ImageTestCase & aTestCase,image::Decoder * aDecoder)78 static void CheckDecoderResults(const ImageTestCase& aTestCase,
79                                 image::Decoder* aDecoder) {
80   RefPtr<SourceSurface> surface = CheckDecoderState(aTestCase, aDecoder);
81   if (!surface) {
82     return;
83   }
84 
85   if (aTestCase.mFlags & TEST_CASE_IGNORE_OUTPUT) {
86     return;
87   }
88 
89   // Check the output.
90   EXPECT_TRUE(IsSolidColor(surface, aTestCase.Color(), aTestCase.Fuzz()));
91 }
92 
93 template <typename Func>
WithBadBufferDecode(const ImageTestCase & aTestCase,const Maybe<IntSize> & aOutputSize,Func aResultChecker)94 void WithBadBufferDecode(const ImageTestCase& aTestCase,
95                          const Maybe<IntSize>& aOutputSize,
96                          Func aResultChecker) {
97   // Prepare a SourceBuffer with an error that will immediately move iterators
98   // to COMPLETE.
99   auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
100   sourceBuffer->ExpectLength(SIZE_MAX);
101 
102   // Create a decoder.
103   DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
104   RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
105       decoderType, sourceBuffer, aOutputSize, DecoderFlags::FIRST_FRAME_ONLY,
106       aTestCase.mSurfaceFlags);
107   ASSERT_TRUE(decoder != nullptr);
108   RefPtr<IDecodingTask> task =
109       new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
110 
111   // Run the full decoder synchronously on the main thread.
112   task->Run();
113 
114   // Call the lambda to verify the expected results.
115   aResultChecker(decoder);
116 }
117 
CheckDecoderBadBuffer(const ImageTestCase & aTestCase)118 static void CheckDecoderBadBuffer(const ImageTestCase& aTestCase) {
119   WithBadBufferDecode(aTestCase, Nothing(), [&](image::Decoder* aDecoder) {
120     CheckDecoderResults(aTestCase, aDecoder);
121   });
122 }
123 
124 template <typename Func>
WithSingleChunkDecode(const ImageTestCase & aTestCase,const Maybe<IntSize> & aOutputSize,bool aUseDecodePool,Func aResultChecker)125 void WithSingleChunkDecode(const ImageTestCase& aTestCase,
126                            const Maybe<IntSize>& aOutputSize,
127                            bool aUseDecodePool, Func aResultChecker) {
128   nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
129   ASSERT_TRUE(inputStream != nullptr);
130 
131   // Figure out how much data we have.
132   uint64_t length;
133   nsresult rv = inputStream->Available(&length);
134   ASSERT_TRUE(NS_SUCCEEDED(rv));
135 
136   // Write the data into a SourceBuffer.
137   auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
138   sourceBuffer->ExpectLength(length);
139   rv = sourceBuffer->AppendFromInputStream(inputStream, length);
140   ASSERT_TRUE(NS_SUCCEEDED(rv));
141   sourceBuffer->Complete(NS_OK);
142 
143   // Create a decoder.
144   DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
145   RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
146       decoderType, sourceBuffer, aOutputSize, DecoderFlags::FIRST_FRAME_ONLY,
147       aTestCase.mSurfaceFlags);
148   ASSERT_TRUE(decoder != nullptr);
149   RefPtr<IDecodingTask> task =
150       new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
151 
152   if (aUseDecodePool) {
153     DecodePool::Singleton()->AsyncRun(task.get());
154 
155     while (!decoder->GetDecodeDone()) {
156       task->Resume();
157     }
158   } else {  // Run the full decoder synchronously on the main thread.
159     task->Run();
160   }
161 
162   // Call the lambda to verify the expected results.
163   aResultChecker(decoder);
164 }
165 
CheckDecoderSingleChunk(const ImageTestCase & aTestCase,bool aUseDecodePool=false)166 static void CheckDecoderSingleChunk(const ImageTestCase& aTestCase,
167                                     bool aUseDecodePool = false) {
168   WithSingleChunkDecode(aTestCase, Nothing(), aUseDecodePool,
169                         [&](image::Decoder* aDecoder) {
170                           CheckDecoderResults(aTestCase, aDecoder);
171                         });
172 }
173 
174 template <typename Func>
WithDelayedChunkDecode(const ImageTestCase & aTestCase,const Maybe<IntSize> & aOutputSize,Func aResultChecker)175 void WithDelayedChunkDecode(const ImageTestCase& aTestCase,
176                             const Maybe<IntSize>& aOutputSize,
177                             Func aResultChecker) {
178   nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
179   ASSERT_TRUE(inputStream != nullptr);
180 
181   // Figure out how much data we have.
182   uint64_t length;
183   nsresult rv = inputStream->Available(&length);
184   ASSERT_TRUE(NS_SUCCEEDED(rv));
185 
186   // Prepare an empty SourceBuffer.
187   auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
188 
189   // Create a decoder.
190   DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
191   RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
192       decoderType, sourceBuffer, aOutputSize, DecoderFlags::FIRST_FRAME_ONLY,
193       aTestCase.mSurfaceFlags);
194   ASSERT_TRUE(decoder != nullptr);
195   RefPtr<IDecodingTask> task =
196       new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ true);
197 
198   // Run the full decoder synchronously. It should now be waiting on
199   // the iterator to yield some data since we haven't written anything yet.
200   task->Run();
201 
202   // Writing all of the data should wake up the decoder to complete.
203   sourceBuffer->ExpectLength(length);
204   rv = sourceBuffer->AppendFromInputStream(inputStream, length);
205   ASSERT_TRUE(NS_SUCCEEDED(rv));
206   sourceBuffer->Complete(NS_OK);
207 
208   // It would have gotten posted to the main thread to avoid mutex contention.
209   SpinPendingEvents();
210 
211   // Call the lambda to verify the expected results.
212   aResultChecker(decoder);
213 }
214 
CheckDecoderDelayedChunk(const ImageTestCase & aTestCase)215 static void CheckDecoderDelayedChunk(const ImageTestCase& aTestCase) {
216   WithDelayedChunkDecode(aTestCase, Nothing(), [&](image::Decoder* aDecoder) {
217     CheckDecoderResults(aTestCase, aDecoder);
218   });
219 }
220 
CheckDecoderMultiChunk(const ImageTestCase & aTestCase,uint64_t aChunkSize=1)221 static void CheckDecoderMultiChunk(const ImageTestCase& aTestCase,
222                                    uint64_t aChunkSize = 1) {
223   nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
224   ASSERT_TRUE(inputStream != nullptr);
225 
226   // Figure out how much data we have.
227   uint64_t length;
228   nsresult rv = inputStream->Available(&length);
229   ASSERT_TRUE(NS_SUCCEEDED(rv));
230 
231   // Create a SourceBuffer and a decoder.
232   auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
233   sourceBuffer->ExpectLength(length);
234   DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
235   RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
236       decoderType, sourceBuffer, Nothing(), DecoderFlags::FIRST_FRAME_ONLY,
237       aTestCase.mSurfaceFlags);
238   ASSERT_TRUE(decoder != nullptr);
239   RefPtr<IDecodingTask> task =
240       new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ true);
241 
242   // Run the full decoder synchronously. It should now be waiting on
243   // the iterator to yield some data since we haven't written anything yet.
244   task->Run();
245 
246   while (length > 0) {
247     uint64_t read = length > aChunkSize ? aChunkSize : length;
248     length -= read;
249 
250     uint64_t available = 0;
251     rv = inputStream->Available(&available);
252     ASSERT_TRUE(available >= read);
253     ASSERT_TRUE(NS_SUCCEEDED(rv));
254 
255     // Writing any data should wake up the decoder to complete.
256     rv = sourceBuffer->AppendFromInputStream(inputStream, read);
257     ASSERT_TRUE(NS_SUCCEEDED(rv));
258 
259     // It would have gotten posted to the main thread to avoid mutex contention.
260     SpinPendingEvents();
261   }
262 
263   sourceBuffer->Complete(NS_OK);
264   SpinPendingEvents();
265 
266   CheckDecoderResults(aTestCase, decoder);
267 }
268 
CheckDownscaleDuringDecode(const ImageTestCase & aTestCase)269 static void CheckDownscaleDuringDecode(const ImageTestCase& aTestCase) {
270   // This function expects that |aTestCase| consists of 25 lines of green,
271   // followed by 25 lines of red, followed by 25 lines of green, followed by 25
272   // more lines of red. We'll downscale it from 100x100 to 20x20.
273   IntSize outputSize(20, 20);
274 
275   WithSingleChunkDecode(
276       aTestCase, Some(outputSize), /* aUseDecodePool */ false,
277       [&](image::Decoder* aDecoder) {
278         RefPtr<SourceSurface> surface = CheckDecoderState(aTestCase, aDecoder);
279 
280         // There are no downscale-during-decode tests that have
281         // TEST_CASE_HAS_ERROR set, so we expect to always get a surface here.
282         EXPECT_TRUE(surface != nullptr);
283 
284         if (aTestCase.mFlags & TEST_CASE_IGNORE_OUTPUT) {
285           return;
286         }
287 
288         // Check that the downscaled image is correct. Note that we skip rows
289         // near the transitions between colors, since the downscaler does not
290         // produce a sharp boundary at these points. Even some of the rows we
291         // test need a small amount of fuzz; this is just the nature of Lanczos
292         // downscaling.
293         EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4,
294                                       aTestCase.ChooseColor(BGRAColor::Green()),
295                                       /* aFuzz = */ 47));
296         EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3,
297                                       aTestCase.ChooseColor(BGRAColor::Red()),
298                                       /* aFuzz = */ 27));
299         EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(),
300                                       /* aFuzz = */ 47));
301         EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4,
302                                       aTestCase.ChooseColor(BGRAColor::Red()),
303                                       /* aFuzz = */ 27));
304       });
305 }
306 
CheckAnimationDecoderResults(const ImageTestCase & aTestCase,AnimationSurfaceProvider * aProvider,image::Decoder * aDecoder)307 static void CheckAnimationDecoderResults(const ImageTestCase& aTestCase,
308                                          AnimationSurfaceProvider* aProvider,
309                                          image::Decoder* aDecoder) {
310   EXPECT_TRUE(aDecoder->GetDecodeDone());
311   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR), aDecoder->HasError());
312 
313   if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
314     return;  // That's all we can check for bad images.
315   }
316 
317   // The decoder should get the correct size.
318   IntSize size = aDecoder->Size();
319   EXPECT_EQ(aTestCase.mSize.width, size.width);
320   EXPECT_EQ(aTestCase.mSize.height, size.height);
321 
322   if (aTestCase.mFlags & TEST_CASE_IGNORE_OUTPUT) {
323     return;
324   }
325 
326   // Check the output.
327   AutoTArray<BGRAColor, 2> framePixels;
328   framePixels.AppendElement(aTestCase.ChooseColor(BGRAColor::Green()));
329   framePixels.AppendElement(
330       aTestCase.ChooseColor(BGRAColor(0x7F, 0x7F, 0x7F, 0xFF)));
331 
332   DrawableSurface drawableSurface(WrapNotNull(aProvider));
333   for (size_t i = 0; i < framePixels.Length(); ++i) {
334     nsresult rv = drawableSurface.Seek(i);
335     EXPECT_TRUE(NS_SUCCEEDED(rv));
336 
337     // Check the first frame, all green.
338     RawAccessFrameRef rawFrame = drawableSurface->RawAccessRef();
339     RefPtr<SourceSurface> surface = rawFrame->GetSourceSurface();
340 
341     // Verify that the resulting surfaces matches our expectations.
342     EXPECT_TRUE(surface->IsDataSourceSurface());
343     EXPECT_TRUE(surface->GetFormat() == SurfaceFormat::OS_RGBX ||
344                 surface->GetFormat() == SurfaceFormat::OS_RGBA);
345     EXPECT_EQ(aTestCase.mOutputSize, surface->GetSize());
346     EXPECT_TRUE(IsSolidColor(surface, framePixels[i], aTestCase.Fuzz()));
347   }
348 
349   // Should be no more frames.
350   nsresult rv = drawableSurface.Seek(framePixels.Length());
351   EXPECT_TRUE(NS_FAILED(rv));
352 }
353 
354 template <typename Func>
WithSingleChunkAnimationDecode(const ImageTestCase & aTestCase,Func aResultChecker)355 static void WithSingleChunkAnimationDecode(const ImageTestCase& aTestCase,
356                                            Func aResultChecker) {
357   // Create an image.
358   RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
359       nsDependentCString(aTestCase.mMimeType));
360   ASSERT_TRUE(!image->HasError());
361 
362   NotNull<RefPtr<RasterImage>> rasterImage =
363       WrapNotNull(static_cast<RasterImage*>(image.get()));
364 
365   nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
366   ASSERT_TRUE(inputStream != nullptr);
367 
368   // Figure out how much data we have.
369   uint64_t length;
370   nsresult rv = inputStream->Available(&length);
371   ASSERT_TRUE(NS_SUCCEEDED(rv));
372 
373   // Write the data into a SourceBuffer.
374   NotNull<RefPtr<SourceBuffer>> sourceBuffer = WrapNotNull(new SourceBuffer());
375   sourceBuffer->ExpectLength(length);
376   rv = sourceBuffer->AppendFromInputStream(inputStream, length);
377   ASSERT_TRUE(NS_SUCCEEDED(rv));
378   sourceBuffer->Complete(NS_OK);
379 
380   // Create a metadata decoder first, because otherwise RasterImage will get
381   // unhappy about finding out the image is animated during a full decode.
382   DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
383   RefPtr<IDecodingTask> task = DecoderFactory::CreateMetadataDecoder(
384       decoderType, rasterImage, sourceBuffer);
385   ASSERT_TRUE(task != nullptr);
386 
387   // Run the metadata decoder synchronously.
388   task->Run();
389 
390   // Create a decoder.
391   DecoderFlags decoderFlags = DefaultDecoderFlags();
392   SurfaceFlags surfaceFlags = aTestCase.mSurfaceFlags;
393   RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
394       decoderType, sourceBuffer, Nothing(), decoderFlags, surfaceFlags);
395   ASSERT_TRUE(decoder != nullptr);
396 
397   // Create an AnimationSurfaceProvider which will manage the decoding process
398   // and make this decoder's output available in the surface cache.
399   SurfaceKey surfaceKey = RasterSurfaceKey(aTestCase.mOutputSize, surfaceFlags,
400                                            PlaybackType::eAnimated);
401   RefPtr<AnimationSurfaceProvider> provider = new AnimationSurfaceProvider(
402       rasterImage, surfaceKey, WrapNotNull(decoder),
403       /* aCurrentFrame */ 0);
404 
405   // Run the full decoder synchronously.
406   provider->Run();
407 
408   // Call the lambda to verify the expected results.
409   aResultChecker(provider, decoder);
410 }
411 
CheckAnimationDecoderSingleChunk(const ImageTestCase & aTestCase)412 static void CheckAnimationDecoderSingleChunk(const ImageTestCase& aTestCase) {
413   WithSingleChunkAnimationDecode(
414       aTestCase,
415       [&](AnimationSurfaceProvider* aProvider, image::Decoder* aDecoder) {
416         CheckAnimationDecoderResults(aTestCase, aProvider, aDecoder);
417       });
418 }
419 
CheckDecoderFrameFirst(const ImageTestCase & aTestCase)420 static void CheckDecoderFrameFirst(const ImageTestCase& aTestCase) {
421   // Verify that we can decode this test case and retrieve the first frame using
422   // imgIContainer::FRAME_FIRST. This ensures that we correctly trigger a
423   // single-frame decode rather than an animated decode when
424   // imgIContainer::FRAME_FIRST is requested.
425 
426   // Create an image.
427   RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
428       nsDependentCString(aTestCase.mMimeType));
429   ASSERT_TRUE(!image->HasError());
430 
431   nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
432   ASSERT_TRUE(inputStream);
433 
434   // Figure out how much data we have.
435   uint64_t length;
436   nsresult rv = inputStream->Available(&length);
437   ASSERT_TRUE(NS_SUCCEEDED(rv));
438 
439   // Write the data into the image.
440   rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
441                                    static_cast<uint32_t>(length));
442   ASSERT_TRUE(NS_SUCCEEDED(rv));
443 
444   // Let the image know we've sent all the data.
445   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
446   ASSERT_TRUE(NS_SUCCEEDED(rv));
447 
448   RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
449   tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
450 
451   // Lock the image so its surfaces don't disappear during the test.
452   image->LockImage();
453 
454   auto unlock = mozilla::MakeScopeExit([&] { image->UnlockImage(); });
455 
456   // Use GetFrame() to force a sync decode of the image, specifying FRAME_FIRST
457   // to ensure that we don't get an animated decode.
458   RefPtr<SourceSurface> surface = image->GetFrame(
459       imgIContainer::FRAME_FIRST, imgIContainer::FLAG_SYNC_DECODE);
460 
461   // Ensure that the image's metadata meets our expectations.
462   IntSize imageSize(0, 0);
463   rv = image->GetWidth(&imageSize.width);
464   EXPECT_TRUE(NS_SUCCEEDED(rv));
465   rv = image->GetHeight(&imageSize.height);
466   EXPECT_TRUE(NS_SUCCEEDED(rv));
467 
468   EXPECT_EQ(aTestCase.mSize.width, imageSize.width);
469   EXPECT_EQ(aTestCase.mSize.height, imageSize.height);
470 
471   Progress imageProgress = tracker->GetProgress();
472 
473   EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
474   EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
475 
476   // Ensure that we decoded the static version of the image.
477   {
478     LookupResult result = SurfaceCache::Lookup(
479         ImageKey(image.get()),
480         RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
481                          PlaybackType::eStatic),
482         /* aMarkUsed = */ false);
483     ASSERT_EQ(MatchType::EXACT, result.Type());
484     EXPECT_TRUE(bool(result.Surface()));
485   }
486 
487   // Ensure that we didn't decode the animated version of the image.
488   {
489     LookupResult result = SurfaceCache::Lookup(
490         ImageKey(image.get()),
491         RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
492                          PlaybackType::eAnimated),
493         /* aMarkUsed = */ false);
494     ASSERT_EQ(MatchType::NOT_FOUND, result.Type());
495   }
496 
497   // Use GetFrame() to force a sync decode of the image, this time specifying
498   // FRAME_CURRENT to ensure that we get an animated decode.
499   RefPtr<SourceSurface> animatedSurface = image->GetFrame(
500       imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
501 
502   // Ensure that we decoded both frames of the animated version of the image.
503   {
504     LookupResult result = SurfaceCache::Lookup(
505         ImageKey(image.get()),
506         RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
507                          PlaybackType::eAnimated),
508         /* aMarkUsed = */ true);
509     ASSERT_EQ(MatchType::EXACT, result.Type());
510 
511     EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
512     EXPECT_TRUE(bool(result.Surface()));
513 
514     RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
515     EXPECT_TRUE(bool(partialFrame));
516   }
517 
518   // Ensure that the static version is still around.
519   {
520     LookupResult result = SurfaceCache::Lookup(
521         ImageKey(image.get()),
522         RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
523                          PlaybackType::eStatic),
524         /* aMarkUsed = */ true);
525     ASSERT_EQ(MatchType::EXACT, result.Type());
526     EXPECT_TRUE(bool(result.Surface()));
527   }
528 }
529 
CheckDecoderFrameCurrent(const ImageTestCase & aTestCase)530 static void CheckDecoderFrameCurrent(const ImageTestCase& aTestCase) {
531   // Verify that we can decode this test case and retrieve the entire sequence
532   // of frames using imgIContainer::FRAME_CURRENT. This ensures that we
533   // correctly trigger an animated decode rather than a single-frame decode when
534   // imgIContainer::FRAME_CURRENT is requested.
535 
536   // Create an image.
537   RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
538       nsDependentCString(aTestCase.mMimeType));
539   ASSERT_TRUE(!image->HasError());
540 
541   nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
542   ASSERT_TRUE(inputStream);
543 
544   // Figure out how much data we have.
545   uint64_t length;
546   nsresult rv = inputStream->Available(&length);
547   ASSERT_TRUE(NS_SUCCEEDED(rv));
548 
549   // Write the data into the image.
550   rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
551                                    static_cast<uint32_t>(length));
552   ASSERT_TRUE(NS_SUCCEEDED(rv));
553 
554   // Let the image know we've sent all the data.
555   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
556   ASSERT_TRUE(NS_SUCCEEDED(rv));
557 
558   RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
559   tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
560 
561   // Lock the image so its surfaces don't disappear during the test.
562   image->LockImage();
563 
564   // Use GetFrame() to force a sync decode of the image, specifying
565   // FRAME_CURRENT to ensure we get an animated decode.
566   RefPtr<SourceSurface> surface = image->GetFrame(
567       imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
568 
569   // Ensure that the image's metadata meets our expectations.
570   IntSize imageSize(0, 0);
571   rv = image->GetWidth(&imageSize.width);
572   EXPECT_TRUE(NS_SUCCEEDED(rv));
573   rv = image->GetHeight(&imageSize.height);
574   EXPECT_TRUE(NS_SUCCEEDED(rv));
575 
576   EXPECT_EQ(aTestCase.mSize.width, imageSize.width);
577   EXPECT_EQ(aTestCase.mSize.height, imageSize.height);
578 
579   Progress imageProgress = tracker->GetProgress();
580 
581   EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
582   EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
583 
584   // Ensure that we decoded both frames of the animated version of the image.
585   {
586     LookupResult result = SurfaceCache::Lookup(
587         ImageKey(image.get()),
588         RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
589                          PlaybackType::eAnimated),
590         /* aMarkUsed = */ true);
591     ASSERT_EQ(MatchType::EXACT, result.Type());
592 
593     EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
594     EXPECT_TRUE(bool(result.Surface()));
595 
596     RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
597     EXPECT_TRUE(bool(partialFrame));
598   }
599 
600   // Ensure that we didn't decode the static version of the image.
601   {
602     LookupResult result = SurfaceCache::Lookup(
603         ImageKey(image.get()),
604         RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
605                          PlaybackType::eStatic),
606         /* aMarkUsed = */ false);
607     ASSERT_EQ(MatchType::NOT_FOUND, result.Type());
608   }
609 
610   // Use GetFrame() to force a sync decode of the image, this time specifying
611   // FRAME_FIRST to ensure that we get a single-frame decode.
612   RefPtr<SourceSurface> animatedSurface = image->GetFrame(
613       imgIContainer::FRAME_FIRST, imgIContainer::FLAG_SYNC_DECODE);
614 
615   // Ensure that we decoded the static version of the image.
616   {
617     LookupResult result = SurfaceCache::Lookup(
618         ImageKey(image.get()),
619         RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
620                          PlaybackType::eStatic),
621         /* aMarkUsed = */ true);
622     ASSERT_EQ(MatchType::EXACT, result.Type());
623     EXPECT_TRUE(bool(result.Surface()));
624   }
625 
626   // Ensure that both frames of the animated version are still around.
627   {
628     LookupResult result = SurfaceCache::Lookup(
629         ImageKey(image.get()),
630         RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
631                          PlaybackType::eAnimated),
632         /* aMarkUsed = */ true);
633     ASSERT_EQ(MatchType::EXACT, result.Type());
634 
635     EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
636     EXPECT_TRUE(bool(result.Surface()));
637 
638     RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
639     EXPECT_TRUE(bool(partialFrame));
640   }
641 }
642 
643 class ImageDecoders : public ::testing::Test {
644  protected:
645   AutoInitializeImageLib mInit;
646 };
647 
648 #define IMAGE_GTEST_DECODER_BASE_F(test_prefix)                              \
649   TEST_F(ImageDecoders, test_prefix##SingleChunk) {                          \
650     CheckDecoderSingleChunk(Green##test_prefix##TestCase());                 \
651   }                                                                          \
652                                                                              \
653   TEST_F(ImageDecoders, test_prefix##DelayedChunk) {                         \
654     CheckDecoderDelayedChunk(Green##test_prefix##TestCase());                \
655   }                                                                          \
656                                                                              \
657   TEST_F(ImageDecoders, test_prefix##MultiChunk) {                           \
658     CheckDecoderMultiChunk(Green##test_prefix##TestCase());                  \
659   }                                                                          \
660                                                                              \
661   TEST_F(ImageDecoders, test_prefix##DownscaleDuringDecode) {                \
662     CheckDownscaleDuringDecode(Downscaled##test_prefix##TestCase());         \
663   }                                                                          \
664                                                                              \
665   TEST_F(ImageDecoders, test_prefix##ForceSRGB) {                            \
666     CheckDecoderSingleChunk(Green##test_prefix##TestCase().WithSurfaceFlags( \
667         SurfaceFlags::TO_SRGB_COLORSPACE));                                  \
668   }                                                                          \
669                                                                              \
670   TEST_F(ImageDecoders, test_prefix##BadBuffer) {                            \
671     CheckDecoderBadBuffer(Green##test_prefix##TestCase().WithFlags(          \
672         TEST_CASE_HAS_ERROR | TEST_CASE_IGNORE_OUTPUT));                     \
673   }
674 
675 IMAGE_GTEST_DECODER_BASE_F(PNG)
IMAGE_GTEST_DECODER_BASE_F(GIF)676 IMAGE_GTEST_DECODER_BASE_F(GIF)
677 IMAGE_GTEST_DECODER_BASE_F(JPG)
678 IMAGE_GTEST_DECODER_BASE_F(BMP)
679 IMAGE_GTEST_DECODER_BASE_F(ICO)
680 IMAGE_GTEST_DECODER_BASE_F(Icon)
681 IMAGE_GTEST_DECODER_BASE_F(WebP)
682 
683 TEST_F(ImageDecoders, ICOWithANDMaskDownscaleDuringDecode) {
684   CheckDownscaleDuringDecode(DownscaledTransparentICOWithANDMaskTestCase());
685 }
686 
TEST_F(ImageDecoders,WebPLargeMultiChunk)687 TEST_F(ImageDecoders, WebPLargeMultiChunk) {
688   CheckDecoderMultiChunk(LargeWebPTestCase(), /* aChunkSize */ 64);
689 }
690 
TEST_F(ImageDecoders,WebPIccSrgbMultiChunk)691 TEST_F(ImageDecoders, WebPIccSrgbMultiChunk) {
692   CheckDecoderMultiChunk(GreenWebPIccSrgbTestCase());
693 }
694 
TEST_F(ImageDecoders,WebPTransparentSingleChunk)695 TEST_F(ImageDecoders, WebPTransparentSingleChunk) {
696   CheckDecoderSingleChunk(TransparentWebPTestCase());
697 }
698 
TEST_F(ImageDecoders,WebPTransparentNoAlphaHeaderSingleChunk)699 TEST_F(ImageDecoders, WebPTransparentNoAlphaHeaderSingleChunk) {
700   CheckDecoderSingleChunk(TransparentNoAlphaHeaderWebPTestCase());
701 }
702 
TEST_F(ImageDecoders,AVIFSingleChunk)703 TEST_F(ImageDecoders, AVIFSingleChunk) {
704   CheckDecoderSingleChunk(GreenAVIFTestCase());
705 }
706 
707 // This test must use the decode pool in order to check for regressions
708 // of crashing the dav1d decoder when the ImgDecoder threads have a standard-
709 // sized stack.
TEST_F(ImageDecoders,AVIFStackCheck)710 TEST_F(ImageDecoders, AVIFStackCheck) {
711   CheckDecoderSingleChunk(StackCheckAVIFTestCase(), /* aUseDecodePool */ true);
712 }
713 
TEST_F(ImageDecoders,AVIFDelayedChunk)714 TEST_F(ImageDecoders, AVIFDelayedChunk) {
715   CheckDecoderDelayedChunk(GreenAVIFTestCase());
716 }
717 
TEST_F(ImageDecoders,AVIFMultiChunk)718 TEST_F(ImageDecoders, AVIFMultiChunk) {
719   CheckDecoderMultiChunk(GreenAVIFTestCase());
720 }
721 
TEST_F(ImageDecoders,AVIFLargeMultiChunk)722 TEST_F(ImageDecoders, AVIFLargeMultiChunk) {
723   CheckDecoderMultiChunk(LargeAVIFTestCase(), /* aChunkSize */ 64);
724 }
725 
TEST_F(ImageDecoders,AVIFDownscaleDuringDecode)726 TEST_F(ImageDecoders, AVIFDownscaleDuringDecode) {
727   CheckDownscaleDuringDecode(DownscaledAVIFTestCase());
728 }
729 
TEST_F(ImageDecoders,AnimatedGIFSingleChunk)730 TEST_F(ImageDecoders, AnimatedGIFSingleChunk) {
731   CheckDecoderSingleChunk(GreenFirstFrameAnimatedGIFTestCase());
732 }
733 
TEST_F(ImageDecoders,AnimatedGIFMultiChunk)734 TEST_F(ImageDecoders, AnimatedGIFMultiChunk) {
735   CheckDecoderMultiChunk(GreenFirstFrameAnimatedGIFTestCase());
736 }
737 
TEST_F(ImageDecoders,AnimatedGIFWithBlendedFrames)738 TEST_F(ImageDecoders, AnimatedGIFWithBlendedFrames) {
739   CheckAnimationDecoderSingleChunk(GreenFirstFrameAnimatedGIFTestCase());
740 }
741 
TEST_F(ImageDecoders,AnimatedPNGSingleChunk)742 TEST_F(ImageDecoders, AnimatedPNGSingleChunk) {
743   CheckDecoderSingleChunk(GreenFirstFrameAnimatedPNGTestCase());
744 }
745 
TEST_F(ImageDecoders,AnimatedPNGMultiChunk)746 TEST_F(ImageDecoders, AnimatedPNGMultiChunk) {
747   CheckDecoderMultiChunk(GreenFirstFrameAnimatedPNGTestCase());
748 }
749 
TEST_F(ImageDecoders,AnimatedPNGWithBlendedFrames)750 TEST_F(ImageDecoders, AnimatedPNGWithBlendedFrames) {
751   CheckAnimationDecoderSingleChunk(GreenFirstFrameAnimatedPNGTestCase());
752 }
753 
TEST_F(ImageDecoders,AnimatedWebPSingleChunk)754 TEST_F(ImageDecoders, AnimatedWebPSingleChunk) {
755   CheckDecoderSingleChunk(GreenFirstFrameAnimatedWebPTestCase());
756 }
757 
TEST_F(ImageDecoders,AnimatedWebPMultiChunk)758 TEST_F(ImageDecoders, AnimatedWebPMultiChunk) {
759   CheckDecoderMultiChunk(GreenFirstFrameAnimatedWebPTestCase());
760 }
761 
TEST_F(ImageDecoders,AnimatedWebPWithBlendedFrames)762 TEST_F(ImageDecoders, AnimatedWebPWithBlendedFrames) {
763   CheckAnimationDecoderSingleChunk(GreenFirstFrameAnimatedWebPTestCase());
764 }
765 
TEST_F(ImageDecoders,CorruptSingleChunk)766 TEST_F(ImageDecoders, CorruptSingleChunk) {
767   CheckDecoderSingleChunk(CorruptTestCase());
768 }
769 
TEST_F(ImageDecoders,CorruptMultiChunk)770 TEST_F(ImageDecoders, CorruptMultiChunk) {
771   CheckDecoderMultiChunk(CorruptTestCase());
772 }
773 
TEST_F(ImageDecoders,CorruptBMPWithTruncatedHeaderSingleChunk)774 TEST_F(ImageDecoders, CorruptBMPWithTruncatedHeaderSingleChunk) {
775   CheckDecoderSingleChunk(CorruptBMPWithTruncatedHeader());
776 }
777 
TEST_F(ImageDecoders,CorruptBMPWithTruncatedHeaderMultiChunk)778 TEST_F(ImageDecoders, CorruptBMPWithTruncatedHeaderMultiChunk) {
779   CheckDecoderMultiChunk(CorruptBMPWithTruncatedHeader());
780 }
781 
TEST_F(ImageDecoders,CorruptICOWithBadBMPWidthSingleChunk)782 TEST_F(ImageDecoders, CorruptICOWithBadBMPWidthSingleChunk) {
783   CheckDecoderSingleChunk(CorruptICOWithBadBMPWidthTestCase());
784 }
785 
TEST_F(ImageDecoders,CorruptICOWithBadBMPWidthMultiChunk)786 TEST_F(ImageDecoders, CorruptICOWithBadBMPWidthMultiChunk) {
787   CheckDecoderMultiChunk(CorruptICOWithBadBMPWidthTestCase());
788 }
789 
TEST_F(ImageDecoders,CorruptICOWithBadBMPHeightSingleChunk)790 TEST_F(ImageDecoders, CorruptICOWithBadBMPHeightSingleChunk) {
791   CheckDecoderSingleChunk(CorruptICOWithBadBMPHeightTestCase());
792 }
793 
TEST_F(ImageDecoders,CorruptICOWithBadBMPHeightMultiChunk)794 TEST_F(ImageDecoders, CorruptICOWithBadBMPHeightMultiChunk) {
795   CheckDecoderMultiChunk(CorruptICOWithBadBMPHeightTestCase());
796 }
797 
TEST_F(ImageDecoders,CorruptICOWithBadBppSingleChunk)798 TEST_F(ImageDecoders, CorruptICOWithBadBppSingleChunk) {
799   CheckDecoderSingleChunk(CorruptICOWithBadBppTestCase());
800 }
801 
TEST_F(ImageDecoders,AnimatedGIFWithFRAME_FIRST)802 TEST_F(ImageDecoders, AnimatedGIFWithFRAME_FIRST) {
803   CheckDecoderFrameFirst(GreenFirstFrameAnimatedGIFTestCase());
804 }
805 
TEST_F(ImageDecoders,AnimatedGIFWithFRAME_CURRENT)806 TEST_F(ImageDecoders, AnimatedGIFWithFRAME_CURRENT) {
807   CheckDecoderFrameCurrent(GreenFirstFrameAnimatedGIFTestCase());
808 }
809 
TEST_F(ImageDecoders,AnimatedGIFWithExtraImageSubBlocks)810 TEST_F(ImageDecoders, AnimatedGIFWithExtraImageSubBlocks) {
811   ImageTestCase testCase = ExtraImageSubBlocksAnimatedGIFTestCase();
812 
813   // Verify that we can decode this test case and get two frames, even though
814   // there are extra image sub blocks between the first and second frame. The
815   // extra data shouldn't confuse the decoder or cause the decode to fail.
816 
817   // Create an image.
818   RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
819       nsDependentCString(testCase.mMimeType));
820   ASSERT_TRUE(!image->HasError());
821 
822   nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
823   ASSERT_TRUE(inputStream);
824 
825   // Figure out how much data we have.
826   uint64_t length;
827   nsresult rv = inputStream->Available(&length);
828   ASSERT_TRUE(NS_SUCCEEDED(rv));
829 
830   // Write the data into the image.
831   rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
832                                    static_cast<uint32_t>(length));
833   ASSERT_TRUE(NS_SUCCEEDED(rv));
834 
835   // Let the image know we've sent all the data.
836   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
837   ASSERT_TRUE(NS_SUCCEEDED(rv));
838 
839   RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
840   tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
841 
842   // Use GetFrame() to force a sync decode of the image.
843   RefPtr<SourceSurface> surface = image->GetFrame(
844       imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
845 
846   // Ensure that the image's metadata meets our expectations.
847   IntSize imageSize(0, 0);
848   rv = image->GetWidth(&imageSize.width);
849   EXPECT_TRUE(NS_SUCCEEDED(rv));
850   rv = image->GetHeight(&imageSize.height);
851   EXPECT_TRUE(NS_SUCCEEDED(rv));
852 
853   EXPECT_EQ(testCase.mSize.width, imageSize.width);
854   EXPECT_EQ(testCase.mSize.height, imageSize.height);
855 
856   Progress imageProgress = tracker->GetProgress();
857 
858   EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
859   EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
860 
861   // Ensure that we decoded both frames of the image.
862   LookupResult result =
863       SurfaceCache::Lookup(ImageKey(image.get()),
864                            RasterSurfaceKey(imageSize, testCase.mSurfaceFlags,
865                                             PlaybackType::eAnimated),
866                            /* aMarkUsed = */ true);
867   ASSERT_EQ(MatchType::EXACT, result.Type());
868 
869   EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
870   EXPECT_TRUE(bool(result.Surface()));
871 
872   RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
873   EXPECT_TRUE(bool(partialFrame));
874 }
875 
TEST_F(ImageDecoders,AnimatedWebPWithFRAME_FIRST)876 TEST_F(ImageDecoders, AnimatedWebPWithFRAME_FIRST) {
877   CheckDecoderFrameFirst(GreenFirstFrameAnimatedWebPTestCase());
878 }
879 
TEST_F(ImageDecoders,AnimatedWebPWithFRAME_CURRENT)880 TEST_F(ImageDecoders, AnimatedWebPWithFRAME_CURRENT) {
881   CheckDecoderFrameCurrent(GreenFirstFrameAnimatedWebPTestCase());
882 }
883 
TEST_F(ImageDecoders,TruncatedSmallGIFSingleChunk)884 TEST_F(ImageDecoders, TruncatedSmallGIFSingleChunk) {
885   CheckDecoderSingleChunk(TruncatedSmallGIFTestCase());
886 }
887 
TEST_F(ImageDecoders,LargeICOWithBMPSingleChunk)888 TEST_F(ImageDecoders, LargeICOWithBMPSingleChunk) {
889   CheckDecoderSingleChunk(LargeICOWithBMPTestCase());
890 }
891 
TEST_F(ImageDecoders,LargeICOWithBMPMultiChunk)892 TEST_F(ImageDecoders, LargeICOWithBMPMultiChunk) {
893   CheckDecoderMultiChunk(LargeICOWithBMPTestCase(), /* aChunkSize */ 64);
894 }
895 
TEST_F(ImageDecoders,LargeICOWithPNGSingleChunk)896 TEST_F(ImageDecoders, LargeICOWithPNGSingleChunk) {
897   CheckDecoderSingleChunk(LargeICOWithPNGTestCase());
898 }
899 
TEST_F(ImageDecoders,LargeICOWithPNGMultiChunk)900 TEST_F(ImageDecoders, LargeICOWithPNGMultiChunk) {
901   CheckDecoderMultiChunk(LargeICOWithPNGTestCase());
902 }
903 
TEST_F(ImageDecoders,MultipleSizesICOSingleChunk)904 TEST_F(ImageDecoders, MultipleSizesICOSingleChunk) {
905   ImageTestCase testCase = GreenMultipleSizesICOTestCase();
906 
907   // Create an image.
908   RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
909       nsDependentCString(testCase.mMimeType));
910   ASSERT_TRUE(!image->HasError());
911 
912   nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
913   ASSERT_TRUE(inputStream);
914 
915   // Figure out how much data we have.
916   uint64_t length;
917   nsresult rv = inputStream->Available(&length);
918   ASSERT_TRUE(NS_SUCCEEDED(rv));
919 
920   // Write the data into the image.
921   rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
922                                    static_cast<uint32_t>(length));
923   ASSERT_TRUE(NS_SUCCEEDED(rv));
924 
925   // Let the image know we've sent all the data.
926   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
927   ASSERT_TRUE(NS_SUCCEEDED(rv));
928 
929   RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
930   tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
931 
932   // Use GetFrame() to force a sync decode of the image.
933   RefPtr<SourceSurface> surface = image->GetFrame(
934       imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
935 
936   // Ensure that the image's metadata meets our expectations.
937   IntSize imageSize(0, 0);
938   rv = image->GetWidth(&imageSize.width);
939   EXPECT_TRUE(NS_SUCCEEDED(rv));
940   rv = image->GetHeight(&imageSize.height);
941   EXPECT_TRUE(NS_SUCCEEDED(rv));
942 
943   EXPECT_EQ(testCase.mSize.width, imageSize.width);
944   EXPECT_EQ(testCase.mSize.height, imageSize.height);
945 
946   nsTArray<IntSize> nativeSizes;
947   rv = image->GetNativeSizes(nativeSizes);
948   EXPECT_TRUE(NS_SUCCEEDED(rv));
949   ASSERT_EQ(6u, nativeSizes.Length());
950 
951   IntSize expectedSizes[] = {IntSize(16, 16),   IntSize(32, 32),
952                              IntSize(64, 64),   IntSize(128, 128),
953                              IntSize(256, 256), IntSize(256, 128)};
954 
955   for (int i = 0; i < 6; ++i) {
956     EXPECT_EQ(expectedSizes[i], nativeSizes[i]);
957   }
958 
959   RefPtr<Image> image90 =
960       ImageOps::Orient(image, Orientation(Angle::D90, Flip::Unflipped));
961   rv = image90->GetNativeSizes(nativeSizes);
962   EXPECT_TRUE(NS_SUCCEEDED(rv));
963   ASSERT_EQ(6u, nativeSizes.Length());
964 
965   for (int i = 0; i < 5; ++i) {
966     EXPECT_EQ(expectedSizes[i], nativeSizes[i]);
967   }
968   EXPECT_EQ(IntSize(128, 256), nativeSizes[5]);
969 
970   RefPtr<Image> image180 =
971       ImageOps::Orient(image, Orientation(Angle::D180, Flip::Unflipped));
972   rv = image180->GetNativeSizes(nativeSizes);
973   EXPECT_TRUE(NS_SUCCEEDED(rv));
974   ASSERT_EQ(6u, nativeSizes.Length());
975 
976   for (int i = 0; i < 6; ++i) {
977     EXPECT_EQ(expectedSizes[i], nativeSizes[i]);
978   }
979 }
980