ResponsiveImageSelector.h (7725B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_responsiveimageselector_h__ 8 #define mozilla_dom_responsiveimageselector_h__ 9 10 #include "mozilla/FunctionRef.h" 11 #include "mozilla/ServoBindingTypes.h" 12 #include "mozilla/UniquePtr.h" 13 #include "nsCycleCollectionParticipant.h" 14 #include "nsIContent.h" 15 #include "nsISupports.h" 16 #include "nsString.h" 17 18 class nsMediaQuery; 19 class nsCSSValue; 20 21 namespace mozilla::dom { 22 23 class ResponsiveImageCandidate; 24 25 class ResponsiveImageSelector { 26 friend class ResponsiveImageCandidate; 27 28 public: 29 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ResponsiveImageSelector) 30 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ResponsiveImageSelector) 31 32 explicit ResponsiveImageSelector(nsIContent* aContent); 33 explicit ResponsiveImageSelector(dom::Document* aDocument); 34 35 // Parses the raw candidates and calls into the callback for each one of them. 36 static void ParseSourceSet(const nsAString& aSrcSet, 37 FunctionRef<void(ResponsiveImageCandidate&&)>); 38 39 // NOTE ABOUT CURRENT SELECTION 40 // 41 // The best candidate is selected lazily when GetSelectedImage*() is 42 // called, or when SelectImage() is called explicitly. This result 43 // is then cached until either invalidated by further Set*() calls, 44 // or explicitly by replaced by SelectImage(aReselect = true). 45 // 46 // Because the selected image depends on external variants like 47 // viewport size and device pixel ratio, the time at which image 48 // selection occurs can affect the result. 49 50 // Given a srcset string, parse and replace current candidates (does not 51 // replace default source) 52 bool SetCandidatesFromSourceSet(const nsAString& aSrcSet, 53 nsIPrincipal* aTriggeringPrincipal = nullptr); 54 55 // Fill the source sizes from a valid sizes descriptor. Returns false if 56 // descriptor is invalid. 57 bool SetSizesFromDescriptor(const nsAString& aSizesDescriptor); 58 59 // Set the default source, treated as the least-precedence 1.0 density source. 60 void SetDefaultSource(const nsAString& aURLString, nsIPrincipal* = nullptr); 61 void SetDefaultSource(nsIURI* aURI, nsIPrincipal* = nullptr); 62 void ClearDefaultSource(); 63 64 uint32_t NumCandidates(bool aIncludeDefault = true); 65 66 // If this was created for a specific content. May be null if we were only 67 // created for a document. 68 nsIContent* Content(); 69 70 // The document we were created for, or the owner document of the content if 71 // we were created for a specific nsIContent. 72 dom::Document* Document(); 73 74 // Get the url and density for the selected best candidate. These 75 // implicitly cause an image to be selected if necessary. 76 already_AddRefed<nsIURI> GetSelectedImageURL(); 77 // Returns false if there is no selected image 78 bool GetSelectedImageURLSpec(nsAString& aResult); 79 double GetSelectedImageDensity(); 80 nsIPrincipal* GetSelectedImageTriggeringPrincipal(); 81 82 // Runs image selection now if necessary. If an image has already 83 // been choosen, takes no action unless aReselect is true. 84 // 85 // aReselect - Always re-run selection, replacing the previously 86 // choosen image. 87 // return - true if the selected image result changed. 88 bool SelectImage(bool aReselect = false); 89 90 protected: 91 virtual ~ResponsiveImageSelector(); 92 93 private: 94 // Append a candidate unless its selector is duplicated by a higher priority 95 // candidate 96 void AppendCandidateIfUnique(ResponsiveImageCandidate&& aCandidate); 97 98 // Append a default candidate with this URL if necessary. Does not check if 99 // the array already contains one, use SetDefaultSource instead. 100 void MaybeAppendDefaultCandidate(); 101 102 // Get index of selected candidate, triggering selection if necessary. 103 int GetSelectedCandidateIndex(); 104 105 // Forget currently selected candidate. (See "NOTE ABOUT CURRENT SELECTION" 106 // above.) 107 void ClearSelectedCandidate(); 108 109 // Compute a density from a Candidate width. Returns false if sizes were not 110 // specified for this selector. 111 // 112 // aContext is the presContext to use for current viewport sizing, null will 113 // use the associated content's context. 114 bool ComputeFinalWidthForCurrentViewport(double* aWidth); 115 116 nsCOMPtr<nsINode> mOwnerNode; 117 // The cached URL for default candidate. 118 nsString mDefaultSourceURL; 119 nsCOMPtr<nsIPrincipal> mDefaultSourceTriggeringPrincipal; 120 // If this array contains an eCandidateType_Default, it should be the last 121 // element, such that the Setters can preserve/replace it respectively. 122 nsTArray<ResponsiveImageCandidate> mCandidates; 123 int mSelectedCandidateIndex; 124 // The cached resolved URL for mSelectedCandidateIndex, such that we only 125 // resolve the absolute URL at selection time 126 nsCOMPtr<nsIURI> mSelectedCandidateURL; 127 128 // Servo bits. 129 UniquePtr<StyleSourceSizeList> mServoSourceSizeList; 130 }; 131 132 class ResponsiveImageCandidate { 133 public: 134 ResponsiveImageCandidate(); 135 ResponsiveImageCandidate(const ResponsiveImageCandidate&) = delete; 136 ResponsiveImageCandidate(ResponsiveImageCandidate&&) = default; 137 138 void SetURLSpec(const nsAString& aURLString); 139 void SetTriggeringPrincipal(nsIPrincipal* aPrincipal); 140 // Set this as a default-candidate. This behaves the same as density 1.0, but 141 // has a differing type such that it can be replaced by subsequent 142 // SetDefaultSource calls. 143 void SetParameterDefault(); 144 145 // Set this candidate as a by-density candidate with specified density. 146 void SetParameterAsDensity(double aDensity); 147 void SetParameterAsComputedWidth(int32_t aWidth); 148 149 void SetParameterInvalid(); 150 151 // Consume descriptors from a string defined by aIter and aIterEnd, adjusts 152 // aIter to the end of data consumed. 153 // Returns false if descriptors string is invalid, but still parses to the end 154 // of descriptors microsyntax. 155 bool ConsumeDescriptors(nsAString::const_iterator& aIter, 156 const nsAString::const_iterator& aIterEnd); 157 158 // Check if our parameter (which does not include the url) is identical 159 bool HasSameParameter(const ResponsiveImageCandidate& aOther) const; 160 161 const nsAString& URLString() const { return mURLString; } 162 nsIPrincipal* TriggeringPrincipal() const { return mTriggeringPrincipal; } 163 164 // Compute and return the density relative to a selector. 165 double Density(ResponsiveImageSelector* aSelector) const; 166 // If the width is already known. Useful when iterating over candidates to 167 // avoid having each call re-compute the width. 168 double Density(double aMatchingWidth) const; 169 170 // Append the descriptors for this candidate serialized as a string. 171 void AppendDescriptors(nsAString&) const; 172 173 bool IsValid() const { return mType != CandidateType::Invalid; } 174 175 // If this selector is computed from the selector's matching width. 176 bool IsComputedFromWidth() const { 177 return mType == CandidateType::ComputedFromWidth; 178 } 179 180 bool IsDefault() const { return mType == CandidateType::Default; } 181 182 enum class CandidateType : uint8_t { 183 Invalid, 184 Density, 185 // Treated as 1.0 density, but a separate type so we can update the 186 // responsive candidates and default separately 187 Default, 188 ComputedFromWidth 189 }; 190 191 CandidateType Type() const { return mType; } 192 193 private: 194 nsString mURLString; 195 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal; 196 CandidateType mType; 197 union { 198 double mDensity; 199 int32_t mWidth; 200 } mValue; 201 }; 202 203 } // namespace mozilla::dom 204 205 #endif // mozilla_dom_responsiveimageselector_h__