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/ServoUtils.h"
17 #include "mozilla/StyleBackendType.h"
18 
19 #include "nsCSSPropertyID.h"
20 
21 class nsHTMLCSSStyleSheet;
22 
23 namespace mozilla {
24 
25 class ServoDeclarationBlock;
26 
27 namespace css {
28 class Declaration;
29 class Rule;
30 }  // namespace css
31 
32 class DeclarationBlock {
33  protected:
DeclarationBlock(StyleBackendType aType)34   explicit DeclarationBlock(StyleBackendType aType)
35       : mImmutable(false), mType(aType), mIsDirty(false) {
36     mContainer.mRaw = 0;
37   }
38 
DeclarationBlock(const DeclarationBlock & aCopy)39   DeclarationBlock(const DeclarationBlock& aCopy)
40       : DeclarationBlock(aCopy.mType) {}
41 
42  public:
43   MOZ_DECL_STYLO_METHODS(css::Declaration, ServoDeclarationBlock)
44 
45   inline MozExternalRefCountType AddRef();
46   inline MozExternalRefCountType Release();
47 
48   inline already_AddRefed<DeclarationBlock> Clone() const;
49 
50   /**
51    * Return whether |this| may be modified.
52    */
IsMutable()53   bool IsMutable() const { return !mImmutable; }
54 
55   /**
56    * Crash if |this| cannot be modified.
57    */
AssertMutable()58   void AssertMutable() const {
59     MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable");
60   }
61 
62   /**
63    * Mark this declaration as unmodifiable.  It's 'const' so it can
64    * be called from ToString.
65    */
SetImmutable()66   void SetImmutable() { mImmutable = true; }
67 
68   /**
69    * Return whether |this| has been restyled after modified.
70    */
IsDirty()71   bool IsDirty() const { return mIsDirty; }
72 
73   /**
74    * Mark this declaration as dirty.
75    */
SetDirty()76   void SetDirty() { mIsDirty = true; }
77 
78   /**
79    * Mark this declaration as not dirty.
80    */
UnsetDirty()81   void UnsetDirty() { mIsDirty = false; }
82 
83   /**
84    * Copy |this|, if necessary to ensure that it can be modified.
85    */
86   inline already_AddRefed<DeclarationBlock> EnsureMutable();
87 
SetOwningRule(css::Rule * aRule)88   void SetOwningRule(css::Rule* aRule) {
89     MOZ_ASSERT(!mContainer.mOwningRule || !aRule,
90                "should never overwrite one rule with another");
91     mContainer.mOwningRule = aRule;
92   }
93 
GetOwningRule()94   css::Rule* GetOwningRule() const {
95     if (mContainer.mRaw & 0x1) {
96       return nullptr;
97     }
98     return mContainer.mOwningRule;
99   }
100 
SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet * aHTMLCSSStyleSheet)101   void SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aHTMLCSSStyleSheet) {
102     MOZ_ASSERT(!mContainer.mHTMLCSSStyleSheet || !aHTMLCSSStyleSheet,
103                "should never overwrite one sheet with another");
104     mContainer.mHTMLCSSStyleSheet = aHTMLCSSStyleSheet;
105     if (aHTMLCSSStyleSheet) {
106       mContainer.mRaw |= uintptr_t(1);
107     }
108   }
109 
GetHTMLCSSStyleSheet()110   nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const {
111     if (!(mContainer.mRaw & 0x1)) {
112       return nullptr;
113     }
114     auto c = mContainer;
115     c.mRaw &= ~uintptr_t(1);
116     return c.mHTMLCSSStyleSheet;
117   }
118 
119   inline void ToString(nsAString& aString) const;
120 
121   inline uint32_t Count() const;
122   inline bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const;
123 
124   inline void GetPropertyValue(const nsAString& aProperty,
125                                nsAString& aValue) const;
126   inline void GetPropertyValueByID(nsCSSPropertyID aPropID,
127                                    nsAString& aValue) const;
128   inline bool GetPropertyIsImportant(const nsAString& aProperty) const;
129   // Returns whether the property was removed.
130   inline bool RemoveProperty(const nsAString& aProperty);
131   // Returns whether the property was removed.
132   inline bool RemovePropertyByID(nsCSSPropertyID aProperty);
133 
134  private:
135   union {
136     // We only ever have one of these since we have an
137     // nsHTMLCSSStyleSheet only for style attributes, and style
138     // attributes never have an owning rule.
139 
140     // It's an nsHTMLCSSStyleSheet if the low bit is set.
141 
142     uintptr_t mRaw;
143 
144     // The style rule that owns this declaration.  May be null.
145     css::Rule* mOwningRule;
146 
147     // The nsHTMLCSSStyleSheet that is responsible for this declaration.
148     // Only non-null for style attributes.
149     nsHTMLCSSStyleSheet* mHTMLCSSStyleSheet;
150   } mContainer;
151 
152   // set when declaration put in the rule tree;
153   bool mImmutable;
154 
155   const StyleBackendType mType;
156 
157   // True if this declaration has not been restyled after modified.
158   //
159   // Since we can clear this flag from style worker threads, we use an Atomic.
160   //
161   // Note that although a single DeclarationBlock can be shared between
162   // different rule nodes (due to the style="" attribute cache), whenever a
163   // DeclarationBlock has its mIsDirty flag set to true, we always clone it to
164   // a unique object first. So when we clear this flag during Servo traversal,
165   // we know that we are clearing it on a DeclarationBlock that has a single
166   // reference, and there is no problem with another user of the same
167   // DeclarationBlock thinking that it is not dirty.
168   Atomic<bool, MemoryOrdering::Relaxed> mIsDirty;
169 };
170 
171 }  // namespace mozilla
172 
173 #endif  // mozilla_DeclarationBlock_h
174