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