diff options
Diffstat (limited to 'dom/base/ResponsiveImageSelector.h')
-rw-r--r-- | dom/base/ResponsiveImageSelector.h | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/dom/base/ResponsiveImageSelector.h b/dom/base/ResponsiveImageSelector.h new file mode 100644 index 0000000000..2fee92409d --- /dev/null +++ b/dom/base/ResponsiveImageSelector.h @@ -0,0 +1,204 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_responsiveimageselector_h__ +#define mozilla_dom_responsiveimageselector_h__ + +#include "mozilla/UniquePtr.h" +#include "mozilla/ServoBindingTypes.h" +#include "mozilla/FunctionRef.h" +#include "nsISupports.h" +#include "nsIContent.h" +#include "nsString.h" +#include "nsCycleCollectionParticipant.h" + +class nsMediaQuery; +class nsCSSValue; + +namespace mozilla::dom { + +class ResponsiveImageCandidate; + +class ResponsiveImageSelector { + friend class ResponsiveImageCandidate; + + public: + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ResponsiveImageSelector) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ResponsiveImageSelector) + + explicit ResponsiveImageSelector(nsIContent* aContent); + explicit ResponsiveImageSelector(dom::Document* aDocument); + + // Parses the raw candidates and calls into the callback for each one of them. + static void ParseSourceSet(const nsAString& aSrcSet, + FunctionRef<void(ResponsiveImageCandidate&&)>); + + // NOTE ABOUT CURRENT SELECTION + // + // The best candidate is selected lazily when GetSelectedImage*() is + // called, or when SelectImage() is called explicitly. This result + // is then cached until either invalidated by further Set*() calls, + // or explicitly by replaced by SelectImage(aReselect = true). + // + // Because the selected image depends on external variants like + // viewport size and device pixel ratio, the time at which image + // selection occurs can affect the result. + + // Given a srcset string, parse and replace current candidates (does not + // replace default source) + bool SetCandidatesFromSourceSet(const nsAString& aSrcSet, + nsIPrincipal* aTriggeringPrincipal = nullptr); + + // Fill the source sizes from a valid sizes descriptor. Returns false if + // descriptor is invalid. + bool SetSizesFromDescriptor(const nsAString& aSizesDescriptor); + + // Set the default source, treated as the least-precedence 1.0 density source. + void SetDefaultSource(const nsAString& aURLString, + nsIPrincipal* aPrincipal = nullptr); + + uint32_t NumCandidates(bool aIncludeDefault = true); + + // If this was created for a specific content. May be null if we were only + // created for a document. + nsIContent* Content(); + + // The document we were created for, or the owner document of the content if + // we were created for a specific nsIContent. + dom::Document* Document(); + + // Get the url and density for the selected best candidate. These + // implicitly cause an image to be selected if necessary. + already_AddRefed<nsIURI> GetSelectedImageURL(); + // Returns false if there is no selected image + bool GetSelectedImageURLSpec(nsAString& aResult); + double GetSelectedImageDensity(); + nsIPrincipal* GetSelectedImageTriggeringPrincipal(); + + // Runs image selection now if necessary. If an image has already + // been choosen, takes no action unless aReselect is true. + // + // aReselect - Always re-run selection, replacing the previously + // choosen image. + // return - true if the selected image result changed. + bool SelectImage(bool aReselect = false); + + protected: + virtual ~ResponsiveImageSelector(); + + private: + // Append a candidate unless its selector is duplicated by a higher priority + // candidate + void AppendCandidateIfUnique(ResponsiveImageCandidate&& aCandidate); + + // Append a default candidate with this URL if necessary. Does not check if + // the array already contains one, use SetDefaultSource instead. + void MaybeAppendDefaultCandidate(); + + // Get index of selected candidate, triggering selection if necessary. + int GetSelectedCandidateIndex(); + + // Forget currently selected candidate. (See "NOTE ABOUT CURRENT SELECTION" + // above.) + void ClearSelectedCandidate(); + + // Compute a density from a Candidate width. Returns false if sizes were not + // specified for this selector. + // + // aContext is the presContext to use for current viewport sizing, null will + // use the associated content's context. + bool ComputeFinalWidthForCurrentViewport(double* aWidth); + + nsCOMPtr<nsINode> mOwnerNode; + // The cached URL for default candidate. + nsString mDefaultSourceURL; + nsCOMPtr<nsIPrincipal> mDefaultSourceTriggeringPrincipal; + // If this array contains an eCandidateType_Default, it should be the last + // element, such that the Setters can preserve/replace it respectively. + nsTArray<ResponsiveImageCandidate> mCandidates; + int mSelectedCandidateIndex; + // The cached resolved URL for mSelectedCandidateIndex, such that we only + // resolve the absolute URL at selection time + nsCOMPtr<nsIURI> mSelectedCandidateURL; + + // Servo bits. + UniquePtr<StyleSourceSizeList> mServoSourceSizeList; +}; + +class ResponsiveImageCandidate { + public: + ResponsiveImageCandidate(); + ResponsiveImageCandidate(const ResponsiveImageCandidate&) = delete; + ResponsiveImageCandidate(ResponsiveImageCandidate&&) = default; + + void SetURLSpec(const nsAString& aURLString); + void SetTriggeringPrincipal(nsIPrincipal* aPrincipal); + // Set this as a default-candidate. This behaves the same as density 1.0, but + // has a differing type such that it can be replaced by subsequent + // SetDefaultSource calls. + void SetParameterDefault(); + + // Set this candidate as a by-density candidate with specified density. + void SetParameterAsDensity(double aDensity); + void SetParameterAsComputedWidth(int32_t aWidth); + + void SetParameterInvalid(); + + // Consume descriptors from a string defined by aIter and aIterEnd, adjusts + // aIter to the end of data consumed. + // Returns false if descriptors string is invalid, but still parses to the end + // of descriptors microsyntax. + bool ConsumeDescriptors(nsAString::const_iterator& aIter, + const nsAString::const_iterator& aIterEnd); + + // Check if our parameter (which does not include the url) is identical + bool HasSameParameter(const ResponsiveImageCandidate& aOther) const; + + const nsAString& URLString() const { return mURLString; } + nsIPrincipal* TriggeringPrincipal() const { return mTriggeringPrincipal; } + + // Compute and return the density relative to a selector. + double Density(ResponsiveImageSelector* aSelector) const; + // If the width is already known. Useful when iterating over candidates to + // avoid having each call re-compute the width. + double Density(double aMatchingWidth) const; + + // Append the descriptors for this candidate serialized as a string. + void AppendDescriptors(nsAString&) const; + + bool IsValid() const { return mType != CandidateType::Invalid; } + + // If this selector is computed from the selector's matching width. + bool IsComputedFromWidth() const { + return mType == CandidateType::ComputedFromWidth; + } + + bool IsDefault() const { return mType == CandidateType::Default; } + + enum class CandidateType : uint8_t { + Invalid, + Density, + // Treated as 1.0 density, but a separate type so we can update the + // responsive candidates and default separately + Default, + ComputedFromWidth + }; + + CandidateType Type() const { return mType; } + + private: + nsString mURLString; + nsCOMPtr<nsIPrincipal> mTriggeringPrincipal; + CandidateType mType; + union { + double mDensity; + int32_t mWidth; + } mValue; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_responsiveimageselector_h__ |