diff options
Diffstat (limited to '')
-rw-r--r-- | dom/base/ImageTracker.cpp | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/dom/base/ImageTracker.cpp b/dom/base/ImageTracker.cpp new file mode 100644 index 0000000000..d7ba03f02d --- /dev/null +++ b/dom/base/ImageTracker.cpp @@ -0,0 +1,167 @@ +/* -*- 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() : mLocking(false), mAnimating(true) {} + +ImageTracker::~ImageTracker() { SetLockingState(false); } + +nsresult ImageTracker::Add(imgIRequest* aImage) { + MOZ_ASSERT(aImage); + + nsresult rv = NS_OK; + auto entry = mImages.LookupForAdd(aImage); + 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.OrInsert([]() { return 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; +} + +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; +} + +nsresult ImageTracker::SetLockingState(bool aLocked) { + if (XRE_IsContentProcess() && + !Preferences::GetBool("image.mem.allow_locking_in_content_processes", + true)) { + return NS_OK; + } + + // If there's no change, there's nothing to do. + if (mLocking == aLocked) return NS_OK; + + // Otherwise, iterate over our images and perform the appropriate action. + for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) { + imgIRequest* image = iter.Key(); + if (aLocked) { + image->LockImage(); + } else { + image->UnlockImage(); + } + } + + // Update state. + mLocking = aLocked; + + return NS_OK; +} + +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 (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) { + imgIRequest* image = iter.Key(); + if (aAnimating) { + image->IncrementAnimationConsumers(); + } else { + image->DecrementAnimationConsumers(); + } + } + + // Update state. + mAnimating = aAnimating; +} + +void ImageTracker::RequestDiscardAll() { + for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) { + iter.Key()->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 (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) { + imgIRequest* req = iter.Key(); + 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 |