tor-browser

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

TestAnimationFrameBuffer.cpp (34816B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include <utility>
      7 
      8 #include "AnimationFrameBuffer.h"
      9 #include "Common.h"
     10 #include "gtest/gtest.h"
     11 
     12 using namespace mozilla;
     13 using namespace mozilla::image;
     14 
     15 static already_AddRefed<imgFrame> CreateEmptyFrame(
     16    const gfx::IntSize& aSize = gfx::IntSize(1, 1),
     17    const gfx::IntRect& aFrameRect = gfx::IntRect(0, 0, 1, 1),
     18    bool aCanRecycle = true) {
     19  RefPtr<imgFrame> frame = new imgFrame();
     20  AnimationParams animParams{aFrameRect, FrameTimeout::Forever(),
     21                             /* aFrameNum */ 1, BlendMethod::OVER,
     22                             DisposalMethod::NOT_SPECIFIED};
     23  nsresult rv =
     24      frame->InitForDecoder(aSize, mozilla::gfx::SurfaceFormat::OS_RGBA, false,
     25                            Some(animParams), aCanRecycle);
     26  EXPECT_NS_SUCCEEDED(rv);
     27  RawAccessFrameRef frameRef = frame->RawAccessRef();
     28  // Normally the blend animation filter would set the dirty rect, but since
     29  // we aren't producing an actual animation here, we need to fake it.
     30  frame->SetDirtyRect(aFrameRect);
     31  frame->Finish();
     32  return frame.forget();
     33 }
     34 
     35 static bool ReinitForRecycle(RawAccessFrameRef& aFrame) {
     36  if (!aFrame) {
     37    return false;
     38  }
     39 
     40  AnimationParams animParams{aFrame->GetRect(), FrameTimeout::Forever(),
     41                             /* aFrameNum */ 1, BlendMethod::OVER,
     42                             DisposalMethod::NOT_SPECIFIED};
     43  return NS_SUCCEEDED(aFrame->InitForDecoderRecycle(animParams));
     44 }
     45 
     46 static void PrepareForDiscardingQueue(AnimationFrameRetainedBuffer& aQueue) {
     47  ASSERT_EQ(size_t(0), aQueue.Size());
     48  ASSERT_LT(size_t(1), aQueue.Batch());
     49 
     50  AnimationFrameBuffer::InsertStatus status = aQueue.Insert(CreateEmptyFrame());
     51  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
     52 
     53  while (true) {
     54    status = aQueue.Insert(CreateEmptyFrame());
     55    bool restartDecoder = aQueue.AdvanceTo(aQueue.Size() - 1);
     56    EXPECT_FALSE(restartDecoder);
     57 
     58    if (status == AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE) {
     59      break;
     60    }
     61    EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
     62  }
     63 
     64  EXPECT_EQ(aQueue.Threshold(), aQueue.Size());
     65 }
     66 
     67 static void VerifyDiscardingQueueContents(
     68    AnimationFrameDiscardingQueue& aQueue) {
     69  auto frames = aQueue.Display();
     70  for (auto i : frames) {
     71    EXPECT_TRUE(i != nullptr);
     72  }
     73 }
     74 
     75 static void VerifyInsertInternal(AnimationFrameBuffer& aQueue,
     76                                 imgFrame* aFrame) {
     77  // Determine the frame index where we just inserted the frame.
     78  size_t frameIndex;
     79  if (aQueue.MayDiscard()) {
     80    const AnimationFrameDiscardingQueue& queue =
     81        *static_cast<AnimationFrameDiscardingQueue*>(&aQueue);
     82    frameIndex = queue.PendingInsert() == 0 ? queue.Size() - 1
     83                                            : queue.PendingInsert() - 1;
     84  } else {
     85    ASSERT_FALSE(aQueue.SizeKnown());
     86    frameIndex = aQueue.Size() - 1;
     87  }
     88 
     89  // Make sure we can get the frame from that index.
     90  RefPtr<imgFrame> frame = aQueue.Get(frameIndex, false);
     91  EXPECT_EQ(aFrame, frame.get());
     92 }
     93 
     94 static void VerifyAdvance(AnimationFrameBuffer& aQueue, size_t aExpectedFrame,
     95                          bool aExpectedRestartDecoder) {
     96  RefPtr<imgFrame> oldFrame;
     97  size_t totalRecycled;
     98  if (aQueue.IsRecycling()) {
     99    AnimationFrameRecyclingQueue& queue =
    100        *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
    101    oldFrame = queue.Get(queue.Displayed(), false);
    102    totalRecycled = queue.Recycle().size();
    103  }
    104 
    105  bool restartDecoder = aQueue.AdvanceTo(aExpectedFrame);
    106  EXPECT_EQ(aExpectedRestartDecoder, restartDecoder);
    107 
    108  if (aQueue.IsRecycling()) {
    109    const AnimationFrameRecyclingQueue& queue =
    110        *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
    111    EXPECT_FALSE(queue.Recycle().back().mDirtyRect.IsEmpty());
    112    EXPECT_TRUE(
    113        queue.Recycle().back().mDirtyRect.Contains(oldFrame->GetDirtyRect()));
    114    EXPECT_EQ(totalRecycled + 1, queue.Recycle().size());
    115    EXPECT_EQ(oldFrame.get(), queue.Recycle().back().mFrame.get());
    116  }
    117 }
    118 
    119 static void VerifyInsertAndAdvance(
    120    AnimationFrameBuffer& aQueue, size_t aExpectedFrame,
    121    AnimationFrameBuffer::InsertStatus aExpectedStatus) {
    122  // Insert the decoded frame.
    123  RefPtr<imgFrame> frame = CreateEmptyFrame();
    124  AnimationFrameBuffer::InsertStatus status =
    125      aQueue.Insert(RefPtr<imgFrame>(frame));
    126  EXPECT_EQ(aExpectedStatus, status);
    127  EXPECT_TRUE(aQueue.IsLastInsertedFrame(frame));
    128  VerifyInsertInternal(aQueue, frame);
    129 
    130  // Advance the display frame.
    131  bool expectedRestartDecoder =
    132      aExpectedStatus == AnimationFrameBuffer::InsertStatus::YIELD;
    133  VerifyAdvance(aQueue, aExpectedFrame, expectedRestartDecoder);
    134 }
    135 
    136 static void VerifyMarkComplete(
    137    AnimationFrameBuffer& aQueue, bool aExpectedContinue,
    138    const gfx::IntRect& aRefreshArea = gfx::IntRect(0, 0, 1, 1)) {
    139  if (aQueue.IsRecycling() && !aQueue.SizeKnown()) {
    140    const AnimationFrameRecyclingQueue& queue =
    141        *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
    142    EXPECT_EQ(queue.FirstFrame()->GetRect(), queue.FirstFrameRefreshArea());
    143  }
    144 
    145  bool keepDecoding = aQueue.MarkComplete(aRefreshArea);
    146  EXPECT_EQ(aExpectedContinue, keepDecoding);
    147 
    148  if (aQueue.IsRecycling()) {
    149    const AnimationFrameRecyclingQueue& queue =
    150        *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
    151    EXPECT_EQ(aRefreshArea, queue.FirstFrameRefreshArea());
    152  }
    153 }
    154 
    155 static void VerifyInsert(AnimationFrameBuffer& aQueue,
    156                         AnimationFrameBuffer::InsertStatus aExpectedStatus) {
    157  RefPtr<imgFrame> frame = CreateEmptyFrame();
    158  AnimationFrameBuffer::InsertStatus status =
    159      aQueue.Insert(RefPtr<imgFrame>(frame));
    160  EXPECT_EQ(aExpectedStatus, status);
    161  EXPECT_TRUE(aQueue.IsLastInsertedFrame(frame));
    162  VerifyInsertInternal(aQueue, frame);
    163 }
    164 
    165 static void VerifyReset(AnimationFrameBuffer& aQueue, bool aExpectedContinue,
    166                        const imgFrame* aFirstFrame) {
    167  bool keepDecoding = aQueue.Reset();
    168  EXPECT_EQ(aExpectedContinue, keepDecoding);
    169  EXPECT_EQ(aQueue.Batch() * 2, aQueue.PendingDecode());
    170  EXPECT_EQ(aFirstFrame, aQueue.Get(0, true));
    171 
    172  if (!aQueue.MayDiscard()) {
    173    const AnimationFrameRetainedBuffer& queue =
    174        *static_cast<AnimationFrameRetainedBuffer*>(&aQueue);
    175    EXPECT_EQ(aFirstFrame, queue.Frames()[0].get());
    176    EXPECT_EQ(aFirstFrame, aQueue.Get(0, false));
    177  } else {
    178    const AnimationFrameDiscardingQueue& queue =
    179        *static_cast<AnimationFrameDiscardingQueue*>(&aQueue);
    180    EXPECT_EQ(size_t(0), queue.PendingInsert());
    181    EXPECT_EQ(size_t(0), queue.Display().size());
    182    EXPECT_EQ(aFirstFrame, queue.FirstFrame());
    183    EXPECT_EQ(nullptr, aQueue.Get(0, false));
    184  }
    185 }
    186 
    187 class ImageAnimationFrameBuffer : public ::testing::Test {
    188 public:
    189  ImageAnimationFrameBuffer() {}
    190 
    191 private:
    192  AutoInitializeImageLib mInit;
    193 };
    194 
    195 TEST_F(ImageAnimationFrameBuffer, RetainedInitialState) {
    196  const size_t kThreshold = 800;
    197  const size_t kBatch = 100;
    198  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
    199 
    200  EXPECT_EQ(kThreshold, buffer.Threshold());
    201  EXPECT_EQ(kBatch, buffer.Batch());
    202  EXPECT_EQ(size_t(0), buffer.Displayed());
    203  EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
    204  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    205  EXPECT_FALSE(buffer.MayDiscard());
    206  EXPECT_FALSE(buffer.SizeKnown());
    207  EXPECT_EQ(size_t(0), buffer.Size());
    208 }
    209 
    210 TEST_F(ImageAnimationFrameBuffer, ThresholdTooSmall) {
    211  const size_t kThreshold = 0;
    212  const size_t kBatch = 10;
    213  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
    214 
    215  EXPECT_EQ(kBatch * 2 + 1, buffer.Threshold());
    216  EXPECT_EQ(kBatch, buffer.Batch());
    217  EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
    218  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    219 }
    220 
    221 TEST_F(ImageAnimationFrameBuffer, BatchTooSmall) {
    222  const size_t kThreshold = 10;
    223  const size_t kBatch = 0;
    224  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
    225 
    226  EXPECT_EQ(kThreshold, buffer.Threshold());
    227  EXPECT_EQ(size_t(1), buffer.Batch());
    228  EXPECT_EQ(size_t(2), buffer.PendingDecode());
    229  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    230 }
    231 
    232 TEST_F(ImageAnimationFrameBuffer, BatchTooBig) {
    233  const size_t kThreshold = 50;
    234  const size_t kBatch = SIZE_MAX;
    235  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
    236 
    237  // The rounding is important here (e.g. SIZE_MAX/4 * 2 != SIZE_MAX/2).
    238  EXPECT_EQ(SIZE_MAX / 4, buffer.Batch());
    239  EXPECT_EQ(buffer.Batch() * 2 + 1, buffer.Threshold());
    240  EXPECT_EQ(buffer.Batch() * 2, buffer.PendingDecode());
    241  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    242 }
    243 
    244 TEST_F(ImageAnimationFrameBuffer, FinishUnderBatchAndThreshold) {
    245  const size_t kThreshold = 30;
    246  const size_t kBatch = 10;
    247  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
    248  const auto& frames = buffer.Frames();
    249 
    250  EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
    251 
    252  RefPtr<imgFrame> firstFrame;
    253  for (size_t i = 0; i < 5; ++i) {
    254    RefPtr<imgFrame> frame = CreateEmptyFrame();
    255    auto status = buffer.Insert(RefPtr<imgFrame>(frame));
    256    EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE);
    257    EXPECT_FALSE(buffer.SizeKnown());
    258    EXPECT_EQ(buffer.Size(), i + 1);
    259 
    260    if (i == 4) {
    261      EXPECT_EQ(size_t(15), buffer.PendingDecode());
    262      bool keepDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
    263      EXPECT_FALSE(keepDecoding);
    264      EXPECT_TRUE(buffer.SizeKnown());
    265      EXPECT_EQ(size_t(0), buffer.PendingDecode());
    266      EXPECT_FALSE(buffer.HasRedecodeError());
    267    }
    268 
    269    EXPECT_FALSE(buffer.MayDiscard());
    270 
    271    imgFrame* gotFrame = buffer.Get(i, false);
    272    EXPECT_EQ(frame.get(), gotFrame);
    273    ASSERT_EQ(i + 1, frames.Length());
    274    EXPECT_EQ(frame.get(), frames[i].get());
    275 
    276    if (i == 0) {
    277      firstFrame = std::move(frame);
    278      EXPECT_EQ(size_t(0), buffer.Displayed());
    279    } else {
    280      EXPECT_EQ(i - 1, buffer.Displayed());
    281      bool restartDecoder = buffer.AdvanceTo(i);
    282      EXPECT_FALSE(restartDecoder);
    283      EXPECT_EQ(i, buffer.Displayed());
    284    }
    285 
    286    gotFrame = buffer.Get(0, false);
    287    EXPECT_EQ(firstFrame.get(), gotFrame);
    288  }
    289 
    290  // Loop again over the animation and make sure it is still all there.
    291  for (size_t i = 0; i < frames.Length(); ++i) {
    292    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    293 
    294    bool restartDecoder = buffer.AdvanceTo(i);
    295    EXPECT_FALSE(restartDecoder);
    296  }
    297 }
    298 
    299 TEST_F(ImageAnimationFrameBuffer, FinishMultipleBatchesUnderThreshold) {
    300  const size_t kThreshold = 30;
    301  const size_t kBatch = 2;
    302  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
    303  const auto& frames = buffer.Frames();
    304 
    305  EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
    306 
    307  // Add frames until it tells us to stop.
    308  AnimationFrameBuffer::InsertStatus status;
    309  do {
    310    status = buffer.Insert(CreateEmptyFrame());
    311    EXPECT_FALSE(buffer.SizeKnown());
    312    EXPECT_FALSE(buffer.MayDiscard());
    313  } while (status == AnimationFrameBuffer::InsertStatus::CONTINUE);
    314 
    315  EXPECT_EQ(size_t(0), buffer.PendingDecode());
    316  EXPECT_EQ(size_t(4), frames.Length());
    317  EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::YIELD);
    318 
    319  // Progress through the animation until it lets us decode again.
    320  bool restartDecoder = false;
    321  size_t i = 0;
    322  do {
    323    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    324    if (i > 0) {
    325      restartDecoder = buffer.AdvanceTo(i);
    326    }
    327    ++i;
    328  } while (!restartDecoder);
    329 
    330  EXPECT_EQ(size_t(2), buffer.PendingDecode());
    331  EXPECT_EQ(size_t(2), buffer.Displayed());
    332 
    333  // Add the last frame.
    334  status = buffer.Insert(CreateEmptyFrame());
    335  EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE);
    336  bool keepDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
    337  EXPECT_FALSE(keepDecoding);
    338  EXPECT_TRUE(buffer.SizeKnown());
    339  EXPECT_EQ(size_t(0), buffer.PendingDecode());
    340  EXPECT_EQ(size_t(5), frames.Length());
    341  EXPECT_FALSE(buffer.HasRedecodeError());
    342 
    343  // Finish progressing through the animation.
    344  for (; i < frames.Length(); ++i) {
    345    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    346    restartDecoder = buffer.AdvanceTo(i);
    347    EXPECT_FALSE(restartDecoder);
    348  }
    349 
    350  // Loop again over the animation and make sure it is still all there.
    351  for (i = 0; i < frames.Length(); ++i) {
    352    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    353    restartDecoder = buffer.AdvanceTo(i);
    354    EXPECT_FALSE(restartDecoder);
    355  }
    356 
    357  // Loop to the third frame and then reset the animation.
    358  for (i = 0; i < 3; ++i) {
    359    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    360    restartDecoder = buffer.AdvanceTo(i);
    361    EXPECT_FALSE(restartDecoder);
    362  }
    363 
    364  // Since we are below the threshold, we can reset the get index only.
    365  // Nothing else should have changed.
    366  restartDecoder = buffer.Reset();
    367  EXPECT_FALSE(restartDecoder);
    368  for (i = 0; i < 5; ++i) {
    369    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    370  }
    371  EXPECT_EQ(size_t(0), buffer.PendingDecode());
    372  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    373  EXPECT_EQ(size_t(0), buffer.Displayed());
    374 }
    375 
    376 TEST_F(ImageAnimationFrameBuffer, StartAfterBeginning) {
    377  const size_t kThreshold = 30;
    378  const size_t kBatch = 2;
    379  const size_t kStartFrame = 7;
    380  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, kStartFrame);
    381 
    382  EXPECT_EQ(kStartFrame, buffer.PendingAdvance());
    383 
    384  // Add frames until it tells us to stop. It should be later than before,
    385  // because it auto-advances until its displayed frame is kStartFrame.
    386  AnimationFrameBuffer::InsertStatus status;
    387  size_t i = 0;
    388  do {
    389    status = buffer.Insert(CreateEmptyFrame());
    390    EXPECT_FALSE(buffer.SizeKnown());
    391    EXPECT_FALSE(buffer.MayDiscard());
    392 
    393    if (i <= kStartFrame) {
    394      EXPECT_EQ(i, buffer.Displayed());
    395      EXPECT_EQ(kStartFrame - i, buffer.PendingAdvance());
    396    } else {
    397      EXPECT_EQ(kStartFrame, buffer.Displayed());
    398      EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    399    }
    400 
    401    i++;
    402  } while (status == AnimationFrameBuffer::InsertStatus::CONTINUE);
    403 
    404  EXPECT_EQ(size_t(0), buffer.PendingDecode());
    405  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    406  EXPECT_EQ(size_t(10), buffer.Size());
    407 }
    408 
    409 TEST_F(ImageAnimationFrameBuffer, StartAfterBeginningAndReset) {
    410  const size_t kThreshold = 30;
    411  const size_t kBatch = 2;
    412  const size_t kStartFrame = 7;
    413  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, kStartFrame);
    414 
    415  EXPECT_EQ(kStartFrame, buffer.PendingAdvance());
    416 
    417  // Add frames until it tells us to stop. It should be later than before,
    418  // because it auto-advances until its displayed frame is kStartFrame.
    419  for (size_t i = 0; i < 5; ++i) {
    420    AnimationFrameBuffer::InsertStatus status =
    421        buffer.Insert(CreateEmptyFrame());
    422    EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE);
    423    EXPECT_FALSE(buffer.SizeKnown());
    424    EXPECT_FALSE(buffer.MayDiscard());
    425    EXPECT_EQ(i, buffer.Displayed());
    426    EXPECT_EQ(kStartFrame - i, buffer.PendingAdvance());
    427  }
    428 
    429  // When we reset the animation, it goes back to the beginning. That means
    430  // we can forget about what we were told to advance to at the start. While
    431  // we have plenty of frames in our buffer, we still need one more because
    432  // in the real scenario, the decoder thread is still running and it is easier
    433  // to let it insert its last frame than to coordinate quitting earlier.
    434  buffer.Reset();
    435  EXPECT_EQ(size_t(0), buffer.Displayed());
    436  EXPECT_EQ(size_t(1), buffer.PendingDecode());
    437  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    438  EXPECT_EQ(size_t(5), buffer.Size());
    439 }
    440 
    441 static void TestDiscardingQueueLoop(AnimationFrameDiscardingQueue& aQueue,
    442                                    const imgFrame* aFirstFrame,
    443                                    size_t aThreshold, size_t aBatch,
    444                                    size_t aStartFrame) {
    445  // We should be advanced right up to the last decoded frame.
    446  EXPECT_TRUE(aQueue.MayDiscard());
    447  EXPECT_FALSE(aQueue.SizeKnown());
    448  EXPECT_EQ(aBatch, aQueue.Batch());
    449  EXPECT_EQ(aThreshold, aQueue.PendingInsert());
    450  EXPECT_EQ(aThreshold, aQueue.Size());
    451  EXPECT_EQ(aFirstFrame, aQueue.FirstFrame());
    452  EXPECT_EQ(size_t(1), aQueue.Display().size());
    453  EXPECT_EQ(size_t(3), aQueue.PendingDecode());
    454  VerifyDiscardingQueueContents(aQueue);
    455 
    456  // Make sure frames get removed as we advance.
    457  VerifyInsertAndAdvance(aQueue, 5,
    458                         AnimationFrameBuffer::InsertStatus::CONTINUE);
    459  EXPECT_EQ(size_t(1), aQueue.Display().size());
    460  VerifyInsertAndAdvance(aQueue, 6,
    461                         AnimationFrameBuffer::InsertStatus::CONTINUE);
    462  EXPECT_EQ(size_t(1), aQueue.Display().size());
    463 
    464  // We actually will yield if we are recycling instead of continuing because
    465  // the pending calculation is slightly different. We will actually request one
    466  // less frame than we have to recycle.
    467  if (aQueue.IsRecycling()) {
    468    VerifyInsertAndAdvance(aQueue, 7,
    469                           AnimationFrameBuffer::InsertStatus::YIELD);
    470  } else {
    471    VerifyInsertAndAdvance(aQueue, 7,
    472                           AnimationFrameBuffer::InsertStatus::CONTINUE);
    473  }
    474  EXPECT_EQ(size_t(1), aQueue.Display().size());
    475 
    476  // We should get throttled if we insert too much.
    477  VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::CONTINUE);
    478  EXPECT_EQ(size_t(2), aQueue.Display().size());
    479  EXPECT_EQ(size_t(1), aQueue.PendingDecode());
    480  VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::YIELD);
    481  EXPECT_EQ(size_t(3), aQueue.Display().size());
    482  EXPECT_EQ(size_t(0), aQueue.PendingDecode());
    483 
    484  // We should get restarted if we advance.
    485  VerifyAdvance(aQueue, 8, true);
    486  EXPECT_EQ(size_t(2), aQueue.PendingDecode());
    487  VerifyAdvance(aQueue, 9, false);
    488  EXPECT_EQ(size_t(2), aQueue.PendingDecode());
    489 
    490  // We should continue decoding if we completed, since we are discarding.
    491  VerifyMarkComplete(aQueue, true);
    492  EXPECT_EQ(size_t(2), aQueue.PendingDecode());
    493  EXPECT_EQ(size_t(10), aQueue.Size());
    494  EXPECT_TRUE(aQueue.SizeKnown());
    495  EXPECT_FALSE(aQueue.HasRedecodeError());
    496 
    497  // Insert the first frames of the animation.
    498  VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::CONTINUE);
    499  VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::YIELD);
    500  EXPECT_EQ(size_t(0), aQueue.PendingDecode());
    501  EXPECT_EQ(size_t(10), aQueue.Size());
    502 
    503  // Advance back at the beginning. The first frame should only match for
    504  // display purposes.
    505  VerifyAdvance(aQueue, 0, true);
    506  EXPECT_EQ(size_t(2), aQueue.PendingDecode());
    507  EXPECT_TRUE(aQueue.FirstFrame() != nullptr);
    508  EXPECT_TRUE(aQueue.Get(0, false) != nullptr);
    509  EXPECT_NE(aQueue.FirstFrame(), aQueue.Get(0, false));
    510  EXPECT_EQ(aQueue.FirstFrame(), aQueue.Get(0, true));
    511 
    512  // Reiterate one more time and make it loops back.
    513  VerifyInsertAndAdvance(aQueue, 1,
    514                         AnimationFrameBuffer::InsertStatus::CONTINUE);
    515  VerifyInsertAndAdvance(aQueue, 2, AnimationFrameBuffer::InsertStatus::YIELD);
    516  VerifyInsertAndAdvance(aQueue, 3,
    517                         AnimationFrameBuffer::InsertStatus::CONTINUE);
    518  VerifyInsertAndAdvance(aQueue, 4, AnimationFrameBuffer::InsertStatus::YIELD);
    519  VerifyInsertAndAdvance(aQueue, 5,
    520                         AnimationFrameBuffer::InsertStatus::CONTINUE);
    521  VerifyInsertAndAdvance(aQueue, 6, AnimationFrameBuffer::InsertStatus::YIELD);
    522  VerifyInsertAndAdvance(aQueue, 7,
    523                         AnimationFrameBuffer::InsertStatus::CONTINUE);
    524  VerifyInsertAndAdvance(aQueue, 8, AnimationFrameBuffer::InsertStatus::YIELD);
    525 
    526  EXPECT_EQ(size_t(10), aQueue.PendingInsert());
    527  VerifyMarkComplete(aQueue, true);
    528  EXPECT_EQ(size_t(0), aQueue.PendingInsert());
    529 
    530  VerifyInsertAndAdvance(aQueue, 9,
    531                         AnimationFrameBuffer::InsertStatus::CONTINUE);
    532  VerifyInsertAndAdvance(aQueue, 0, AnimationFrameBuffer::InsertStatus::YIELD);
    533  VerifyInsertAndAdvance(aQueue, 1,
    534                         AnimationFrameBuffer::InsertStatus::CONTINUE);
    535 }
    536 
    537 TEST_F(ImageAnimationFrameBuffer, DiscardingLoop) {
    538  const size_t kThreshold = 5;
    539  const size_t kBatch = 2;
    540  const size_t kStartFrame = 0;
    541  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    542  PrepareForDiscardingQueue(retained);
    543  const imgFrame* firstFrame = retained.Frames()[0].get();
    544  AnimationFrameDiscardingQueue buffer(std::move(retained));
    545  TestDiscardingQueueLoop(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
    546 }
    547 
    548 TEST_F(ImageAnimationFrameBuffer, RecyclingLoop) {
    549  const size_t kThreshold = 5;
    550  const size_t kBatch = 2;
    551  const size_t kStartFrame = 0;
    552  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    553  PrepareForDiscardingQueue(retained);
    554  const imgFrame* firstFrame = retained.Frames()[0].get();
    555  AnimationFrameRecyclingQueue buffer(std::move(retained));
    556 
    557  // We should not start with any recycled frames.
    558  ASSERT_TRUE(buffer.Recycle().empty());
    559 
    560  TestDiscardingQueueLoop(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
    561 
    562  // All the frames we inserted should have been recycleable.
    563  ASSERT_FALSE(buffer.Recycle().empty());
    564  while (!buffer.Recycle().empty()) {
    565    gfx::IntRect expectedRect(0, 0, 1, 1);
    566    RefPtr<imgFrame> expectedFrame = buffer.Recycle().front().mFrame;
    567    EXPECT_FALSE(expectedRect.IsEmpty());
    568    EXPECT_TRUE(expectedFrame.get() != nullptr);
    569 
    570    gfx::IntRect gotRect;
    571    RawAccessFrameRef gotFrame = buffer.RecycleFrame(gotRect);
    572    EXPECT_EQ(expectedFrame.get(), gotFrame.get());
    573    EXPECT_EQ(expectedRect, gotRect);
    574    EXPECT_TRUE(ReinitForRecycle(gotFrame));
    575  }
    576 
    577  // Trying to pull a recycled frame when we have nothing should be safe too.
    578  gfx::IntRect gotRect;
    579  RawAccessFrameRef gotFrame = buffer.RecycleFrame(gotRect);
    580  EXPECT_TRUE(gotFrame.get() == nullptr);
    581  EXPECT_FALSE(ReinitForRecycle(gotFrame));
    582 }
    583 
    584 static void TestDiscardingQueueReset(AnimationFrameDiscardingQueue& aQueue,
    585                                     const imgFrame* aFirstFrame,
    586                                     size_t aThreshold, size_t aBatch,
    587                                     size_t aStartFrame) {
    588  // We should be advanced right up to the last decoded frame.
    589  EXPECT_TRUE(aQueue.MayDiscard());
    590  EXPECT_FALSE(aQueue.SizeKnown());
    591  EXPECT_EQ(aBatch, aQueue.Batch());
    592  EXPECT_EQ(aThreshold, aQueue.PendingInsert());
    593  EXPECT_EQ(aThreshold, aQueue.Size());
    594  EXPECT_EQ(aFirstFrame, aQueue.FirstFrame());
    595  EXPECT_EQ(size_t(1), aQueue.Display().size());
    596  EXPECT_EQ(size_t(4), aQueue.PendingDecode());
    597  VerifyDiscardingQueueContents(aQueue);
    598 
    599  // Reset should clear everything except the first frame.
    600  VerifyReset(aQueue, false, aFirstFrame);
    601 }
    602 
    603 TEST_F(ImageAnimationFrameBuffer, DiscardingReset) {
    604  const size_t kThreshold = 8;
    605  const size_t kBatch = 3;
    606  const size_t kStartFrame = 0;
    607  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    608  PrepareForDiscardingQueue(retained);
    609  const imgFrame* firstFrame = retained.Frames()[0].get();
    610  AnimationFrameDiscardingQueue buffer(std::move(retained));
    611  TestDiscardingQueueReset(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
    612 }
    613 
    614 TEST_F(ImageAnimationFrameBuffer, ResetBeforeDiscardingThreshold) {
    615  const size_t kThreshold = 3;
    616  const size_t kBatch = 1;
    617  const size_t kStartFrame = 0;
    618 
    619  // Get the starting buffer to just before the point where we need to switch
    620  // to a discarding buffer, reset the animation so advancing points at the
    621  // first frame, and insert the last frame to cross the threshold.
    622  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    623  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
    624  VerifyInsertAndAdvance(retained, 1,
    625                         AnimationFrameBuffer::InsertStatus::YIELD);
    626  bool restartDecoder = retained.Reset();
    627  EXPECT_FALSE(restartDecoder);
    628  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);
    629 
    630  const imgFrame* firstFrame = retained.Frames()[0].get();
    631  EXPECT_TRUE(firstFrame != nullptr);
    632  AnimationFrameDiscardingQueue buffer(std::move(retained));
    633  const imgFrame* displayFirstFrame = buffer.Get(0, true);
    634  const imgFrame* advanceFirstFrame = buffer.Get(0, false);
    635  EXPECT_EQ(firstFrame, displayFirstFrame);
    636  EXPECT_EQ(firstFrame, advanceFirstFrame);
    637 }
    638 
    639 TEST_F(ImageAnimationFrameBuffer, DiscardingTooFewFrames) {
    640  const size_t kThreshold = 3;
    641  const size_t kBatch = 1;
    642  const size_t kStartFrame = 0;
    643 
    644  // First get us to a discarding buffer state.
    645  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    646  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
    647  VerifyInsertAndAdvance(retained, 1,
    648                         AnimationFrameBuffer::InsertStatus::YIELD);
    649  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);
    650 
    651  // Insert one more frame.
    652  AnimationFrameDiscardingQueue buffer(std::move(retained));
    653  VerifyAdvance(buffer, 2, true);
    654  VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD);
    655 
    656  // Mark it as complete.
    657  bool restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
    658  EXPECT_FALSE(restartDecoder);
    659  EXPECT_FALSE(buffer.HasRedecodeError());
    660 
    661  // Insert one fewer frame than before.
    662  VerifyAdvance(buffer, 3, true);
    663  VerifyInsertAndAdvance(buffer, 0, AnimationFrameBuffer::InsertStatus::YIELD);
    664  VerifyInsertAndAdvance(buffer, 1, AnimationFrameBuffer::InsertStatus::YIELD);
    665  VerifyInsertAndAdvance(buffer, 2, AnimationFrameBuffer::InsertStatus::YIELD);
    666 
    667  // When we mark it as complete, it should fail due to too few frames.
    668  restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
    669  EXPECT_TRUE(buffer.HasRedecodeError());
    670  EXPECT_EQ(size_t(0), buffer.PendingDecode());
    671  EXPECT_EQ(size_t(4), buffer.Size());
    672 }
    673 
    674 TEST_F(ImageAnimationFrameBuffer, DiscardingTooManyFrames) {
    675  const size_t kThreshold = 3;
    676  const size_t kBatch = 1;
    677  const size_t kStartFrame = 0;
    678 
    679  // First get us to a discarding buffer state.
    680  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    681  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
    682  VerifyInsertAndAdvance(retained, 1,
    683                         AnimationFrameBuffer::InsertStatus::YIELD);
    684  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);
    685 
    686  // Insert one more frame.
    687  AnimationFrameDiscardingQueue buffer(std::move(retained));
    688  VerifyAdvance(buffer, 2, true);
    689  VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD);
    690 
    691  // Mark it as complete.
    692  bool restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
    693  EXPECT_FALSE(restartDecoder);
    694  EXPECT_FALSE(buffer.HasRedecodeError());
    695 
    696  // Advance and insert to get us back to the end on the redecode.
    697  VerifyAdvance(buffer, 3, true);
    698  VerifyInsertAndAdvance(buffer, 0, AnimationFrameBuffer::InsertStatus::YIELD);
    699  VerifyInsertAndAdvance(buffer, 1, AnimationFrameBuffer::InsertStatus::YIELD);
    700  VerifyInsertAndAdvance(buffer, 2, AnimationFrameBuffer::InsertStatus::YIELD);
    701  VerifyInsertAndAdvance(buffer, 3, AnimationFrameBuffer::InsertStatus::YIELD);
    702 
    703  // Attempt to insert a 5th frame, it should fail.
    704  RefPtr<imgFrame> frame = CreateEmptyFrame();
    705  AnimationFrameBuffer::InsertStatus status = buffer.Insert(std::move(frame));
    706  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
    707  EXPECT_TRUE(buffer.HasRedecodeError());
    708  EXPECT_EQ(size_t(0), buffer.PendingDecode());
    709  EXPECT_EQ(size_t(4), buffer.Size());
    710 }
    711 
    712 TEST_F(ImageAnimationFrameBuffer, RecyclingReset) {
    713  const size_t kThreshold = 8;
    714  const size_t kBatch = 3;
    715  const size_t kStartFrame = 0;
    716  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    717  PrepareForDiscardingQueue(retained);
    718  const imgFrame* firstFrame = retained.Frames()[0].get();
    719  AnimationFrameRecyclingQueue buffer(std::move(retained));
    720  TestDiscardingQueueReset(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
    721 }
    722 
    723 TEST_F(ImageAnimationFrameBuffer, RecyclingResetBeforeComplete) {
    724  const size_t kThreshold = 3;
    725  const size_t kBatch = 1;
    726  const size_t kStartFrame = 0;
    727  const gfx::IntSize kImageSize(100, 100);
    728  const gfx::IntRect kImageRect(gfx::IntPoint(0, 0), kImageSize);
    729  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    730 
    731  // Get the starting buffer to just before the point where we need to switch
    732  // to a discarding buffer, reset the animation so advancing points at the
    733  // first frame, and insert the last frame to cross the threshold.
    734  RefPtr<imgFrame> frame;
    735  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
    736  AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame));
    737  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
    738 
    739  frame = CreateEmptyFrame(
    740      kImageSize, gfx::IntRect(gfx::IntPoint(10, 10), gfx::IntSize(1, 1)),
    741      false);
    742  status = retained.Insert(std::move(frame));
    743  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
    744 
    745  VerifyAdvance(retained, 1, true);
    746 
    747  frame = CreateEmptyFrame(
    748      kImageSize, gfx::IntRect(gfx::IntPoint(20, 10), gfx::IntSize(1, 1)),
    749      false);
    750  status = retained.Insert(std::move(frame));
    751  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_YIELD, status);
    752 
    753  AnimationFrameRecyclingQueue buffer(std::move(retained));
    754  bool restartDecoding = buffer.Reset();
    755  EXPECT_TRUE(restartDecoding);
    756 
    757  // None of the buffers were recyclable.
    758  EXPECT_FALSE(buffer.Recycle().empty());
    759  while (!buffer.Recycle().empty()) {
    760    gfx::IntRect recycleRect;
    761    RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
    762    EXPECT_FALSE(frameRef);
    763  }
    764 
    765  // Reinsert the first two frames as recyclable and reset again.
    766  frame = CreateEmptyFrame(kImageSize, kImageRect, true);
    767  status = buffer.Insert(std::move(frame));
    768  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
    769 
    770  frame = CreateEmptyFrame(
    771      kImageSize, gfx::IntRect(gfx::IntPoint(10, 10), gfx::IntSize(1, 1)),
    772      true);
    773  status = buffer.Insert(std::move(frame));
    774  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
    775 
    776  restartDecoding = buffer.Reset();
    777  EXPECT_TRUE(restartDecoding);
    778 
    779  // Now both buffers should have been saved and the dirty rect replaced with
    780  // the full image rect since we don't know the first frame refresh area yet.
    781  EXPECT_EQ(size_t(2), buffer.Recycle().size());
    782  for (const auto& entry : buffer.Recycle()) {
    783    EXPECT_EQ(kImageRect, entry.mDirtyRect);
    784  }
    785 }
    786 
    787 TEST_F(ImageAnimationFrameBuffer, RecyclingRect) {
    788  const size_t kThreshold = 5;
    789  const size_t kBatch = 2;
    790  const size_t kStartFrame = 0;
    791  const gfx::IntSize kImageSize(100, 100);
    792  const gfx::IntRect kImageRect(gfx::IntPoint(0, 0), kImageSize);
    793  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
    794 
    795  // Let's get to the recycling state while marking all of the frames as not
    796  // recyclable, just like AnimationFrameBuffer / the decoders would do.
    797  RefPtr<imgFrame> frame;
    798  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
    799  AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame));
    800  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
    801 
    802  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
    803  status = retained.Insert(std::move(frame));
    804  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
    805 
    806  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
    807  status = retained.Insert(std::move(frame));
    808  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
    809 
    810  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
    811  status = retained.Insert(std::move(frame));
    812  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
    813 
    814  VerifyAdvance(retained, 1, false);
    815  VerifyAdvance(retained, 2, true);
    816  VerifyAdvance(retained, 3, false);
    817 
    818  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
    819  status = retained.Insert(std::move(frame));
    820  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE, status);
    821 
    822  AnimationFrameRecyclingQueue buffer(std::move(retained));
    823 
    824  // The first frame is now the candidate for recycling. Since it was marked as
    825  // not recyclable, we should get nothing.
    826  VerifyAdvance(buffer, 4, false);
    827 
    828  gfx::IntRect recycleRect;
    829  EXPECT_FALSE(buffer.Recycle().empty());
    830  RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
    831  EXPECT_FALSE(frameRef);
    832  EXPECT_TRUE(buffer.Recycle().empty());
    833 
    834  // Insert a recyclable partial frame. Its dirty rect shouldn't matter since
    835  // the previous frame was not recyclable.
    836  frame = CreateEmptyFrame(kImageSize, gfx::IntRect(0, 0, 25, 25));
    837  status = buffer.Insert(std::move(frame));
    838  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
    839 
    840  VerifyAdvance(buffer, 5, true);
    841  EXPECT_FALSE(buffer.Recycle().empty());
    842  frameRef = buffer.RecycleFrame(recycleRect);
    843  EXPECT_FALSE(frameRef);
    844  EXPECT_TRUE(buffer.Recycle().empty());
    845 
    846  // Insert a recyclable partial frame. Its dirty rect should match the recycle
    847  // rect since it is the only frame in the buffer.
    848  frame = CreateEmptyFrame(kImageSize, gfx::IntRect(25, 0, 50, 50));
    849  status = buffer.Insert(std::move(frame));
    850  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
    851 
    852  VerifyAdvance(buffer, 6, true);
    853  EXPECT_FALSE(buffer.Recycle().empty());
    854  frameRef = buffer.RecycleFrame(recycleRect);
    855  EXPECT_TRUE(frameRef);
    856  EXPECT_TRUE(ReinitForRecycle(frameRef));
    857  EXPECT_EQ(gfx::IntRect(25, 0, 50, 50), recycleRect);
    858  EXPECT_TRUE(buffer.Recycle().empty());
    859 
    860  // Insert the last frame and mark us as complete. The next recycled frame is
    861  // producing the first frame again, so we should use the first frame refresh
    862  // area instead of its dirty rect.
    863  frame = CreateEmptyFrame(kImageSize, gfx::IntRect(10, 10, 60, 10));
    864  status = buffer.Insert(std::move(frame));
    865  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
    866 
    867  bool continueDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 75, 50));
    868  EXPECT_FALSE(continueDecoding);
    869 
    870  VerifyAdvance(buffer, 7, true);
    871  EXPECT_FALSE(buffer.Recycle().empty());
    872  frameRef = buffer.RecycleFrame(recycleRect);
    873  EXPECT_TRUE(frameRef);
    874  EXPECT_TRUE(ReinitForRecycle(frameRef));
    875  EXPECT_EQ(gfx::IntRect(0, 0, 75, 50), recycleRect);
    876  EXPECT_TRUE(buffer.Recycle().empty());
    877 
    878  // Now let's reinsert the first frame. The recycle rect should still be the
    879  // first frame refresh area instead of the dirty rect of the first frame (e.g.
    880  // the full frame).
    881  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
    882  status = buffer.Insert(std::move(frame));
    883  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
    884 
    885  VerifyAdvance(buffer, 0, true);
    886  EXPECT_FALSE(buffer.Recycle().empty());
    887  frameRef = buffer.RecycleFrame(recycleRect);
    888  EXPECT_TRUE(frameRef);
    889  EXPECT_TRUE(ReinitForRecycle(frameRef));
    890  EXPECT_EQ(gfx::IntRect(0, 0, 75, 50), recycleRect);
    891  EXPECT_TRUE(buffer.Recycle().empty());
    892 }