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