1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7 * representation of a declaration block (or style attribute) in a CSS
8 * stylesheet
9 */
10
11 #ifndef mozilla_css_Declaration_h
12 #define mozilla_css_Declaration_h
13
14 // This header is in EXPORTS because it's used in several places in content/,
15 // but it's not really a public interface.
16 #ifndef MOZILLA_INTERNAL_API
17 #error "This file should only be included within libxul"
18 #endif
19
20 #include "mozilla/Attributes.h"
21 #include "mozilla/DeclarationBlock.h"
22 #include "mozilla/MemoryReporting.h"
23 #include "CSSVariableDeclarations.h"
24 #include "nsCSSDataBlock.h"
25 #include "nsCSSPropertyID.h"
26 #include "nsCSSProps.h"
27 #include "nsIStyleRule.h"
28 #include "nsStringFwd.h"
29 #include "nsTArray.h"
30 #include <stdio.h>
31
32 // feec07b8-3fe6-491e-90d5-cc93f853e048
33 #define NS_CSS_DECLARATION_IMPL_CID \
34 { 0xfeec07b8, 0x3fe6, 0x491e, \
35 { 0x90, 0xd5, 0xcc, 0x93, 0xf8, 0x53, 0xe0, 0x48 } }
36
37 class nsHTMLCSSStyleSheet;
38
39 namespace mozilla {
40 namespace css {
41
42 class Rule;
43 class Declaration;
44
45 /**
46 * ImportantStyleData is the implementation of nsIStyleRule (a source of
47 * style data) representing the style data coming from !important rules;
48 * the !important declarations need a separate nsIStyleRule object since
49 * they fit at a different point in the cascade.
50 *
51 * ImportantStyleData is allocated only as part of a Declaration object.
52 */
53 class ImportantStyleData final : public nsIStyleRule
54 {
55 public:
56
57 NS_DECL_ISUPPORTS
58
59 inline ::mozilla::css::Declaration* Declaration();
60
61 // nsIStyleRule interface
62 virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
63 virtual bool MightMapInheritedStyleData() override;
64 virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
65 nsCSSValue* aValue) override;
66 #ifdef DEBUG
67 virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
68 #endif
69
70 private:
ImportantStyleData()71 ImportantStyleData() {}
~ImportantStyleData()72 ~ImportantStyleData() {}
73
74 friend class ::mozilla::css::Declaration;
75 };
76
77 // Declaration objects have unusual lifetime rules. Every declaration
78 // begins life in an invalid state which ends when InitializeEmpty or
79 // CompressFrom is called upon it. After that, it can be attached to
80 // exactly one style rule, and will be destroyed when that style rule
81 // is destroyed. A declaration becomes immutable (via a SetImmutable
82 // call) when it is matched (put in the rule tree); after that, it must
83 // be copied before it can be modified, which is taken care of by
84 // |EnsureMutable|.
85
86 class Declaration final : public DeclarationBlock
87 , public nsIStyleRule
88 {
89 public:
90 /**
91 * Construct an |Declaration| that is in an invalid state (null
92 * |mData|) and cannot be used until its |CompressFrom| method or
93 * |InitializeEmpty| method is called.
94 */
Declaration()95 Declaration() : DeclarationBlock(StyleBackendType::Gecko) {}
96
97 Declaration(const Declaration& aCopy);
98
99 NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_DECLARATION_IMPL_CID)
100
101 NS_DECL_ISUPPORTS
102
103 private:
104 ~Declaration();
105
106 public:
107
108 // nsIStyleRule implementation
109 virtual void MapRuleInfoInto(nsRuleData *aRuleData) override;
110 virtual bool MightMapInheritedStyleData() override;
111 virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
112 nsCSSValue* aValue) override;
113 #ifdef DEBUG
114 virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
115 #endif
116
117 /**
118 * |ValueAppended| must be called to maintain this declaration's
119 * |mOrder| whenever a property is parsed into an expanded data block
120 * for this declaration. aProperty must not be a shorthand.
121 */
122 void ValueAppended(nsCSSPropertyID aProperty);
123
124 void GetPropertyValue(const nsAString& aProperty, nsAString& aValue) const;
125 void GetPropertyValueByID(nsCSSPropertyID aPropID, nsAString& aValue) const;
126 void GetAuthoredPropertyValue(const nsAString& aProperty,
127 nsAString& aValue) const;
128 bool GetPropertyIsImportant(const nsAString& aProperty) const;
129 void RemoveProperty(const nsAString& aProperty);
130 void RemovePropertyByID(nsCSSPropertyID aProperty);
131
132 bool HasProperty(nsCSSPropertyID aProperty) const;
133
HasImportantData()134 bool HasImportantData() const {
135 return mImportantData || mImportantVariables;
136 }
137
138 /**
139 * Adds a custom property declaration to this object.
140 *
141 * @param aName The variable name (i.e., without the "--" prefix).
142 * @param aType The type of value the variable has.
143 * @param aValue The value of the variable, if aType is
144 * CSSVariableDeclarations::eTokenStream.
145 * @param aIsImportant Whether the declaration is !important.
146 * @param aOverrideImportant When aIsImportant is false, whether an
147 * existing !important declaration will be overridden.
148 */
149 void AddVariable(const nsAString& aName,
150 CSSVariableDeclarations::Type aType,
151 const nsString& aValue,
152 bool aIsImportant,
153 bool aOverrideImportant);
154
155 /**
156 * Removes a custom property declaration from this object.
157 *
158 * @param aName The variable name (i.e., without the "--" prefix).
159 */
160 void RemoveVariable(const nsAString& aName);
161
162 /**
163 * Gets the string value for a custom property declaration of a variable
164 * with a given name.
165 *
166 * @param aName The variable name (i.e., without the "--" prefix).
167 * @param aValue Out parameter into which the variable's value will be
168 * stored. If the value is 'initial' or 'inherit', that exact string
169 * will be stored in aValue.
170 */
171 void GetVariableValue(const nsAString& aName, nsAString& aValue) const;
172
173 /**
174 * Returns whether the custom property declaration for a variable with
175 * the given name was !important.
176 */
177 bool GetVariableIsImportant(const nsAString& aName) const;
178
Count()179 uint32_t Count() const {
180 return mOrder.Length();
181 }
182
183 // Returns whether we actually had a property at aIndex
184 bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const;
185
186 void ToString(nsAString& aString) const;
187
GetNormalBlock()188 nsCSSCompressedDataBlock* GetNormalBlock() const { return mData; }
GetImportantBlock()189 nsCSSCompressedDataBlock* GetImportantBlock() const { return mImportantData; }
190
AssertNotExpanded()191 void AssertNotExpanded() const {
192 MOZ_ASSERT(mData, "should only be called when not expanded");
193 }
194
195 /**
196 * Initialize this declaration as holding no data. Cannot fail.
197 */
198 void InitializeEmpty();
199
200 /**
201 * Transfer all of the state from |aExpandedData| into this declaration.
202 * After calling, |aExpandedData| should be in its initial state.
203 * Callers must make sure mOrder is updated as necessary.
204 */
CompressFrom(nsCSSExpandedDataBlock * aExpandedData)205 void CompressFrom(nsCSSExpandedDataBlock *aExpandedData) {
206 MOZ_ASSERT(!mData, "oops");
207 MOZ_ASSERT(!mImportantData, "oops");
208 aExpandedData->Compress(getter_Transfers(mData),
209 getter_Transfers(mImportantData),
210 mOrder);
211 aExpandedData->AssertInitialState();
212 }
213
214 /**
215 * Transfer all of the state from this declaration into
216 * |aExpandedData| and put this declaration temporarily into an
217 * invalid state (ended by |CompressFrom| or |InitializeEmpty|) that
218 * should last only during parsing. During this time only
219 * |ValueAppended| should be called.
220 */
ExpandTo(nsCSSExpandedDataBlock * aExpandedData)221 void ExpandTo(nsCSSExpandedDataBlock *aExpandedData) {
222 AssertMutable();
223 aExpandedData->AssertInitialState();
224
225 MOZ_ASSERT(mData, "oops");
226 aExpandedData->Expand(mData.forget(), mImportantData.forget());
227 }
228
MapImportantRuleInfoInto(nsRuleData * aRuleData)229 void MapImportantRuleInfoInto(nsRuleData *aRuleData) const {
230 AssertNotExpanded();
231 MOZ_ASSERT(mImportantData || mImportantVariables,
232 "must have important data or variables");
233 if (mImportantData) {
234 mImportantData->MapRuleInfoInto(aRuleData);
235 }
236 if (mImportantVariables) {
237 mImportantVariables->MapRuleInfoInto(aRuleData);
238 }
239 }
240
241 bool MapsImportantInheritedStyleData() const;
242
243 /**
244 * Attempt to replace the value for |aProperty| stored in this
245 * declaration with the matching value from |aFromBlock|.
246 * This method may only be called on a mutable declaration.
247 * It will fail (returning false) if |aProperty| is shorthand,
248 * is not already in this declaration, or does not have the indicated
249 * importance level. If it returns true, it erases the value in
250 * |aFromBlock|. |aChanged| is set to true if the declaration
251 * changed as a result of the call, and to false otherwise.
252 */
TryReplaceValue(nsCSSPropertyID aProperty,bool aIsImportant,nsCSSExpandedDataBlock & aFromBlock,bool * aChanged)253 bool TryReplaceValue(nsCSSPropertyID aProperty, bool aIsImportant,
254 nsCSSExpandedDataBlock& aFromBlock,
255 bool* aChanged)
256 {
257 AssertMutable();
258 AssertNotExpanded();
259
260 if (nsCSSProps::IsShorthand(aProperty)) {
261 *aChanged = false;
262 return false;
263 }
264 nsCSSCompressedDataBlock *block = aIsImportant ? mImportantData : mData;
265 // mImportantData might be null
266 if (!block) {
267 *aChanged = false;
268 return false;
269 }
270
271 #ifdef DEBUG
272 {
273 nsCSSCompressedDataBlock *other = aIsImportant ? mData : mImportantData;
274 MOZ_ASSERT(!other || !other->ValueFor(aProperty) ||
275 !block->ValueFor(aProperty),
276 "Property both important and not?");
277 }
278 #endif
279 return block->TryReplaceValue(aProperty, aFromBlock, aChanged);
280 }
281
HasNonImportantValueFor(nsCSSPropertyID aProperty)282 bool HasNonImportantValueFor(nsCSSPropertyID aProperty) const {
283 MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), "must be longhand");
284 return !!mData->ValueFor(aProperty);
285 }
286
287 /**
288 * Clear the data, in preparation for its replacement with entirely
289 * new data by a call to |CompressFrom|.
290 */
ClearData()291 void ClearData() {
292 AssertMutable();
293 mData = nullptr;
294 mImportantData = nullptr;
295 mVariables = nullptr;
296 mImportantVariables = nullptr;
297 mOrder.Clear();
298 mVariableOrder.Clear();
299 }
300
GetImportantStyleData()301 ImportantStyleData* GetImportantStyleData() {
302 if (HasImportantData()) {
303 return &mImportantStyleData;
304 }
305 return nullptr;
306 }
307
308 private:
309 Declaration& operator=(const Declaration& aCopy) = delete;
310 bool operator==(const Declaration& aCopy) const = delete;
311
312 void GetPropertyValueInternal(nsCSSPropertyID aProperty, nsAString& aValue,
313 nsCSSValue::Serialization aValueSerialization)
314 const;
315 bool GetPropertyIsImportantByID(nsCSSPropertyID aProperty) const;
316
317 static void AppendImportanceToString(bool aIsImportant, nsAString& aString);
318 // return whether there was a value in |aValue| (i.e., it had a non-null unit)
319 bool AppendValueToString(nsCSSPropertyID aProperty, nsAString& aResult) const;
320 bool AppendValueToString(nsCSSPropertyID aProperty, nsAString& aResult,
321 nsCSSValue::Serialization aValueSerialization) const;
322 // Helper for ToString with strange semantics regarding aValue.
323 void AppendPropertyAndValueToString(nsCSSPropertyID aProperty,
324 nsAutoString& aValue,
325 nsAString& aResult) const;
326 // helper for ToString that serializes a custom property declaration for
327 // a variable with the specified name
328 void AppendVariableAndValueToString(const nsAString& aName,
329 nsAString& aResult) const;
330
331 void GetImageLayerValue(nsCSSCompressedDataBlock *data,
332 nsAString& aValue,
333 nsCSSValue::Serialization aSerialization,
334 const nsCSSPropertyID aTable[]) const;
335
336 void GetImageLayerPositionValue(nsCSSCompressedDataBlock *data,
337 nsAString& aValue,
338 nsCSSValue::Serialization aSerialization,
339 const nsCSSPropertyID aTable[]) const;
340
341 public:
342 /**
343 * Returns the property at the given index in the ordered list of
344 * declarations. For custom properties, eCSSPropertyExtra_variable
345 * is returned.
346 */
GetPropertyAt(uint32_t aIndex)347 nsCSSPropertyID GetPropertyAt(uint32_t aIndex) const {
348 uint32_t value = mOrder[aIndex];
349 if (value >= eCSSProperty_COUNT) {
350 return eCSSPropertyExtra_variable;
351 }
352 return nsCSSPropertyID(value);
353 }
354
355 /**
356 * Gets the name of the custom property at the given index in the ordered
357 * list of declarations.
358 */
GetCustomPropertyNameAt(uint32_t aIndex,nsAString & aResult)359 void GetCustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const {
360 MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT);
361 uint32_t variableIndex = mOrder[aIndex] - eCSSProperty_COUNT;
362 aResult.Truncate();
363 aResult.AppendLiteral("--");
364 aResult.Append(mVariableOrder[variableIndex]);
365 }
366
367 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
368
369 private:
370 // The order of properties in this declaration. Longhand properties are
371 // represented by their nsCSSPropertyID value, and each custom property (--*)
372 // is represented by a value that begins at eCSSProperty_COUNT.
373 //
374 // Subtracting eCSSProperty_COUNT from those values that represent custom
375 // properties results in an index into mVariableOrder, which identifies the
376 // specific variable the custom property declaration is for.
377 AutoTArray<uint32_t, 8> mOrder;
378
379 // variable names of custom properties found in mOrder
380 nsTArray<nsString> mVariableOrder;
381
382 // never null, except while expanded, or before the first call to
383 // InitializeEmpty or CompressFrom.
384 nsAutoPtr<nsCSSCompressedDataBlock> mData;
385
386 // may be null
387 nsAutoPtr<nsCSSCompressedDataBlock> mImportantData;
388
389 // may be null
390 nsAutoPtr<CSSVariableDeclarations> mVariables;
391
392 // may be null
393 nsAutoPtr<CSSVariableDeclarations> mImportantVariables;
394
395 friend class ImportantStyleData;
396 ImportantStyleData mImportantStyleData;
397 };
398
399 inline ::mozilla::css::Declaration*
Declaration()400 ImportantStyleData::Declaration()
401 {
402 union {
403 char* ch; /* for pointer arithmetic */
404 ::mozilla::css::Declaration* declaration;
405 ImportantStyleData* importantData;
406 } u;
407 u.importantData = this;
408 u.ch -= offsetof(::mozilla::css::Declaration, mImportantStyleData);
409 return u.declaration;
410 }
411
412 NS_DEFINE_STATIC_IID_ACCESSOR(Declaration, NS_CSS_DECLARATION_IMPL_CID)
413
414 } // namespace css
415 } // namespace mozilla
416
417 #endif /* mozilla_css_Declaration_h */
418