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