nsCSSPropertyIDSet.h (9446B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* bit vectors for sets of CSS properties */ 6 7 #ifndef nsCSSPropertyIDSet_h__ 8 #define nsCSSPropertyIDSet_h__ 9 10 #include <limits.h> // for CHAR_BIT 11 12 #include <initializer_list> 13 #include <ostream> 14 15 #include "mozilla/ArrayUtils.h" 16 // For COMPOSITOR_ANIMATABLE_PROPERTY_LIST and 17 // COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH 18 #include "NonCustomCSSPropertyId.h" 19 #include "mozilla/CSSPropertyId.h" 20 #include "mozilla/CompositorAnimatableProperties.h" 21 #include "nsCSSProps.h" // For operator<< for NonCustomCSSPropertyId 22 23 /** 24 * nsCSSPropertyIDSet maintains a set of non-shorthand CSS properties. In 25 * other words, for each longhand CSS property we support, it has a bit 26 * for whether that property is in the set. 27 */ 28 class nsCSSPropertyIDSet { 29 public: 30 constexpr nsCSSPropertyIDSet() : mProperties{0} {} 31 // auto-generated copy-constructor OK 32 33 explicit constexpr nsCSSPropertyIDSet( 34 std::initializer_list<NonCustomCSSPropertyId> aProperties) 35 : mProperties{0} { 36 for (auto property : aProperties) { 37 size_t p = property; 38 mProperties[p / kBitsInChunk] |= property_set_type(1) 39 << (p % kBitsInChunk); 40 } 41 } 42 43 void AssertInSetRange(NonCustomCSSPropertyId aProperty) const { 44 MOZ_DIAGNOSTIC_ASSERT(aProperty != eCSSProperty_UNKNOWN && 45 aProperty < eCSSProperty_COUNT_no_shorthands, 46 "out of bounds"); 47 } 48 49 // Conversion of aProperty to |size_t| after AssertInSetRange 50 // lets the compiler generate significantly tighter code. 51 52 void AddProperty(NonCustomCSSPropertyId aProperty) { 53 AssertInSetRange(aProperty); 54 size_t p = aProperty; 55 mProperties[p / kBitsInChunk] |= property_set_type(1) << (p % kBitsInChunk); 56 } 57 58 void RemoveProperty(NonCustomCSSPropertyId aProperty) { 59 AssertInSetRange(aProperty); 60 size_t p = aProperty; 61 mProperties[p / kBitsInChunk] &= 62 ~(property_set_type(1) << (p % kBitsInChunk)); 63 } 64 65 bool HasProperty(const mozilla::CSSPropertyId& aProperty) const { 66 return !aProperty.IsCustom() && HasProperty(aProperty.mId); 67 } 68 69 bool HasProperty(NonCustomCSSPropertyId aProperty) const { 70 AssertInSetRange(aProperty); 71 size_t p = aProperty; 72 return (mProperties[p / kBitsInChunk] & 73 (property_set_type(1) << (p % kBitsInChunk))) != 0; 74 } 75 76 // Returns an nsCSSPropertyIDSet including all properties that can be run 77 // on the compositor. 78 static constexpr nsCSSPropertyIDSet CompositorAnimatables() { 79 return nsCSSPropertyIDSet(COMPOSITOR_ANIMATABLE_PROPERTY_LIST); 80 } 81 82 static constexpr size_t CompositorAnimatableCount() { 83 return COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH; 84 } 85 86 static constexpr size_t CompositorAnimatableDisplayItemCount() { 87 // We have 3 individual transforms and 5 motion path properties, and they 88 // also use DisplayItemType::TYPE_TRANSFORM. 89 return COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH - 8; 90 } 91 92 static constexpr nsCSSPropertyIDSet CSSTransformProperties() { 93 return nsCSSPropertyIDSet{eCSSProperty_transform, eCSSProperty_translate, 94 eCSSProperty_rotate, eCSSProperty_scale}; 95 } 96 97 static constexpr nsCSSPropertyIDSet MotionPathProperties() { 98 return nsCSSPropertyIDSet{ 99 eCSSProperty_offset_path, eCSSProperty_offset_distance, 100 eCSSProperty_offset_rotate, eCSSProperty_offset_anchor, 101 eCSSProperty_offset_position}; 102 } 103 104 static constexpr nsCSSPropertyIDSet TransformLikeProperties() { 105 return nsCSSPropertyIDSet{ 106 eCSSProperty_transform, eCSSProperty_translate, 107 eCSSProperty_rotate, eCSSProperty_scale, 108 eCSSProperty_offset_path, eCSSProperty_offset_distance, 109 eCSSProperty_offset_rotate, eCSSProperty_offset_anchor, 110 eCSSProperty_offset_position}; 111 } 112 113 static constexpr nsCSSPropertyIDSet OpacityProperties() { 114 return nsCSSPropertyIDSet{eCSSProperty_opacity}; 115 } 116 117 bool Intersects(const nsCSSPropertyIDSet& aOther) const { 118 for (size_t i = 0; i < std::size(mProperties); ++i) { 119 if (mProperties[i] & aOther.mProperties[i]) { 120 return true; 121 } 122 } 123 return false; 124 } 125 126 void Empty() { memset(mProperties, 0, sizeof(mProperties)); } 127 128 void AssertIsEmpty(const char* aText) const { 129 for (size_t i = 0; i < std::size(mProperties); ++i) { 130 NS_ASSERTION(mProperties[i] == 0, aText); 131 } 132 } 133 134 bool Equals(const nsCSSPropertyIDSet& aOther) const { 135 return mozilla::ArrayEqual(mProperties, aOther.mProperties); 136 } 137 138 bool IsEmpty() const { 139 for (size_t i = 0; i < std::size(mProperties); ++i) { 140 if (mProperties[i] != 0) { 141 return false; 142 } 143 } 144 return true; 145 } 146 147 bool IsSubsetOf(const nsCSSPropertyIDSet& aOther) const { 148 return this->Intersect(aOther).Equals(*this); 149 } 150 151 // Returns a new nsCSSPropertyIDSet with all properties that are both in 152 // this set and |aOther|. 153 nsCSSPropertyIDSet Intersect(const nsCSSPropertyIDSet& aOther) const { 154 nsCSSPropertyIDSet result; 155 for (size_t i = 0; i < std::size(mProperties); ++i) { 156 result.mProperties[i] = mProperties[i] & aOther.mProperties[i]; 157 } 158 return result; 159 } 160 161 // Returns a new nsCSSPropertyIDSet with all properties that are in either 162 // this set or |aOther| but not both. 163 nsCSSPropertyIDSet Xor(const nsCSSPropertyIDSet& aOther) const { 164 nsCSSPropertyIDSet result; 165 for (size_t i = 0; i < std::size(mProperties); ++i) { 166 result.mProperties[i] = mProperties[i] ^ aOther.mProperties[i]; 167 } 168 return result; 169 } 170 171 nsCSSPropertyIDSet& operator|=(const nsCSSPropertyIDSet& aOther) { 172 for (size_t i = 0; i < std::size(mProperties); ++i) { 173 mProperties[i] |= aOther.mProperties[i]; 174 } 175 return *this; 176 } 177 178 private: 179 typedef unsigned long property_set_type; 180 181 public: 182 // number of bits in |property_set_type|. 183 static const size_t kBitsInChunk = sizeof(property_set_type) * CHAR_BIT; 184 // number of |property_set_type|s in the set 185 static const size_t kChunkCount = 186 (eCSSProperty_COUNT_no_shorthands + kBitsInChunk - 1) / kBitsInChunk; 187 188 /* 189 * For fast enumeration of all the bits that are set, callers can 190 * check each chunk against zero (since in normal cases few bits are 191 * likely to be set). 192 */ 193 bool HasPropertyInChunk(size_t aChunk) const { 194 return mProperties[aChunk] != 0; 195 } 196 bool HasPropertyAt(size_t aChunk, size_t aBit) const { 197 return (mProperties[aChunk] & (property_set_type(1) << aBit)) != 0; 198 } 199 static NonCustomCSSPropertyId CSSPropertyAt(size_t aChunk, size_t aBit) { 200 return NonCustomCSSPropertyId(aChunk * kBitsInChunk + aBit); 201 } 202 203 // Iterator for use in range-based for loops 204 class Iterator { 205 public: 206 Iterator(Iterator&& aOther) 207 : mPropertySet(aOther.mPropertySet), 208 mChunk(aOther.mChunk), 209 mBit(aOther.mBit) {} 210 211 static Iterator BeginIterator(const nsCSSPropertyIDSet& aPropertySet) { 212 Iterator result(aPropertySet); 213 214 // Search for the first property. 215 // Unsigned integer overflow is defined so the following is safe. 216 result.mBit = -1; 217 ++result; 218 219 return result; 220 } 221 222 static Iterator EndIterator(const nsCSSPropertyIDSet& aPropertySet) { 223 Iterator result(aPropertySet); 224 result.mChunk = kChunkCount; 225 result.mBit = 0; 226 return result; 227 } 228 229 bool operator!=(const Iterator& aOther) const { 230 return mChunk != aOther.mChunk || mBit != aOther.mBit; 231 } 232 233 Iterator& operator++() { 234 MOZ_ASSERT(mChunk < kChunkCount, "Should not iterate beyond end"); 235 236 do { 237 mBit++; 238 } while (mBit < kBitsInChunk && 239 !mPropertySet.HasPropertyAt(mChunk, mBit)); 240 if (mBit != kBitsInChunk) { 241 return *this; 242 } 243 244 do { 245 mChunk++; 246 } while (mChunk < kChunkCount && 247 !mPropertySet.HasPropertyInChunk(mChunk)); 248 mBit = 0; 249 if (mChunk != kChunkCount) { 250 while (mBit < kBitsInChunk && 251 !mPropertySet.HasPropertyAt(mChunk, mBit)) { 252 mBit++; 253 } 254 } 255 256 return *this; 257 } 258 259 NonCustomCSSPropertyId operator*() { 260 MOZ_ASSERT(mChunk < kChunkCount, "Should not dereference beyond end"); 261 return nsCSSPropertyIDSet::CSSPropertyAt(mChunk, mBit); 262 } 263 264 private: 265 explicit Iterator(const nsCSSPropertyIDSet& aPropertySet) 266 : mPropertySet(aPropertySet) {} 267 268 Iterator() = delete; 269 Iterator(const Iterator&) = delete; 270 Iterator& operator=(const Iterator&) = delete; 271 Iterator& operator=(const Iterator&&) = delete; 272 273 const nsCSSPropertyIDSet& mPropertySet; 274 size_t mChunk = 0; 275 size_t mBit = 0; 276 }; 277 278 Iterator begin() const { return Iterator::BeginIterator(*this); } 279 Iterator end() const { return Iterator::EndIterator(*this); } 280 281 private: 282 property_set_type mProperties[kChunkCount]; 283 }; 284 285 // MOZ_DBG support 286 287 inline std::ostream& operator<<(std::ostream& aOut, 288 const nsCSSPropertyIDSet& aPropertySet) { 289 AutoTArray<NonCustomCSSPropertyId, 16> properties; 290 for (NonCustomCSSPropertyId property : aPropertySet) { 291 properties.AppendElement(property); 292 } 293 return aOut << properties; 294 } 295 296 #endif /* !defined(nsCSSPropertyIDSet_h__) */