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