tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestMetadata.cpp (13405B)


      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 "Decoder.h"
      9 #include "DecoderFactory.h"
     10 #include "decoders/nsBMPDecoder.h"
     11 #include "IDecodingTask.h"
     12 #include "imgIContainer.h"
     13 #include "ImageFactory.h"
     14 #include "mozilla/gfx/2D.h"
     15 #include "nsComponentManagerUtils.h"
     16 #include "nsCOMPtr.h"
     17 #include "nsIInputStream.h"
     18 #include "mozilla/RefPtr.h"
     19 #include "nsStreamUtils.h"
     20 #include "nsString.h"
     21 #include "nsThreadUtils.h"
     22 #include "ProgressTracker.h"
     23 #include "SourceBuffer.h"
     24 
     25 using namespace mozilla;
     26 using namespace mozilla::gfx;
     27 using namespace mozilla::image;
     28 
     29 enum class BMPWithinICO { NO, YES };
     30 
     31 static void CheckMetadataFrameCount(
     32    const ImageTestCase& aTestCase,
     33    NotNull<RefPtr<SourceBuffer>>& aSourceBuffer, BMPWithinICO aBMPWithinICO) {
     34  // Create a metadata decoder.
     35  DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
     36  DecoderFlags decoderFlags =
     37      DecoderFactory::GetDefaultDecoderFlagsForType(decoderType);
     38  decoderFlags |= DecoderFlags::COUNT_FRAMES;
     39  RefPtr<image::Decoder> decoder =
     40      DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, aSourceBuffer,
     41                                                     decoderFlags);
     42  ASSERT_TRUE(decoder != nullptr);
     43  RefPtr<IDecodingTask> task =
     44      new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
     45 
     46  if (aBMPWithinICO == BMPWithinICO::YES) {
     47    static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO();
     48  }
     49 
     50  // Run the metadata decoder synchronously.
     51  task->Run();
     52 
     53  // Ensure that the metadata decoder didn't make progress it shouldn't have
     54  // (which would indicate that it decoded past the header of the image).
     55  Progress metadataProgress = decoder->TakeProgress();
     56  EXPECT_TRUE(
     57      0 == (metadataProgress &
     58            ~(FLAG_SIZE_AVAILABLE | FLAG_HAS_TRANSPARENCY | FLAG_IS_ANIMATED)));
     59 
     60  // If the test case is corrupt, assert what we can and return early.
     61  if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
     62    EXPECT_TRUE(decoder->GetDecodeDone());
     63    EXPECT_TRUE(decoder->HasError());
     64    return;
     65  }
     66 
     67  EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError());
     68 
     69  // Check that we got the expected metadata.
     70  EXPECT_TRUE(metadataProgress & FLAG_SIZE_AVAILABLE);
     71 
     72  OrientedIntSize metadataSize = decoder->Size();
     73  EXPECT_EQ(aTestCase.mSize.width, metadataSize.width);
     74  if (aBMPWithinICO == BMPWithinICO::YES) {
     75    // Half the data is considered to be part of the AND mask if embedded
     76    EXPECT_EQ(aTestCase.mSize.height / 2, metadataSize.height);
     77  } else {
     78    EXPECT_EQ(aTestCase.mSize.height, metadataSize.height);
     79  }
     80 
     81  bool expectTransparency =
     82      aBMPWithinICO == BMPWithinICO::YES
     83          ? true
     84          : bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT);
     85  EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY));
     86 
     87  EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
     88            bool(metadataProgress & FLAG_IS_ANIMATED));
     89 
     90  EXPECT_TRUE(decoder->WantsFrameCount());
     91  const auto metadata = decoder->GetImageMetadata();
     92  ASSERT_TRUE(metadata.HasFrameCount());
     93  EXPECT_EQ(aTestCase.mFrameCount, metadata.GetFrameCount());
     94 }
     95 
     96 static void CheckMetadataCommon(const ImageTestCase& aTestCase,
     97                                NotNull<RefPtr<SourceBuffer>>& aSourceBuffer,
     98                                BMPWithinICO aBMPWithinICO) {
     99  // Create a metadata decoder.
    100  DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
    101  DecoderFlags decoderFlags =
    102      DecoderFactory::GetDefaultDecoderFlagsForType(decoderType);
    103  decoderFlags |= DecoderFlags::FIRST_FRAME_ONLY;
    104  RefPtr<image::Decoder> decoder =
    105      DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, aSourceBuffer,
    106                                                     decoderFlags);
    107  ASSERT_TRUE(decoder != nullptr);
    108  RefPtr<IDecodingTask> task =
    109      new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
    110 
    111  if (aBMPWithinICO == BMPWithinICO::YES) {
    112    static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO();
    113  }
    114 
    115  // Run the metadata decoder synchronously.
    116  task->Run();
    117 
    118  // Ensure that the metadata decoder didn't make progress it shouldn't have
    119  // (which would indicate that it decoded past the header of the image).
    120  Progress metadataProgress = decoder->TakeProgress();
    121  EXPECT_TRUE(
    122      0 == (metadataProgress &
    123            ~(FLAG_SIZE_AVAILABLE | FLAG_HAS_TRANSPARENCY | FLAG_IS_ANIMATED)));
    124 
    125  // If the test case is corrupt, assert what we can and return early.
    126  if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
    127    EXPECT_TRUE(decoder->GetDecodeDone());
    128    EXPECT_TRUE(decoder->HasError());
    129    return;
    130  }
    131 
    132  EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError());
    133 
    134  // Check that we got the expected metadata.
    135  EXPECT_TRUE(metadataProgress & FLAG_SIZE_AVAILABLE);
    136 
    137  OrientedIntSize metadataSize = decoder->Size();
    138  EXPECT_EQ(aTestCase.mSize.width, metadataSize.width);
    139  if (aBMPWithinICO == BMPWithinICO::YES) {
    140    // Half the data is considered to be part of the AND mask if embedded
    141    EXPECT_EQ(aTestCase.mSize.height / 2, metadataSize.height);
    142  } else {
    143    EXPECT_EQ(aTestCase.mSize.height, metadataSize.height);
    144  }
    145 
    146  bool expectTransparency =
    147      aBMPWithinICO == BMPWithinICO::YES
    148          ? true
    149          : bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT);
    150  EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY));
    151 
    152  EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
    153            bool(metadataProgress & FLAG_IS_ANIMATED));
    154 
    155  // Create a full decoder, so we can compare the result.
    156  decoder = DecoderFactory::CreateAnonymousDecoder(
    157      decoderType, aSourceBuffer, Nothing(), DecoderFlags::FIRST_FRAME_ONLY,
    158      aTestCase.mSurfaceFlags);
    159  ASSERT_TRUE(decoder != nullptr);
    160  task =
    161      new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
    162 
    163  if (aBMPWithinICO == BMPWithinICO::YES) {
    164    static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO();
    165  }
    166 
    167  // Run the full decoder synchronously.
    168  task->Run();
    169 
    170  EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError());
    171  Progress fullProgress = decoder->TakeProgress();
    172 
    173  // If the metadata decoder set a progress bit, the full decoder should also
    174  // have set the same bit.
    175  EXPECT_EQ(fullProgress, metadataProgress | fullProgress);
    176 
    177  // The full decoder and the metadata decoder should agree on the image's size.
    178  OrientedIntSize fullSize = decoder->Size();
    179  EXPECT_EQ(metadataSize.width, fullSize.width);
    180  EXPECT_EQ(metadataSize.height, fullSize.height);
    181 
    182  // We should not discover transparency during the full decode that we didn't
    183  // discover during the metadata decode, unless the image is animated.
    184  EXPECT_TRUE(!(fullProgress & FLAG_HAS_TRANSPARENCY) ||
    185              (metadataProgress & FLAG_HAS_TRANSPARENCY) ||
    186              (fullProgress & FLAG_IS_ANIMATED));
    187 }
    188 
    189 static void CheckMetadata(const ImageTestCase& aTestCase,
    190                          BMPWithinICO aBMPWithinICO = BMPWithinICO::NO,
    191                          bool aSkipCommon = false,
    192                          bool aSkipFrameCount = false) {
    193  nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
    194  ASSERT_TRUE(inputStream != nullptr);
    195 
    196  // Figure out how much data we have.
    197  uint64_t length;
    198  nsresult rv = inputStream->Available(&length);
    199  ASSERT_NS_SUCCEEDED(rv);
    200 
    201  // Write the data into a SourceBuffer.
    202  auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
    203  sourceBuffer->ExpectLength(length);
    204  rv = sourceBuffer->AppendFromInputStream(inputStream, length);
    205  ASSERT_NS_SUCCEEDED(rv);
    206  sourceBuffer->Complete(NS_OK);
    207 
    208  if (!aSkipCommon) {
    209    CheckMetadataCommon(aTestCase, sourceBuffer, aBMPWithinICO);
    210  }
    211 
    212  if (!aSkipFrameCount) {
    213    CheckMetadataFrameCount(aTestCase, sourceBuffer, aBMPWithinICO);
    214  }
    215 }
    216 
    217 class ImageDecoderMetadata : public ::testing::Test {
    218 protected:
    219  AutoInitializeImageLib mInit;
    220 };
    221 
    222 TEST_F(ImageDecoderMetadata, TransparentAVIF) {
    223  CheckMetadata(TransparentAVIFTestCase());
    224 }
    225 
    226 TEST_F(ImageDecoderMetadata, PNG) { CheckMetadata(GreenPNGTestCase()); }
    227 TEST_F(ImageDecoderMetadata, TransparentPNG) {
    228  CheckMetadata(TransparentPNGTestCase());
    229 }
    230 TEST_F(ImageDecoderMetadata, GIF) { CheckMetadata(GreenGIFTestCase()); }
    231 TEST_F(ImageDecoderMetadata, TransparentGIF) {
    232  CheckMetadata(TransparentGIFTestCase());
    233 }
    234 TEST_F(ImageDecoderMetadata, JPG) { CheckMetadata(GreenJPGTestCase()); }
    235 TEST_F(ImageDecoderMetadata, BMP) { CheckMetadata(GreenBMPTestCase()); }
    236 TEST_F(ImageDecoderMetadata, ICO) { CheckMetadata(GreenICOTestCase()); }
    237 TEST_F(ImageDecoderMetadata, Icon) { CheckMetadata(GreenIconTestCase()); }
    238 TEST_F(ImageDecoderMetadata, WebP) { CheckMetadata(GreenWebPTestCase()); }
    239 TEST_F(ImageDecoderMetadata, AVIF) { CheckMetadata(GreenAVIFTestCase()); }
    240 
    241 #ifdef MOZ_JXL
    242 TEST_F(ImageDecoderMetadata, JXL) { CheckMetadata(GreenJXLTestCase()); }
    243 TEST_F(ImageDecoderMetadata, TransparentJXL) {
    244  CheckMetadata(TransparentJXLTestCase());
    245 }
    246 #endif
    247 
    248 TEST_F(ImageDecoderMetadata, AnimatedGIF) {
    249  CheckMetadata(GreenFirstFrameAnimatedGIFTestCase());
    250 }
    251 
    252 TEST_F(ImageDecoderMetadata, AnimatedPNG) {
    253  CheckMetadata(GreenFirstFrameAnimatedPNGTestCase());
    254 }
    255 
    256 TEST_F(ImageDecoderMetadata, AnimatedWebP) {
    257  CheckMetadata(GreenFirstFrameAnimatedWebPTestCase());
    258 }
    259 
    260 TEST_F(ImageDecoderMetadata, AnimatedAVIF) {
    261  // TODO: If we request first frame only decoding, the AVIF decoder says the
    262  // animated image is not animated. This should be fixed at some point.
    263  CheckMetadata(GreenFirstFrameAnimatedAVIFTestCase(), BMPWithinICO::NO,
    264                /* aSkipCommon */ true, /* aSkipFrameCount */ false);
    265 }
    266 
    267 TEST_F(ImageDecoderMetadata, FirstFramePaddingGIF) {
    268  CheckMetadata(FirstFramePaddingGIFTestCase());
    269 }
    270 
    271 TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPNotWithinICO) {
    272  CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_DEFAULT_FLAGS),
    273                BMPWithinICO::NO);
    274 }
    275 
    276 TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPWithinICO) {
    277  CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_IS_TRANSPARENT),
    278                BMPWithinICO::YES);
    279 }
    280 
    281 TEST_F(ImageDecoderMetadata, RLE4BMP) { CheckMetadata(RLE4BMPTestCase()); }
    282 TEST_F(ImageDecoderMetadata, RLE8BMP) { CheckMetadata(RLE8BMPTestCase()); }
    283 
    284 TEST_F(ImageDecoderMetadata, Corrupt) { CheckMetadata(CorruptTestCase()); }
    285 
    286 TEST_F(ImageDecoderMetadata, NoFrameDelayGIF) {
    287  // We skip the frame count version because we realize it is animated with a
    288  // full decode, so the test isn't consistent.
    289  CheckMetadata(NoFrameDelayGIFTestCase(), BMPWithinICO::NO,
    290                /* aSkipCommon */ false, /* aSkipFrameCount */ true);
    291 }
    292 
    293 TEST_F(ImageDecoderMetadata, NoFrameDelayGIFFullDecode) {
    294  ImageTestCase testCase = NoFrameDelayGIFTestCase();
    295 
    296  // The previous test (NoFrameDelayGIF) verifies that we *don't* detect that
    297  // this test case is animated, because it has a zero frame delay for the first
    298  // frame. This test verifies that when we do a full decode, we detect the
    299  // animation at that point and successfully decode all the frames.
    300 
    301  // Create an image.
    302  RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
    303      nsDependentCString(testCase.mMimeType));
    304  ASSERT_TRUE(!image->HasError());
    305 
    306  nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
    307  ASSERT_TRUE(inputStream != nullptr);
    308 
    309  // Figure out how much data we have.
    310  uint64_t length;
    311  nsresult rv = inputStream->Available(&length);
    312  ASSERT_NS_SUCCEEDED(rv);
    313 
    314  // Write the data into the image.
    315  rv = image->OnImageDataAvailable(nullptr, inputStream, 0,
    316                                   static_cast<uint32_t>(length));
    317  ASSERT_NS_SUCCEEDED(rv);
    318 
    319  // Let the image know we've sent all the data.
    320  rv = image->OnImageDataComplete(nullptr, NS_OK, true);
    321  ASSERT_NS_SUCCEEDED(rv);
    322 
    323  RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
    324  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
    325 
    326  // Use GetFrame() to force a sync decode of the image.
    327  RefPtr<SourceSurface> surface = image->GetFrame(
    328      imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
    329 
    330  // Ensure that the image's metadata meets our expectations.
    331  IntSize imageSize(0, 0);
    332  rv = image->GetWidth(&imageSize.width);
    333  EXPECT_NS_SUCCEEDED(rv);
    334  rv = image->GetHeight(&imageSize.height);
    335  EXPECT_NS_SUCCEEDED(rv);
    336 
    337  EXPECT_EQ(testCase.mSize.width, imageSize.width);
    338  EXPECT_EQ(testCase.mSize.height, imageSize.height);
    339 
    340  Progress imageProgress = tracker->GetProgress();
    341 
    342  EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
    343  EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
    344 
    345  // Ensure that we decoded both frames of the image.
    346  LookupResult result =
    347      SurfaceCache::Lookup(ImageKey(image.get()),
    348                           RasterSurfaceKey(imageSize, testCase.mSurfaceFlags,
    349                                            PlaybackType::eAnimated),
    350                           /* aMarkUsed = */ true);
    351  ASSERT_EQ(MatchType::EXACT, result.Type());
    352 
    353  EXPECT_NS_SUCCEEDED(result.Surface().Seek(0));
    354  EXPECT_TRUE(bool(result.Surface()));
    355 
    356  RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
    357  EXPECT_TRUE(bool(partialFrame));
    358 }