nsAttrValueInlines.h (8813B)
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 #ifndef nsAttrValueInlines_h__ 8 #define nsAttrValueInlines_h__ 9 10 #include <stdint.h> 11 12 #include "mozilla/Atomics.h" 13 #include "mozilla/ServoUtils.h" 14 #include "mozilla/dom/DOMString.h" 15 #include "nsAttrValue.h" 16 17 namespace mozilla { 18 class ShadowParts; 19 } 20 21 struct MiscContainer final { 22 using ValueType = nsAttrValue::ValueType; 23 24 ValueType mType; 25 // mStringBits points to either nsAtom* or mozilla::StringBuffer* and is used 26 // when mType isn't eCSSDeclaration. Note eStringBase and eAtomBase is used 27 // also to handle the type of mStringBits. 28 // 29 // Note that we use an atomic here so that we can use Compare-And-Swap 30 // to cache the serialization during the parallel servo traversal. This case 31 // (which happens when the main thread is blocked) is the only case where 32 // mStringBits is mutated off-main-thread. The Atomic needs to be 33 // ReleaseAcquire so that the pointer to the serialization does not become 34 // observable to other threads before the initialization of the pointed-to 35 // memory is also observable. 36 mozilla::Atomic<uintptr_t, mozilla::ReleaseAcquire> mStringBits; 37 union { 38 struct { 39 union { 40 int32_t mInteger; 41 nscolor mColor; 42 uint32_t mEnumValue; 43 mozilla::DeclarationBlock* mCSSDeclaration; 44 nsIURI* mURL; 45 const mozilla::AttrAtomArray* mAtomArray; 46 const mozilla::ShadowParts* mShadowParts; 47 const mozilla::SVGAnimatedIntegerPair* mSVGAnimatedIntegerPair; 48 const mozilla::SVGAnimatedLength* mSVGLength; 49 const mozilla::SVGAnimatedNumberPair* mSVGAnimatedNumberPair; 50 const mozilla::SVGAnimatedOrient* mSVGAnimatedOrient; 51 const mozilla::SVGAnimatedPreserveAspectRatio* 52 mSVGAnimatedPreserveAspectRatio; 53 const mozilla::SVGAnimatedViewBox* mSVGAnimatedViewBox; 54 const mozilla::SVGLengthList* mSVGLengthList; 55 const mozilla::SVGNumberList* mSVGNumberList; 56 const mozilla::SVGPathData* mSVGPathData; 57 const mozilla::SVGPointList* mSVGPointList; 58 const mozilla::SVGStringList* mSVGStringList; 59 const mozilla::SVGTransformList* mSVGTransformList; 60 }; 61 uint32_t mRefCount : 31; 62 uint32_t mCached : 1; 63 } mValue; 64 double mDoubleValue; 65 }; 66 67 MiscContainer() : mType(nsAttrValue::eColor), mStringBits(0) { 68 MOZ_COUNT_CTOR(MiscContainer); 69 mValue.mColor = 0; 70 mValue.mRefCount = 0; 71 mValue.mCached = 0; 72 } 73 74 protected: 75 // Only nsAttrValue should be able to delete us. 76 friend class nsAttrValue; 77 78 ~MiscContainer() { 79 if (IsRefCounted()) { 80 MOZ_ASSERT(mValue.mRefCount == 0); 81 MOZ_ASSERT(!mValue.mCached); 82 } 83 MOZ_COUNT_DTOR(MiscContainer); 84 } 85 86 public: 87 bool GetString(nsAString& aString) const; 88 89 void* GetStringOrAtomPtr(bool& aIsString) const { 90 uintptr_t bits = mStringBits; 91 aIsString = 92 nsAttrValue::ValueBaseType(mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == 93 nsAttrValue::eStringBase; 94 return reinterpret_cast<void*>(bits & NS_ATTRVALUE_POINTERVALUE_MASK); 95 } 96 97 nsAtom* GetStoredAtom() const { 98 bool isString = false; 99 void* ptr = GetStringOrAtomPtr(isString); 100 return isString ? nullptr : static_cast<nsAtom*>(ptr); 101 } 102 103 mozilla::StringBuffer* GetStoredStringBuffer() const { 104 bool isString = false; 105 void* ptr = GetStringOrAtomPtr(isString); 106 return isString ? static_cast<mozilla::StringBuffer*>(ptr) : nullptr; 107 } 108 109 void SetStringBitsMainThread(uintptr_t aBits) { 110 // mStringBits is atomic, but the callers of this function are 111 // single-threaded so they don't have to worry about it. 112 MOZ_ASSERT(!mozilla::IsInServoTraversal()); 113 MOZ_ASSERT(NS_IsMainThread()); 114 mStringBits = aBits; 115 } 116 117 inline bool IsRefCounted() const { 118 // Nothing stops us from refcounting (and sharing) other types of 119 // MiscContainer (except eDoubleValue types) but there's no compelling 120 // reason to. 121 return mType == nsAttrValue::eAtomArray || 122 mType == nsAttrValue::eCSSDeclaration || 123 mType == nsAttrValue::eShadowParts; 124 } 125 126 inline int32_t AddRef() { 127 MOZ_ASSERT(IsRefCounted()); 128 return ++mValue.mRefCount; 129 } 130 131 inline int32_t Release() { 132 MOZ_ASSERT(IsRefCounted()); 133 return --mValue.mRefCount; 134 } 135 136 void Cache(); 137 void Evict(); 138 }; 139 140 /** 141 * Implementation of inline methods 142 */ 143 144 inline int32_t nsAttrValue::GetIntegerValue() const { 145 MOZ_ASSERT(Type() == eInteger, "wrong type"); 146 return (BaseType() == eIntegerBase) ? GetIntInternal() 147 : GetMiscContainer()->mValue.mInteger; 148 } 149 150 inline int16_t nsAttrValue::GetEnumValue() const { 151 MOZ_ASSERT(Type() == eEnum, "wrong type"); 152 // We don't need to worry about sign extension here since we're 153 // returning an int16_t which will cut away the top bits. 154 return static_cast<int16_t>(((BaseType() == eIntegerBase) 155 ? static_cast<uint32_t>(GetIntInternal()) 156 : GetMiscContainer()->mValue.mEnumValue) >> 157 NS_ATTRVALUE_ENUMTABLEINDEX_BITS); 158 } 159 160 inline double nsAttrValue::GetPercentValue() const { 161 MOZ_ASSERT(Type() == ePercent, "wrong type"); 162 if (BaseType() == eIntegerBase) { 163 return GetIntInternal() / 100.0f; 164 } 165 return GetMiscContainer()->mDoubleValue / 100.0f; 166 } 167 168 inline const mozilla::AttrAtomArray* nsAttrValue::GetAtomArrayValue() const { 169 MOZ_ASSERT(Type() == eAtomArray, "wrong type"); 170 return GetMiscContainer()->mValue.mAtomArray; 171 } 172 173 inline mozilla::DeclarationBlock* nsAttrValue::GetCSSDeclarationValue() const { 174 MOZ_ASSERT(Type() == eCSSDeclaration, "wrong type"); 175 return GetMiscContainer()->mValue.mCSSDeclaration; 176 } 177 178 inline nsIURI* nsAttrValue::GetURLValue() const { 179 MOZ_ASSERT(Type() == eURL, "wrong type"); 180 return GetMiscContainer()->mValue.mURL; 181 } 182 183 inline double nsAttrValue::GetDoubleValue() const { 184 MOZ_ASSERT(Type() == eDoubleValue, "wrong type"); 185 return GetMiscContainer()->mDoubleValue; 186 } 187 188 inline bool nsAttrValue::IsSVGType(ValueType aType) const { 189 return aType >= eSVGTypesBegin && aType <= eSVGTypesEnd; 190 } 191 192 inline bool nsAttrValue::StoresOwnData() const { 193 if (BaseType() != eOtherBase) { 194 return true; 195 } 196 ValueType t = Type(); 197 return t != eCSSDeclaration && !IsSVGType(t); 198 } 199 200 inline void nsAttrValue::SetPtrValueAndType(void* aValue, ValueBaseType aType) { 201 NS_ASSERTION(!(NS_PTR_TO_INT32(aValue) & ~NS_ATTRVALUE_POINTERVALUE_MASK), 202 "pointer not properly aligned, this will crash"); 203 mBits = reinterpret_cast<intptr_t>(aValue) | aType; 204 } 205 206 inline void nsAttrValue::ResetIfSet() { 207 if (mBits) { 208 Reset(); 209 } 210 } 211 212 inline MiscContainer* nsAttrValue::GetMiscContainer() const { 213 NS_ASSERTION(BaseType() == eOtherBase, "wrong type"); 214 return static_cast<MiscContainer*>(GetPtr()); 215 } 216 217 inline int32_t nsAttrValue::GetIntInternal() const { 218 NS_ASSERTION(BaseType() == eIntegerBase, "getting integer from non-integer"); 219 // Make sure we get a signed value. 220 // Lets hope the optimizer optimizes this into a shift. Unfortunatly signed 221 // bitshift right is implementaion dependant. 222 return static_cast<int32_t>(mBits & ~NS_ATTRVALUE_INTEGERTYPE_MASK) / 223 NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER; 224 } 225 226 inline nsAttrValue::ValueType nsAttrValue::Type() const { 227 switch (BaseType()) { 228 case eIntegerBase: { 229 return static_cast<ValueType>(mBits & NS_ATTRVALUE_INTEGERTYPE_MASK); 230 } 231 case eOtherBase: { 232 return GetMiscContainer()->mType; 233 } 234 default: { 235 return static_cast<ValueType>(static_cast<uint16_t>(BaseType())); 236 } 237 } 238 } 239 240 inline nsAtom* nsAttrValue::GetAtomValue() const { 241 MOZ_ASSERT(Type() == eAtom, "wrong type"); 242 return reinterpret_cast<nsAtom*>(GetPtr()); 243 } 244 245 inline void nsAttrValue::ToString(mozilla::dom::DOMString& aResult) const { 246 switch (Type()) { 247 case eString: { 248 if (auto* str = static_cast<mozilla::StringBuffer*>(GetPtr())) { 249 aResult.SetKnownLiveStringBuffer( 250 str, str->StorageSize() / sizeof(char16_t) - 1); 251 } 252 // else aResult is already empty 253 return; 254 } 255 case eAtom: { 256 nsAtom* atom = static_cast<nsAtom*>(GetPtr()); 257 aResult.SetKnownLiveAtom(atom, mozilla::dom::DOMString::eNullNotExpected); 258 break; 259 } 260 default: { 261 ToString(aResult.AsAString()); 262 } 263 } 264 } 265 266 inline const mozilla::ShadowParts& nsAttrValue::GetShadowPartsValue() const { 267 MOZ_ASSERT(Type() == eShadowParts); 268 return *GetMiscContainer()->mValue.mShadowParts; 269 } 270 271 #endif