summaryrefslogtreecommitdiffstats
path: root/dom/base/ImageTracker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/ImageTracker.cpp')
-rw-r--r--dom/base/ImageTracker.cpp161
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