Line data Source code
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 : /*
8 : * representation of a declaration block in a CSS stylesheet, or of
9 : * a style attribute
10 : */
11 :
12 : #ifndef mozilla_DeclarationBlock_h
13 : #define mozilla_DeclarationBlock_h
14 :
15 : #include "mozilla/Atomics.h"
16 : #include "mozilla/ServoBindings.h"
17 :
18 : #include "nsCSSPropertyID.h"
19 :
20 : class nsHTMLCSSStyleSheet;
21 :
22 : namespace mozilla {
23 :
24 : namespace css {
25 : class Declaration;
26 : class Rule;
27 : } // namespace css
28 :
29 : class DeclarationBlock final
30 : {
31 11 : DeclarationBlock(const DeclarationBlock& aCopy)
32 0 : : mRaw(Servo_DeclarationBlock_Clone(aCopy.mRaw).Consume())
33 : , mImmutable(false)
34 0 : , mIsDirty(false)
35 : {
36 0 : mContainer.mRaw = 0;
37 11 : }
38 :
39 : public:
40 32 : explicit DeclarationBlock(already_AddRefed<RawServoDeclarationBlock> aRaw)
41 0 : : mRaw(aRaw)
42 : , mImmutable(false)
43 128 : , mIsDirty(false)
44 : {
45 0 : mContainer.mRaw = 0;
46 32 : }
47 :
48 26 : DeclarationBlock()
49 0 : : DeclarationBlock(Servo_DeclarationBlock_CreateEmpty().Consume())
50 0 : { }
51 :
52 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DeclarationBlock)
53 :
54 0 : already_AddRefed<DeclarationBlock> Clone() const
55 : {
56 0 : return do_AddRef(new DeclarationBlock(*this));
57 : }
58 :
59 : /**
60 : * Return whether |this| may be modified.
61 : */
62 : bool IsMutable() const { return !mImmutable; }
63 :
64 : /**
65 : * Crash in debug builds if |this| cannot be modified.
66 : */
67 3 : void AssertMutable() const
68 : {
69 0 : MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable");
70 0 : }
71 :
72 : /**
73 : * Mark this declaration as unmodifiable.
74 : */
75 0 : void SetImmutable() { mImmutable = true; }
76 :
77 : /**
78 : * Return whether |this| has been restyled after modified.
79 : */
80 4 : bool IsDirty() const { return mIsDirty; }
81 :
82 : /**
83 : * Mark this declaration as dirty.
84 : */
85 2 : void SetDirty() { mIsDirty = true; }
86 :
87 : /**
88 : * Mark this declaration as not dirty.
89 : */
90 16 : void UnsetDirty() { mIsDirty = false; }
91 :
92 : /**
93 : * Copy |this|, if necessary to ensure that it can be modified.
94 : */
95 4 : already_AddRefed<DeclarationBlock> EnsureMutable()
96 : {
97 4 : if (!IsDirty()) {
98 : // In stylo, the old DeclarationBlock is stored in element's rule node tree
99 : // directly, to avoid new values replacing the DeclarationBlock in the tree
100 : // directly, we need to copy the old one here if we haven't yet copied.
101 : // As a result the new value does not replace rule node tree until traversal
102 : // happens.
103 : //
104 : // FIXME(emilio): This is a hack for ::first-line and transitions starting
105 : // due to CSSOM changes when other transitions are already running. Try
106 : // to simplify this setup.
107 2 : return Clone();
108 : }
109 :
110 0 : if (!IsMutable()) {
111 0 : return Clone();
112 : }
113 :
114 2 : return do_AddRef(this);
115 : }
116 :
117 0 : void SetOwningRule(css::Rule* aRule)
118 : {
119 0 : MOZ_ASSERT(!mContainer.mOwningRule || !aRule,
120 : "should never overwrite one rule with another");
121 0 : mContainer.mOwningRule = aRule;
122 0 : }
123 :
124 : css::Rule* GetOwningRule() const
125 : {
126 : if (mContainer.mRaw & 0x1) {
127 : return nullptr;
128 : }
129 : return mContainer.mOwningRule;
130 : }
131 :
132 0 : void SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aHTMLCSSStyleSheet)
133 : {
134 0 : MOZ_ASSERT(!mContainer.mHTMLCSSStyleSheet || !aHTMLCSSStyleSheet,
135 : "should never overwrite one sheet with another");
136 0 : mContainer.mHTMLCSSStyleSheet = aHTMLCSSStyleSheet;
137 0 : if (aHTMLCSSStyleSheet) {
138 0 : mContainer.mRaw |= uintptr_t(1);
139 : }
140 0 : }
141 :
142 0 : nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const
143 : {
144 0 : if (!(mContainer.mRaw & 0x1)) {
145 : return nullptr;
146 : }
147 0 : auto c = mContainer;
148 0 : c.mRaw &= ~uintptr_t(1);
149 0 : return c.mHTMLCSSStyleSheet;
150 : }
151 :
152 : static already_AddRefed<DeclarationBlock>
153 : FromCssText(const nsAString& aCssText, URLExtraData* aExtraData,
154 : nsCompatibility aMode, css::Loader* aLoader);
155 :
156 110 : RawServoDeclarationBlock* Raw() const { return mRaw; }
157 : RawServoDeclarationBlock* const* RefRaw() const
158 : {
159 : static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
160 : sizeof(RawServoDeclarationBlock*),
161 : "RefPtr should just be a pointer");
162 : return reinterpret_cast<RawServoDeclarationBlock* const*>(&mRaw);
163 : }
164 :
165 : const RawServoDeclarationBlockStrong* RefRawStrong() const
166 : {
167 : static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
168 : sizeof(RawServoDeclarationBlock*),
169 : "RefPtr should just be a pointer");
170 : static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
171 : sizeof(RawServoDeclarationBlockStrong),
172 : "RawServoDeclarationBlockStrong should be the same as RefPtr");
173 46 : return reinterpret_cast<const RawServoDeclarationBlockStrong*>(&mRaw);
174 : }
175 :
176 1 : void ToString(nsAString& aResult) const
177 : {
178 0 : Servo_DeclarationBlock_GetCssText(mRaw, &aResult);
179 0 : }
180 :
181 0 : uint32_t Count() const
182 : {
183 0 : return Servo_DeclarationBlock_Count(mRaw);
184 : }
185 :
186 0 : bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
187 : {
188 0 : aReturn.Truncate();
189 0 : return Servo_DeclarationBlock_GetNthProperty(mRaw, aIndex, &aReturn);
190 : }
191 :
192 2 : void GetPropertyValue(const nsAString& aProperty, nsAString& aValue) const
193 : {
194 0 : NS_ConvertUTF16toUTF8 property(aProperty);
195 : Servo_DeclarationBlock_GetPropertyValue(mRaw, &property, &aValue);
196 : }
197 :
198 : void GetPropertyValueByID(nsCSSPropertyID aPropID, nsAString& aValue) const
199 : {
200 : Servo_DeclarationBlock_GetPropertyValueById(mRaw, aPropID, &aValue);
201 : }
202 :
203 : bool GetPropertyIsImportant(const nsAString& aProperty) const
204 : {
205 : NS_ConvertUTF16toUTF8 property(aProperty);
206 : return Servo_DeclarationBlock_GetPropertyIsImportant(mRaw, &property);
207 : }
208 :
209 : // Returns whether the property was removed.
210 : bool RemoveProperty(const nsAString& aProperty,
211 : DeclarationBlockMutationClosure aClosure = { })
212 : {
213 : AssertMutable();
214 : NS_ConvertUTF16toUTF8 property(aProperty);
215 : return Servo_DeclarationBlock_RemoveProperty(mRaw, &property, aClosure);
216 : }
217 :
218 : // Returns whether the property was removed.
219 : bool RemovePropertyByID(nsCSSPropertyID aProperty,
220 : DeclarationBlockMutationClosure aClosure = { })
221 : {
222 : AssertMutable();
223 : return Servo_DeclarationBlock_RemovePropertyById(mRaw, aProperty, aClosure);
224 : }
225 :
226 : private:
227 : ~DeclarationBlock() = default;
228 : union {
229 : // We only ever have one of these since we have an
230 : // nsHTMLCSSStyleSheet only for style attributes, and style
231 : // attributes never have an owning rule.
232 :
233 : // It's an nsHTMLCSSStyleSheet if the low bit is set.
234 :
235 : uintptr_t mRaw;
236 :
237 : // The style rule that owns this declaration. May be null.
238 : css::Rule* mOwningRule;
239 :
240 : // The nsHTMLCSSStyleSheet that is responsible for this declaration.
241 : // Only non-null for style attributes.
242 : nsHTMLCSSStyleSheet* mHTMLCSSStyleSheet;
243 : } mContainer;
244 :
245 : RefPtr<RawServoDeclarationBlock> mRaw;
246 :
247 : // set when declaration put in the rule tree;
248 : bool mImmutable;
249 :
250 : // True if this declaration has not been restyled after modified.
251 : //
252 : // Since we can clear this flag from style worker threads, we use an Atomic.
253 : //
254 : // Note that although a single DeclarationBlock can be shared between
255 : // different rule nodes (due to the style="" attribute cache), whenever a
256 : // DeclarationBlock has its mIsDirty flag set to true, we always clone it to
257 : // a unique object first. So when we clear this flag during Servo traversal,
258 : // we know that we are clearing it on a DeclarationBlock that has a single
259 : // reference, and there is no problem with another user of the same
260 : // DeclarationBlock thinking that it is not dirty.
261 : Atomic<bool, MemoryOrdering::Relaxed> mIsDirty;
262 : };
263 :
264 : } // namespace mozilla
265 :
266 : #endif // mozilla_DeclarationBlock_h
|