diff options
Diffstat (limited to 'dom/base/ImageTracker.cpp')
-rw-r--r-- | dom/base/ImageTracker.cpp | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/dom/base/ImageTracker.cpp b/dom/base/ImageTracker.cpp new file mode 100644 index 0000000000..42936082f6 --- /dev/null +++ b/dom/base/ImageTracker.cpp @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* table of images used in a document, for batch locking/unlocking and + * animating */ + +#include "ImageTracker.h" + +#include "imgIContainer.h" +#include "imgIRequest.h" +#include "mozilla/Preferences.h" +#include "nsXULAppAPI.h" + +namespace mozilla::dom { + +ImageTracker::ImageTracker() = default; + +ImageTracker::~ImageTracker() { SetLockingState(false); } + +nsresult ImageTracker::Add(imgIRequest* aImage) { + MOZ_ASSERT(aImage); + + const nsresult rv = mImages.WithEntryHandle(aImage, [&](auto&& entry) { + nsresult rv = NS_OK; + if (entry) { + // The image is already in the hashtable. Increment its count. + uint32_t oldCount = entry.Data(); + MOZ_ASSERT(oldCount > 0, "Entry in the image tracker with count 0!"); + entry.Data() = oldCount + 1; + } else { + // A new entry was inserted - set the count to 1. + entry.Insert(1); + + // If we're locking images, lock this image too. + if (mLocking) { + rv = aImage->LockImage(); + } + + // If we're animating images, request that this image be animated too. + if (mAnimating) { + nsresult rv2 = aImage->IncrementAnimationConsumers(); + rv = NS_SUCCEEDED(rv) ? rv2 : rv; + } + } + + return rv; + }); + + return rv; +} + +nsresult ImageTracker::Remove(imgIRequest* aImage, uint32_t aFlags) { + NS_ENSURE_ARG_POINTER(aImage); + + // Get the old count. It should exist and be > 0. + if (auto entry = mImages.Lookup(aImage)) { + MOZ_ASSERT(entry.Data() > 0, "Entry in the image tracker with count 0!"); + // If the count becomes zero, remove it from the tracker. + if (--entry.Data() == 0) { + entry.Remove(); + } else { + return NS_OK; + } + } else { + MOZ_ASSERT_UNREACHABLE("Removing image that wasn't in the tracker!"); + return NS_OK; + } + + nsresult rv = NS_OK; + + // Now that we're no longer tracking this image, unlock it if we'd + // previously locked it. + if (mLocking) { + rv = aImage->UnlockImage(); + } + + // If we're animating images, remove our request to animate this one. + if (mAnimating) { + nsresult rv2 = aImage->DecrementAnimationConsumers(); + rv = NS_SUCCEEDED(rv) ? rv2 : rv; + } + + if (aFlags & REQUEST_DISCARD) { + // Request that the image be discarded if nobody else holds a lock on it. + // Do this even if !mLocking, because even if we didn't just unlock + // this image, it might still be a candidate for discarding. + aImage->RequestDiscard(); + } + + return rv; +} + +void ImageTracker::SetLockingState(bool aLocked) { + // If there's no change, there's nothing to do. + if (mLocking == aLocked) { + return; + } + + // Otherwise, iterate over our images and perform the appropriate action. + for (imgIRequest* image : mImages.Keys()) { + if (aLocked) { + image->LockImage(); + } else { + image->UnlockImage(); + } + } + + // Update state. + mLocking = aLocked; +} + +void ImageTracker::SetAnimatingState(bool aAnimating) { + // If there's no change, there's nothing to do. + if (mAnimating == aAnimating) return; + + // Otherwise, iterate over our images and perform the appropriate action. + for (imgIRequest* image : mImages.Keys()) { + if (aAnimating) { + image->IncrementAnimationConsumers(); + } else { + image->DecrementAnimationConsumers(); + } + } + + // Update state. + mAnimating = aAnimating; +} + +void ImageTracker::RequestDiscardAll() { + for (imgIRequest* image : mImages.Keys()) { + image->RequestDiscard(); + } +} + +void ImageTracker::MediaFeatureValuesChangedAllDocuments( + const MediaFeatureChange& aChange) { + // Inform every content image used in the document that media feature values + // have changed. If the same image is used in multiple places, then we can + // end up informing them multiple times. Theme changes are rare though and we + // don't bother trying to ensure we only do this once per image. + // + // Pull the images out into an array and iterate over them, in case the + // image notifications do something that ends up modifying the table. + nsTArray<nsCOMPtr<imgIContainer>> images; + for (imgIRequest* req : mImages.Keys()) { + nsCOMPtr<imgIContainer> image; + req->GetImage(getter_AddRefs(image)); + if (!image) { + continue; + } + images.AppendElement(image->Unwrap()); + } + for (imgIContainer* image : images) { + image->MediaFeatureValuesChangedAllDocuments(aChange); + } +} + +} // namespace mozilla::dom |