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