diff options
Diffstat (limited to 'layout/style/DeclarationBlock.h')
-rw-r--r-- | layout/style/DeclarationBlock.h | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h new file mode 100644 index 0000000000..57bfd6f1bc --- /dev/null +++ b/layout/style/DeclarationBlock.h @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * representation of a declaration block in a CSS stylesheet, or of + * a style attribute + */ + +#ifndef mozilla_DeclarationBlock_h +#define mozilla_DeclarationBlock_h + +#include "mozilla/Atomics.h" +#include "mozilla/ServoBindings.h" + +#include "nsCSSPropertyID.h" +#include "nsString.h" + +class nsHTMLCSSStyleSheet; + +namespace mozilla { + +namespace css { +class Declaration; +class Rule; +} // namespace css + +class DeclarationBlock final { + DeclarationBlock(const DeclarationBlock& aCopy) + : mRaw(Servo_DeclarationBlock_Clone(aCopy.mRaw).Consume()), + mImmutable(false), + mIsDirty(false) { + mContainer.mRaw = 0; + } + + public: + explicit DeclarationBlock(already_AddRefed<RawServoDeclarationBlock> aRaw) + : mRaw(aRaw), mImmutable(false), mIsDirty(false) { + mContainer.mRaw = 0; + } + + DeclarationBlock() + : DeclarationBlock(Servo_DeclarationBlock_CreateEmpty().Consume()) {} + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DeclarationBlock) + + already_AddRefed<DeclarationBlock> Clone() const { + return do_AddRef(new DeclarationBlock(*this)); + } + + /** + * Return whether |this| may be modified. + */ + bool IsMutable() const { return !mImmutable; } + + /** + * Crash in debug builds if |this| cannot be modified. + */ + void AssertMutable() const { + MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable"); + MOZ_ASSERT(!OwnerIsReadOnly(), "User Agent sheets shouldn't be modified"); + } + + /** + * Mark this declaration as unmodifiable. + */ + void SetImmutable() { mImmutable = true; } + + /** + * Return whether |this| has been restyled after modified. + */ + bool IsDirty() const { return mIsDirty; } + + /** + * Mark this declaration as dirty. + */ + void SetDirty() { mIsDirty = true; } + + /** + * Mark this declaration as not dirty. + */ + void UnsetDirty() { mIsDirty = false; } + + /** + * Copy |this|, if necessary to ensure that it can be modified. + */ + already_AddRefed<DeclarationBlock> EnsureMutable() { + MOZ_ASSERT(!OwnerIsReadOnly()); + + if (!IsDirty()) { + // In stylo, the old DeclarationBlock is stored in element's rule node + // tree directly, to avoid new values replacing the DeclarationBlock in + // the tree directly, we need to copy the old one here if we haven't yet + // copied. As a result the new value does not replace rule node tree until + // traversal happens. + // + // FIXME(emilio, bug 1606413): This is a hack for ::first-line and + // transitions starting due to CSSOM changes when other transitions are + // already running. Try to simplify this setup, so that rule tree updates + // find the mutated declaration block properly rather than having to + // insert the cloned declaration in the tree. + return Clone(); + } + + if (!IsMutable()) { + return Clone(); + } + + return do_AddRef(this); + } + + void SetOwningRule(css::Rule* aRule) { + MOZ_ASSERT(!mContainer.mOwningRule || !aRule, + "should never overwrite one rule with another"); + mContainer.mOwningRule = aRule; + } + + css::Rule* GetOwningRule() const { + if (mContainer.mRaw & 0x1) { + return nullptr; + } + return mContainer.mOwningRule; + } + + void SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aHTMLCSSStyleSheet) { + MOZ_ASSERT(!mContainer.mHTMLCSSStyleSheet || !aHTMLCSSStyleSheet, + "should never overwrite one sheet with another"); + mContainer.mHTMLCSSStyleSheet = aHTMLCSSStyleSheet; + if (aHTMLCSSStyleSheet) { + mContainer.mRaw |= uintptr_t(1); + } + } + + nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const { + if (!(mContainer.mRaw & 0x1)) { + return nullptr; + } + auto c = mContainer; + c.mRaw &= ~uintptr_t(1); + return c.mHTMLCSSStyleSheet; + } + + bool IsReadOnly() const; + + size_t SizeofIncludingThis(MallocSizeOf); + + static already_AddRefed<DeclarationBlock> FromCssText( + const nsACString& aCssText, URLExtraData* aExtraData, + nsCompatibility aMode, css::Loader* aLoader, uint16_t aRuleType) { + RefPtr<RawServoDeclarationBlock> raw = + Servo_ParseStyleAttribute(&aCssText, aExtraData, aMode, aLoader, + aRuleType) + .Consume(); + return MakeAndAddRef<DeclarationBlock>(raw.forget()); + } + + static already_AddRefed<DeclarationBlock> FromCssText( + const nsAString& aCssText, URLExtraData* aExtraData, + nsCompatibility aMode, css::Loader* aLoader, uint16_t aRuleType) { + NS_ConvertUTF16toUTF8 value(aCssText); + return FromCssText(value, aExtraData, aMode, aLoader, aRuleType); + } + + RawServoDeclarationBlock* Raw() const { return mRaw; } + RawServoDeclarationBlock* const* RefRaw() const { + static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) == + sizeof(RawServoDeclarationBlock*), + "RefPtr should just be a pointer"); + return reinterpret_cast<RawServoDeclarationBlock* const*>(&mRaw); + } + + const StyleStrong<RawServoDeclarationBlock>* RefRawStrong() const { + static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) == + sizeof(RawServoDeclarationBlock*), + "RefPtr should just be a pointer"); + static_assert( + sizeof(RefPtr<RawServoDeclarationBlock>) == + sizeof(StyleStrong<RawServoDeclarationBlock>), + "RawServoDeclarationBlockStrong should be the same as RefPtr"); + return reinterpret_cast<const StyleStrong<RawServoDeclarationBlock>*>( + &mRaw); + } + + void ToString(nsACString& aResult) const { + Servo_DeclarationBlock_GetCssText(mRaw, &aResult); + } + + uint32_t Count() const { return Servo_DeclarationBlock_Count(mRaw); } + + bool GetNthProperty(uint32_t aIndex, nsACString& aReturn) const { + aReturn.Truncate(); + return Servo_DeclarationBlock_GetNthProperty(mRaw, aIndex, &aReturn); + } + + void GetPropertyValue(const nsACString& aProperty, nsACString& aValue) const { + Servo_DeclarationBlock_GetPropertyValue(mRaw, &aProperty, &aValue); + } + + void GetPropertyValueByID(nsCSSPropertyID aPropID, nsACString& aValue) const { + Servo_DeclarationBlock_GetPropertyValueById(mRaw, aPropID, &aValue); + } + + bool GetPropertyIsImportant(const nsACString& aProperty) const { + return Servo_DeclarationBlock_GetPropertyIsImportant(mRaw, &aProperty); + } + + // Returns whether the property was removed. + bool RemoveProperty(const nsACString& aProperty, + DeclarationBlockMutationClosure aClosure = {}) { + AssertMutable(); + return Servo_DeclarationBlock_RemoveProperty(mRaw, &aProperty, aClosure); + } + + // Returns whether the property was removed. + bool RemovePropertyByID(nsCSSPropertyID aProperty, + DeclarationBlockMutationClosure aClosure = {}) { + AssertMutable(); + return Servo_DeclarationBlock_RemovePropertyById(mRaw, aProperty, aClosure); + } + + private: + ~DeclarationBlock() = default; + + bool OwnerIsReadOnly() const; + + union { + // We only ever have one of these since we have an + // nsHTMLCSSStyleSheet only for style attributes, and style + // attributes never have an owning rule. + + // It's an nsHTMLCSSStyleSheet if the low bit is set. + + uintptr_t mRaw; + + // The style rule that owns this declaration. May be null. + css::Rule* mOwningRule; + + // The nsHTMLCSSStyleSheet that is responsible for this declaration. + // Only non-null for style attributes. + nsHTMLCSSStyleSheet* mHTMLCSSStyleSheet; + } mContainer; + + RefPtr<RawServoDeclarationBlock> mRaw; + + // set when declaration put in the rule tree; + bool mImmutable; + + // True if this declaration has not been restyled after modified. + // + // Since we can clear this flag from style worker threads, we use an Atomic. + // + // Note that although a single DeclarationBlock can be shared between + // different rule nodes (due to the style="" attribute cache), whenever a + // DeclarationBlock has its mIsDirty flag set to true, we always clone it to + // a unique object first. So when we clear this flag during Servo traversal, + // we know that we are clearing it on a DeclarationBlock that has a single + // reference, and there is no problem with another user of the same + // DeclarationBlock thinking that it is not dirty. + Atomic<bool, MemoryOrdering::Relaxed> mIsDirty; +}; + +} // namespace mozilla + +#endif // mozilla_DeclarationBlock_h |