tor-browser

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

MultipartImage.cpp (13945B)


      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 "MultipartImage.h"
      7 
      8 #include "imgINotificationObserver.h"
      9 
     10 namespace mozilla {
     11 
     12 using gfx::IntSize;
     13 using gfx::SourceSurface;
     14 
     15 namespace image {
     16 
     17 ///////////////////////////////////////////////////////////////////////////////
     18 // Helpers
     19 ///////////////////////////////////////////////////////////////////////////////
     20 
     21 static void FinishPotentialVectorImage(Image* aImage) {
     22  if (!aImage || aImage->GetType() != imgIContainer::TYPE_VECTOR) {
     23    return;
     24  }
     25  // We only want to transition to or away from a part once it has reached
     26  // load complete status, because if we don't then the progress tracker will
     27  // send a fake load event with the lastpart bit set whenever an observer is
     28  // removed without having the load complete progress. That fake load event
     29  // with the lastpart bit set will get out of the multipart image and to the
     30  // imgRequestProxy and further, which will make it look like we got the last
     31  // part of the multipart image and confuse things. If we get here then we
     32  // are still waiting for the load event for this image but we've gotten
     33  // OnDataAvailable for the part after aImage. That means we must have
     34  // gotten OnStopRequest for aImage; OnStopRequest calls
     35  // OnImageDataComplete. For raster images that are part of a multipart
     36  // image OnImageDataComplete will synchronously fire the load event
     37  // because we synchronously perform the metadata decode in that function,
     38  // because parts of multipart images have the transient flag set. So for
     39  // raster images we are assured that the load event has happened by this
     40  // point for aImage. For vector images there is no such luck because
     41  // OnImageDataComplete needs to wait for the load event in the underlying
     42  // svg document, which we can't force to happen synchronously. So we just
     43  // send a fake load event for aImage. This shouldn't confuse anyone because
     44  // the progress tracker won't send out another load event when the real load
     45  // event comes because it's already in the progress. And nobody should be
     46  // caring about load events on the current part of multipart images since
     47  // they might be sent multiple times or might not be sent at all depending
     48  // on timing. So this should be okay.
     49 
     50  RefPtr<ProgressTracker> tracker = aImage->GetProgressTracker();
     51  if (tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE)) {
     52    Progress loadProgress =
     53        LoadCompleteProgress(/* aLastPart = */ false, /* aError = */ false,
     54                             /* aStatus = */ NS_OK);
     55    tracker->SyncNotifyProgress(loadProgress | FLAG_SIZE_AVAILABLE);
     56  }
     57 }
     58 
     59 class NextPartObserver : public IProgressObserver {
     60 public:
     61  MOZ_DECLARE_REFCOUNTED_TYPENAME(NextPartObserver)
     62  NS_INLINE_DECL_REFCOUNTING(NextPartObserver, override)
     63 
     64  explicit NextPartObserver(MultipartImage* aOwner) : mOwner(aOwner) {
     65    MOZ_ASSERT(mOwner);
     66  }
     67 
     68  void BeginObserving(Image* aImage) {
     69    MOZ_ASSERT(aImage);
     70    mImage = aImage;
     71 
     72    RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
     73    tracker->AddObserver(this);
     74  }
     75 
     76  void BlockUntilDecodedAndFinishObserving() {
     77    // Use RequestDecodeForSize() to block until our image finishes decoding.
     78    // The size is ignored because we don't pass the FLAG_HIGH_QUALITY_SCALING
     79    // flag.
     80    mImage->RequestDecodeForSize(gfx::IntSize(0, 0),
     81                                 imgIContainer::FLAG_SYNC_DECODE);
     82 
     83    // Vector images need special handling to make sure they are in a complete
     84    // state.
     85    FinishPotentialVectorImage(mImage);
     86 
     87    // RequestDecodeForSize() should've sent synchronous notifications that
     88    // would have caused us to call FinishObserving() (and null out mImage)
     89    // already. If for some reason it didn't, we should do so here.
     90    if (mImage) {
     91      FinishObserving();
     92    }
     93  }
     94 
     95  virtual void Notify(int32_t aType,
     96                      const nsIntRect* aRect = nullptr) override {
     97    if (!mImage) {
     98      // We've already finished observing the last image we were given.
     99      return;
    100    }
    101 
    102    if (aType != imgINotificationObserver::FRAME_COMPLETE) {
    103      return;
    104    }
    105 
    106    if (mImage && mImage->GetType() == imgIContainer::TYPE_VECTOR) {
    107      RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
    108      if (tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE)) {
    109        // Vector images can send frame complete during loading (any mutation in
    110        // the underlying svg doc will call OnRenderingChange which sends frame
    111        // complete). Whereas raster images can only send frame complete before
    112        // the load event if there has been something to trigger a decode, but
    113        // we do not request a decode. So we will ignore this for vector images
    114        // so as to enforce the invariant described above in
    115        // BlockUntilDecodedAndFinishObserving that the current part always has
    116        // the load complete progress.
    117        return;
    118      }
    119    }
    120 
    121    FinishObserving();
    122  }
    123 
    124  virtual void OnLoadComplete(bool aLastPart) override {
    125    if (!mImage) {
    126      // We've already finished observing the last image we were given.
    127      return;
    128    }
    129 
    130    // Retrieve the image's intrinsic size.
    131    int32_t width = 0;
    132    int32_t height = 0;
    133    mImage->GetWidth(&width);
    134    mImage->GetHeight(&height);
    135 
    136    // Request decoding at the intrinsic size.
    137    mImage->RequestDecodeForSize(IntSize(width, height),
    138                                 imgIContainer::DECODE_FLAGS_DEFAULT |
    139                                     imgIContainer::FLAG_HIGH_QUALITY_SCALING);
    140 
    141    // If there's already an error, we may never get a FRAME_COMPLETE
    142    // notification, so go ahead and notify our owner right away.
    143    RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
    144    if (tracker->GetProgress() & FLAG_HAS_ERROR) {
    145      FinishObserving();
    146      return;
    147    }
    148 
    149    if (mImage && mImage->GetType() == imgIContainer::TYPE_VECTOR &&
    150        (tracker->GetProgress() & FLAG_FRAME_COMPLETE)) {
    151      // It's a vector image that we may have already got a frame complete
    152      // before load that we ignored (see Notify above). Now that we have the
    153      // load event too, we make this part current.
    154      FinishObserving();
    155    }
    156  }
    157 
    158  // Other notifications are ignored.
    159  virtual void SetHasImage() override {}
    160  virtual bool NotificationsDeferred() const override { return false; }
    161  virtual void MarkPendingNotify() override {}
    162  virtual void ClearPendingNotify() override {}
    163 
    164 private:
    165  virtual ~NextPartObserver() {}
    166 
    167  void FinishObserving() {
    168    MOZ_ASSERT(mImage);
    169 
    170    RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
    171    tracker->RemoveObserver(this);
    172    mImage = nullptr;
    173 
    174    mOwner->FinishTransition();
    175  }
    176 
    177  MultipartImage* mOwner;
    178  RefPtr<Image> mImage;
    179 };
    180 
    181 ///////////////////////////////////////////////////////////////////////////////
    182 // Implementation
    183 ///////////////////////////////////////////////////////////////////////////////
    184 
    185 MultipartImage::MultipartImage(Image* aFirstPart)
    186    : ImageWrapper(aFirstPart), mPendingNotify(false) {
    187  mNextPartObserver = new NextPartObserver(this);
    188 }
    189 
    190 void MultipartImage::Init() {
    191  MOZ_ASSERT(NS_IsMainThread());
    192  MOZ_ASSERT(mTracker, "Should've called SetProgressTracker() by now");
    193 
    194  // Start observing the first part.
    195  RefPtr<ProgressTracker> firstPartTracker = InnerImage()->GetProgressTracker();
    196  firstPartTracker->AddObserver(this);
    197  InnerImage()->IncrementAnimationConsumers();
    198 }
    199 
    200 MultipartImage::~MultipartImage() {
    201  // Ask our ProgressTracker to drop its weak reference to us.
    202  mTracker->ResetImage();
    203 }
    204 
    205 NS_IMPL_ISUPPORTS_INHERITED0(MultipartImage, ImageWrapper)
    206 
    207 void MultipartImage::BeginTransitionToPart(Image* aNextPart) {
    208  MOZ_ASSERT(NS_IsMainThread());
    209  MOZ_ASSERT(aNextPart);
    210 
    211  if (mNextPart) {
    212    // Let the decoder catch up so we don't drop frames.
    213    mNextPartObserver->BlockUntilDecodedAndFinishObserving();
    214    MOZ_ASSERT(!mNextPart);
    215  }
    216 
    217  mNextPart = aNextPart;
    218 
    219  // Start observing the next part; we'll complete the transition when
    220  // NextPartObserver calls FinishTransition.
    221  mNextPartObserver->BeginObserving(mNextPart);
    222  mNextPart->IncrementAnimationConsumers();
    223 }
    224 
    225 static Progress FilterProgress(Progress aProgress) {
    226  // Filter out onload blocking notifications, since we don't want to block
    227  // onload for multipart images.
    228  // Filter out errors, since we don't want errors in one part to error out
    229  // the whole stream.
    230  return aProgress & ~FLAG_HAS_ERROR;
    231 }
    232 
    233 void MultipartImage::FinishTransition() {
    234  MOZ_ASSERT(NS_IsMainThread());
    235  MOZ_ASSERT(mNextPart, "Should have a next part here");
    236 
    237  RefPtr<ProgressTracker> newCurrentPartTracker =
    238      mNextPart->GetProgressTracker();
    239  if (newCurrentPartTracker->GetProgress() & FLAG_HAS_ERROR) {
    240    // This frame has an error; drop it.
    241    mNextPart = nullptr;
    242 
    243    // We still need to notify, though.
    244    mTracker->ResetForNewRequest();
    245    RefPtr<ProgressTracker> currentPartTracker =
    246        InnerImage()->GetProgressTracker();
    247    mTracker->SyncNotifyProgress(
    248        FilterProgress(currentPartTracker->GetProgress()));
    249 
    250    return;
    251  }
    252 
    253  // Vector images need special handling to make sure they are in a complete
    254  // state. (Only the first part can require this, subsequent parts have
    255  // FinishPotentialVectorImage called on them before they become the current
    256  // part.)
    257  FinishPotentialVectorImage(InnerImage());
    258 
    259  // Stop observing the current part.
    260  {
    261    RefPtr<ProgressTracker> currentPartTracker =
    262        InnerImage()->GetProgressTracker();
    263    currentPartTracker->RemoveObserver(this);
    264  }
    265 
    266  // Make the next part become the current part.
    267  mTracker->ResetForNewRequest();
    268  SetInnerImage(mNextPart);
    269  mNextPart = nullptr;
    270  newCurrentPartTracker->AddObserver(this);
    271 
    272  // Finally, send all the notifications for the new current part and send a
    273  // FRAME_UPDATE notification so that observers know to redraw.
    274  mTracker->SyncNotifyProgress(
    275      FilterProgress(newCurrentPartTracker->GetProgress()),
    276      GetMaxSizedIntRect());
    277 }
    278 
    279 already_AddRefed<imgIContainer> MultipartImage::Unwrap() {
    280  // Although we wrap another image, we don't allow callers to unwrap as. As far
    281  // as external code is concerned, MultipartImage is atomic.
    282  nsCOMPtr<imgIContainer> image = this;
    283  return image.forget();
    284 }
    285 
    286 already_AddRefed<ProgressTracker> MultipartImage::GetProgressTracker() {
    287  MOZ_ASSERT(mTracker);
    288  RefPtr<ProgressTracker> tracker = mTracker;
    289  return tracker.forget();
    290 }
    291 
    292 void MultipartImage::SetProgressTracker(ProgressTracker* aTracker) {
    293  MOZ_ASSERT(aTracker);
    294  MOZ_ASSERT(!mTracker);
    295  mTracker = aTracker;
    296 }
    297 
    298 nsresult MultipartImage::OnImageDataAvailable(nsIRequest* aRequest,
    299                                              nsIInputStream* aInStr,
    300                                              uint64_t aSourceOffset,
    301                                              uint32_t aCount) {
    302  // Note that this method is special in that we forward it to the next part if
    303  // one exists, and *not* the current part.
    304 
    305  // We may trigger notifications that will free mNextPart, so keep it alive.
    306  RefPtr<Image> nextPart = mNextPart;
    307  if (nextPart) {
    308    nextPart->OnImageDataAvailable(aRequest, aInStr, aSourceOffset, aCount);
    309  } else {
    310    InnerImage()->OnImageDataAvailable(aRequest, aInStr, aSourceOffset, aCount);
    311  }
    312 
    313  return NS_OK;
    314 }
    315 
    316 nsresult MultipartImage::OnImageDataComplete(nsIRequest* aRequest,
    317                                             nsresult aStatus, bool aLastPart) {
    318  // Note that this method is special in that we forward it to the next part if
    319  // one exists, and *not* the current part.
    320 
    321  // We may trigger notifications that will free mNextPart, so keep it alive.
    322  RefPtr<Image> nextPart = mNextPart;
    323  if (nextPart) {
    324    nextPart->OnImageDataComplete(aRequest, aStatus, aLastPart);
    325  } else {
    326    InnerImage()->OnImageDataComplete(aRequest, aStatus, aLastPart);
    327  }
    328 
    329  return NS_OK;
    330 }
    331 
    332 void MultipartImage::Notify(int32_t aType,
    333                            const nsIntRect* aRect /* = nullptr*/) {
    334  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
    335    mTracker->SyncNotifyProgress(FLAG_SIZE_AVAILABLE);
    336  } else if (aType == imgINotificationObserver::FRAME_UPDATE) {
    337    mTracker->SyncNotifyProgress(NoProgress, *aRect);
    338  } else if (aType == imgINotificationObserver::FRAME_COMPLETE) {
    339    mTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE);
    340  } else if (aType == imgINotificationObserver::LOAD_COMPLETE) {
    341    mTracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
    342  } else if (aType == imgINotificationObserver::DECODE_COMPLETE) {
    343    mTracker->SyncNotifyProgress(FLAG_DECODE_COMPLETE);
    344  } else if (aType == imgINotificationObserver::DISCARD) {
    345    mTracker->OnDiscard();
    346  } else if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
    347    mTracker->OnUnlockedDraw();
    348  } else if (aType == imgINotificationObserver::IS_ANIMATED) {
    349    mTracker->SyncNotifyProgress(FLAG_IS_ANIMATED);
    350  } else if (aType == imgINotificationObserver::HAS_TRANSPARENCY) {
    351    mTracker->SyncNotifyProgress(FLAG_HAS_TRANSPARENCY);
    352  } else {
    353    MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
    354  }
    355 }
    356 
    357 void MultipartImage::OnLoadComplete(bool aLastPart) {
    358  Progress progress = FLAG_LOAD_COMPLETE;
    359  if (aLastPart) {
    360    progress |= FLAG_LAST_PART_COMPLETE;
    361  }
    362  mTracker->SyncNotifyProgress(progress);
    363 }
    364 
    365 void MultipartImage::SetHasImage() { mTracker->OnImageAvailable(); }
    366 
    367 bool MultipartImage::NotificationsDeferred() const { return mPendingNotify; }
    368 
    369 void MultipartImage::MarkPendingNotify() { mPendingNotify = true; }
    370 
    371 void MultipartImage::ClearPendingNotify() { mPendingNotify = false; }
    372 
    373 }  // namespace image
    374 }  // namespace mozilla