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