nsAttrValue.h (21321B)
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 * A struct that represents the value (type and actual data) of an 9 * attribute. 10 */ 11 12 #ifndef nsAttrValue_h___ 13 #define nsAttrValue_h___ 14 15 #include <type_traits> 16 17 #include "mozilla/AtomArray.h" 18 #include "mozilla/EnumTypeTraits.h" 19 #include "mozilla/MemoryReporting.h" 20 #include "mozilla/StringBuffer.h" 21 #include "nsAtom.h" 22 #include "nsCaseTreatment.h" 23 #include "nsColor.h" 24 #include "nsMargin.h" 25 #include "nsString.h" 26 #include "nsStringFwd.h" 27 #include "nsTArrayForwardDeclare.h" 28 #include "nscore.h" 29 30 class nsIPrincipal; 31 class nsIURI; 32 class nsStyledElement; 33 struct MiscContainer; 34 35 namespace mozilla { 36 class DeclarationBlock; 37 class ShadowParts; 38 class SVGAnimatedIntegerPair; 39 class SVGAnimatedLength; 40 class SVGAnimatedNumberPair; 41 class SVGAnimatedOrient; 42 class SVGAnimatedPreserveAspectRatio; 43 class SVGAnimatedViewBox; 44 class SVGLengthList; 45 class SVGNumberList; 46 class SVGPathData; 47 class SVGPointList; 48 class SVGStringList; 49 class SVGTransformList; 50 51 struct AttrAtomArray { 52 AtomArray mArray; 53 mutable bool mMayContainDuplicates = false; 54 UniquePtr<AttrAtomArray> CreateDeduplicatedCopyIfDifferent() const { 55 if (!mMayContainDuplicates) { 56 return nullptr; 57 } 58 return CreateDeduplicatedCopyIfDifferentImpl(); 59 } 60 bool operator==(const AttrAtomArray& aOther) const { 61 return mArray == aOther.mArray; 62 } 63 64 size_t ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 65 return aMallocSizeOf(this) + 66 mArray.ShallowSizeOfExcludingThis(aMallocSizeOf); 67 } 68 69 private: 70 UniquePtr<AttrAtomArray> CreateDeduplicatedCopyIfDifferentImpl() const; 71 }; 72 73 namespace dom { 74 class DOMString; 75 } 76 } // namespace mozilla 77 78 #define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12 79 80 const uintptr_t NS_ATTRVALUE_BASETYPE_MASK = 3; 81 #define NS_ATTRVALUE_POINTERVALUE_MASK (~NS_ATTRVALUE_BASETYPE_MASK) 82 83 #define NS_ATTRVALUE_INTEGERTYPE_BITS 4 84 #define NS_ATTRVALUE_INTEGERTYPE_MASK \ 85 (uintptr_t((1 << NS_ATTRVALUE_INTEGERTYPE_BITS) - 1)) 86 #define NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER (1 << NS_ATTRVALUE_INTEGERTYPE_BITS) 87 #define NS_ATTRVALUE_INTEGERTYPE_MAXVALUE \ 88 ((1 << (31 - NS_ATTRVALUE_INTEGERTYPE_BITS)) - 1) 89 #define NS_ATTRVALUE_INTEGERTYPE_MINVALUE \ 90 (-NS_ATTRVALUE_INTEGERTYPE_MAXVALUE - 1) 91 92 #define NS_ATTRVALUE_ENUMTABLEINDEX_BITS \ 93 (32 - 16 - NS_ATTRVALUE_INTEGERTYPE_BITS) 94 #define NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER \ 95 (1 << (NS_ATTRVALUE_ENUMTABLEINDEX_BITS - 1)) 96 #define NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE \ 97 (NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER - 1) 98 #define NS_ATTRVALUE_ENUMTABLEINDEX_MASK \ 99 (uintptr_t((((1 << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) - 1) & \ 100 ~NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER))) 101 102 /** 103 * A class used to construct a nsString from a mozilla::StringBuffer (we might 104 * want to move this to nsString at some point). 105 * 106 * WARNING: Note that nsCheapString doesn't take an explicit length -- it 107 * assumes the string is maximally large, given the mozilla::StringBuffer's 108 * storage size. This means the given string buffer *must* be sized exactly 109 * correctly for the string it contains (including one byte for a null 110 * terminator). If it has any unused storage space, then that will result in 111 * bogus characters at the end of our nsCheapString. 112 */ 113 class nsCheapString : public nsString { 114 public: 115 explicit nsCheapString(mozilla::StringBuffer* aBuf) { 116 if (aBuf) { 117 Assign(aBuf, aBuf->StorageSize() / sizeof(char16_t) - 1); 118 } 119 } 120 }; 121 122 class nsAttrValue { 123 friend struct MiscContainer; 124 125 public: 126 // This has to be the same as in ValueBaseType 127 enum ValueType { 128 eString = 0x00, // 00 129 // 01 this value indicates a 'misc' struct 130 eAtom = 0x02, // 10 131 eInteger = 0x03, // 0011 132 eColor = 0x07, // 0111 133 eEnum = 0x0B, // 1011 This should eventually die 134 ePercent = 0x0F, // 1111 135 // Values below here won't matter, they'll be always stored in the 'misc' 136 // struct. 137 eCSSDeclaration = 0x10, 138 eURL, 139 eImage, 140 eAtomArray, 141 eDoubleValue, 142 // eShadowParts is refcounted in the misc container, as we do copy attribute 143 // values quite a bit (for example to process style invalidation), and the 144 // underlying value could get expensive to copy. 145 eShadowParts, 146 eSVGIntegerPair, 147 eSVGTypesBegin = eSVGIntegerPair, 148 eSVGOrient, 149 eSVGLength, 150 eSVGLengthList, 151 eSVGNumberList, 152 eSVGNumberPair, 153 eSVGPathData, 154 eSVGPointList, 155 eSVGPreserveAspectRatio, 156 eSVGStringList, 157 eSVGTransformList, 158 eSVGViewBox, 159 eSVGTypesEnd = eSVGViewBox, 160 }; 161 162 nsAttrValue(); 163 nsAttrValue(const nsAttrValue& aOther); 164 explicit nsAttrValue(const nsAString& aValue); 165 explicit nsAttrValue(nsAtom* aValue); 166 nsAttrValue(already_AddRefed<mozilla::DeclarationBlock> aValue, 167 const nsAString* aSerialized); 168 ~nsAttrValue(); 169 170 inline const nsAttrValue& operator=(const nsAttrValue& aOther); 171 172 static void Init(); 173 static void Shutdown(); 174 175 inline ValueType Type() const; 176 // Returns true when this value is self-contained and does not depend on 177 // the state of its associated element. 178 // Returns false when this value depends on the state of its associated 179 // element and may be invalid if that state has been changed by changes to 180 // that element state outside of attribute setting. 181 inline bool StoresOwnData() const; 182 183 void Reset(); 184 185 void SetTo(const nsAttrValue& aOther); 186 void SetTo(const nsAString& aValue); 187 void SetTo(nsAtom* aValue); 188 void SetTo(int16_t aInt); 189 void SetTo(int32_t aInt, const nsAString* aSerialized); 190 void SetTo(double aValue, const nsAString* aSerialized); 191 void SetTo(already_AddRefed<mozilla::DeclarationBlock> aValue, 192 const nsAString* aSerialized); 193 void SetTo(nsIURI* aValue, const nsAString* aSerialized); 194 void SetTo(const mozilla::SVGAnimatedIntegerPair& aValue, 195 const nsAString* aSerialized); 196 void SetTo(const mozilla::SVGAnimatedLength& aValue, 197 const nsAString* aSerialized); 198 void SetTo(const mozilla::SVGAnimatedNumberPair& aValue, 199 const nsAString* aSerialized); 200 void SetTo(const mozilla::SVGAnimatedOrient& aValue, 201 const nsAString* aSerialized); 202 void SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue, 203 const nsAString* aSerialized); 204 void SetTo(const mozilla::SVGAnimatedViewBox& aValue, 205 const nsAString* aSerialized); 206 void SetTo(const mozilla::SVGLengthList& aValue, 207 const nsAString* aSerialized); 208 void SetTo(const mozilla::SVGNumberList& aValue, 209 const nsAString* aSerialized); 210 void SetTo(const mozilla::SVGPathData& aValue, const nsAString* aSerialized); 211 void SetTo(const mozilla::SVGPointList& aValue, const nsAString* aSerialized); 212 void SetTo(const mozilla::SVGStringList& aValue, 213 const nsAString* aSerialized); 214 void SetTo(const mozilla::SVGTransformList& aValue, 215 const nsAString* aSerialized); 216 217 /** 218 * Sets this object with the string or atom representation of aValue. 219 * 220 * After calling this method, this object will have type eString unless the 221 * type of aValue is eAtom, in which case this object will also have type 222 * eAtom. 223 */ 224 void SetToSerialized(const nsAttrValue& aValue); 225 226 void SwapValueWith(nsAttrValue& aOther); 227 228 void RemoveDuplicatesFromAtomArray(); 229 230 void ToString(nsAString& aResult) const; 231 inline void ToString(mozilla::dom::DOMString& aResult) const; 232 233 /** 234 * Returns the value of this object as an atom. If necessary, the value will 235 * first be serialised using ToString before converting to an atom. 236 */ 237 already_AddRefed<nsAtom> GetAsAtom() const; 238 239 // Methods to get value. These methods do not convert so only use them 240 // to retrieve the datatype that this nsAttrValue has. 241 inline bool IsEmptyString() const; 242 const nsCheapString GetStringValue() const; 243 inline nsAtom* GetAtomValue() const; 244 inline int32_t GetIntegerValue() const; 245 bool GetColorValue(nscolor& aColor) const; 246 inline int16_t GetEnumValue() const; 247 inline double GetPercentValue() const; 248 inline const mozilla::AttrAtomArray* GetAtomArrayValue() const; 249 inline mozilla::DeclarationBlock* GetCSSDeclarationValue() const; 250 inline nsIURI* GetURLValue() const; 251 inline double GetDoubleValue() const; 252 inline const mozilla::ShadowParts& GetShadowPartsValue() const; 253 254 /** 255 * Returns the string corresponding to the stored enum value. 256 * 257 * @param aResult the string representing the enum tag 258 * @param aRealTag wheter we want to have the real tag or the saved one 259 */ 260 void GetEnumString(nsAString& aResult, bool aRealTag) const; 261 262 // Methods to get access to atoms we may have 263 // Returns the number of atoms we have; 0 if we have none. It's OK 264 // to call this without checking the type first; it handles that. 265 uint32_t GetAtomCount() const; 266 // Returns the atom at aIndex (0-based). Do not call this with 267 // aIndex >= GetAtomCount(). 268 nsAtom* AtomAt(int32_t aIndex) const; 269 270 uint32_t HashValue() const; 271 bool Equals(const nsAttrValue& aOther) const; 272 // aCaseSensitive == eIgnoreCase means ASCII case-insenstive matching 273 bool Equals(const nsAString& aValue, nsCaseTreatment aCaseSensitive) const; 274 bool Equals(const nsAtom* aValue, nsCaseTreatment aCaseSensitive) const; 275 bool HasPrefix(const nsAString& aValue, nsCaseTreatment aCaseSensitive) const; 276 bool HasSuffix(const nsAString& aValue, nsCaseTreatment aCaseSensitive) const; 277 bool HasSubstring(const nsAString& aValue, 278 nsCaseTreatment aCaseSensitive) const; 279 280 /** 281 * Compares this object with aOther according to their string representation. 282 * 283 * For example, when called on an object with type eInteger and value 4, and 284 * given aOther of type eString and value "4", EqualsAsStrings will return 285 * true (while Equals will return false). 286 */ 287 bool EqualsAsStrings(const nsAttrValue& aOther) const; 288 289 /** 290 * Returns true if this AttrValue is equal to the given atom, or is an 291 * array which contains the given atom. 292 */ 293 bool Contains(nsAtom* aValue, nsCaseTreatment aCaseSensitive) const; 294 /** 295 * Returns true if this AttrValue is an atom equal to the given 296 * string, or is an array of atoms which contains the given string. 297 * This always does a case-sensitive comparison. 298 */ 299 bool Contains(const nsAString& aValue) const; 300 301 void ParseAtom(const nsAString& aValue); 302 void ParseAtomArray(const nsAString& aValue); 303 void ParseAtomArray(nsAtom* aValue); 304 void ParseStringOrAtom(const nsAString& aValue); 305 306 /** 307 * Parses an exportparts attribute. 308 * 309 * https://drafts.csswg.org/css-shadow-parts/#parsing-mapping-list 310 */ 311 void ParsePartMapping(const nsAString&); 312 313 /** 314 * Structure for a mapping from int (enum) values to strings. When you use 315 * it you generally create an array of them. 316 * Instantiate like this: 317 * EnumTableEntry myTable[] = { 318 * { "string1", 1 }, 319 * { "string2", 2 } 320 * } 321 */ 322 struct EnumTableEntry { 323 // EnumTable can be initialized either with an int16_t value 324 // or a value of an enumeration type that can fit within an int16_t. 325 326 constexpr EnumTableEntry(const char* aTag, int16_t aValue) 327 : tag(aTag), value(aValue) {} 328 329 template <typename T, 330 typename = typename std::enable_if<std::is_enum<T>::value>::type> 331 constexpr EnumTableEntry(const char* aTag, T aValue) 332 : tag(aTag), value(static_cast<int16_t>(aValue)) { 333 static_assert(mozilla::EnumTypeFitsWithin<T, int16_t>::value, 334 "aValue must be an enum that fits within int16_t"); 335 // TODO: statically assert there are no duplicate values, otherwise 336 // `GetEnumString()` above will return wrong values. 337 } 338 339 /** The string the value maps to */ 340 const char* tag; 341 /** The enum value that maps to this string */ 342 int16_t value; 343 }; 344 345 using EnumTableSpan = mozilla::Span<const EnumTableEntry>; 346 /** 347 * Parse into an enum value. 348 * 349 * @param aValue the string to find the value for 350 * @param aTable the enumeration to map with 351 * @param aCaseSensitive specify if the parsing has to be case sensitive 352 * @param aDefaultValue if non-null, this function will always return true. 353 * Failure to parse aValue as one of the values in aTable will just 354 * cause aDefaultValue->value to be stored as the enumeration value. 355 * @return whether the enum value was found or not 356 */ 357 bool ParseEnumValue(const nsAString& aValue, EnumTableSpan aTable, 358 bool aCaseSensitive, 359 const EnumTableEntry* aDefaultValue = nullptr); 360 361 /** 362 * Parse a string into a dimension value. This is similar to 363 * https://html.spec.whatwg.org/multipage/#rules-for-parsing-dimension-values 364 * but drops the fractional part of the value for now, until we figure out how 365 * to store that in our nsAttrValue. 366 * 367 * The resulting value (if the parse succeeds) is one of eInteger, 368 * eDoubleValue, or ePercent, depending on whether we found a fractional part 369 * and whether we found '%' at the end of the value. 370 * 371 * @param aInput the string to parse 372 * @return whether the value could be parsed 373 */ 374 bool ParseHTMLDimension(const nsAString& aInput) { 375 return DoParseHTMLDimension(aInput, false); 376 } 377 378 /** 379 * Parse a string into a nonzero dimension value. This implements 380 * https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-zero-dimension-values 381 * subject to the same constraints as ParseHTMLDimension above. 382 * 383 * @param aInput the string to parse 384 * @return whether the value could be parsed 385 */ 386 bool ParseNonzeroHTMLDimension(const nsAString& aInput) { 387 return DoParseHTMLDimension(aInput, true); 388 } 389 390 /** 391 * Parse a string value into an integer. 392 * 393 * @param aString the string to parse 394 * @return whether the value could be parsed 395 */ 396 bool ParseIntValue(const nsAString& aString) { 397 return ParseIntWithBounds(aString, INT32_MIN, INT32_MAX); 398 } 399 400 /** 401 * Parse a string value into an integer with minimum value and maximum value. 402 * 403 * @param aString the string to parse 404 * @param aMin the minimum value (if value is less it will be bumped up) 405 * @param aMax the maximum value (if value is greater it will be chopped down) 406 * @return whether the value could be parsed 407 */ 408 bool ParseIntWithBounds(const nsAString& aString, int32_t aMin, 409 int32_t aMax = INT32_MAX); 410 411 /** 412 * Parse a string value into an integer with a fallback for invalid values. 413 * Also allows clamping to a maximum value to support col/colgroup.span (this 414 * is not per spec right now). 415 * 416 * @param aString the string to parse 417 * @param aDefault the default value 418 * @param aMax the maximum value (if value is greater it will be clamped) 419 */ 420 void ParseIntWithFallback(const nsAString& aString, int32_t aDefault, 421 int32_t aMax = INT32_MAX); 422 423 /** 424 * Parse a string value into a non-negative integer. 425 * This method follows the rules for parsing non-negative integer from: 426 * http://dev.w3.org/html5/spec/infrastructure.html#rules-for-parsing-non-negative-integers 427 * 428 * @param aString the string to parse 429 * @return whether the value is valid 430 */ 431 bool ParseNonNegativeIntValue(const nsAString& aString); 432 433 /** 434 * Parse a string value into a clamped non-negative integer. 435 * This method follows the rules for parsing non-negative integer from: 436 * https://html.spec.whatwg.org/multipage/infrastructure.html#clamped-to-the-range 437 * 438 * @param aString the string to parse 439 * @param aDefault value to return for negative or invalid values 440 * @param aMin minimum value 441 * @param aMax maximum value 442 */ 443 void ParseClampedNonNegativeInt(const nsAString& aString, int32_t aDefault, 444 int32_t aMin, int32_t aMax); 445 446 /** 447 * Parse a string value into a positive integer. 448 * This method follows the rules for parsing non-negative integer from: 449 * http://dev.w3.org/html5/spec/infrastructure.html#rules-for-parsing-non-negative-integers 450 * In addition of these rules, the value has to be greater than zero. 451 * 452 * This is generally used for parsing content attributes which reflecting IDL 453 * attributes are limited to only non-negative numbers greater than zero, see: 454 * http://dev.w3.org/html5/spec/common-dom-interfaces.html#limited-to-only-non-negative-numbers-greater-than-zero 455 * 456 * @param aString the string to parse 457 * @return whether the value was valid 458 */ 459 bool ParsePositiveIntValue(const nsAString& aString); 460 461 /** 462 * Parse a string into a color. This implements what HTML5 calls the 463 * "rules for parsing a legacy color value". 464 * 465 * @param aString the string to parse 466 * @return whether the value could be parsed 467 */ 468 bool ParseColor(const nsAString& aString); 469 470 /** 471 * Parse a string value into a double-precision floating point value. 472 * 473 * @param aString the string to parse 474 * @return whether the value could be parsed 475 */ 476 bool ParseDoubleValue(const nsAString& aString); 477 478 /** 479 * Parse a string into a CSS style rule. 480 * 481 * @param aString the style attribute value to be parsed. 482 * @param aElement the element the attribute is set on. 483 * @param aMaybeScriptedPrincipal if available, the scripted principal 484 * responsible for this attribute value, as passed to 485 * Element::ParseAttribute. 486 */ 487 bool ParseStyleAttribute(const nsAString& aString, 488 nsIPrincipal* aMaybeScriptedPrincipal, 489 nsStyledElement* aElement); 490 491 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; 492 493 nsAtom* GetStoredAtom() const; 494 mozilla::StringBuffer* GetStoredStringBuffer() const; 495 496 private: 497 // These have to be the same as in ValueType 498 enum ValueBaseType { 499 eStringBase = eString, // 00 500 eOtherBase = 0x01, // 01 501 eAtomBase = eAtom, // 10 502 eIntegerBase = 0x03 // 11 503 }; 504 505 inline ValueBaseType BaseType() const; 506 inline bool IsSVGType(ValueType aType) const; 507 508 /** 509 * Get the index of an EnumTable in the sEnumTableArray. 510 * If the EnumTable is not in the sEnumTableArray, it is added. 511 * 512 * @param aTable the EnumTable to get the index of. 513 * @return the index of the EnumTable. 514 */ 515 int16_t GetEnumTableIndex(EnumTableSpan aTable); 516 517 inline void SetPtrValueAndType(void* aValue, ValueBaseType aType); 518 void SetIntValueAndType(int32_t aValue, ValueType aType, 519 const nsAString* aStringValue); 520 // aType can be ePercent or eDoubleValue. 521 void SetDoubleValueAndType(double aValue, ValueType aType, 522 const nsAString* aStringValue); 523 bool SetColorValue(nscolor aColor, const nsAString& aString); 524 void SetMiscAtomOrString(const nsAString* aValue); 525 void ResetMiscAtomOrString(); 526 void SetSVGType(ValueType aType, const void* aValue, 527 const nsAString* aSerialized); 528 inline void ResetIfSet(); 529 530 inline void* GetPtr() const; 531 inline MiscContainer* GetMiscContainer() const; 532 inline int32_t GetIntInternal() const; 533 534 // Clears the current MiscContainer. This will return null if there is no 535 // existing container. 536 MiscContainer* ClearMiscContainer(); 537 // Like ClearMiscContainer, except allocates a new container if one does not 538 // exist already. 539 MiscContainer* EnsureEmptyMiscContainer(); 540 already_AddRefed<mozilla::StringBuffer> GetStringBuffer( 541 const nsAString& aValue) const; 542 // Given an enum table and a particular entry in that table, return 543 // the actual integer value we should store. 544 int32_t EnumTableEntryToValue(EnumTableSpan aEnumTable, 545 const EnumTableEntry& aTableEntry); 546 547 template <typename F> 548 bool SubstringCheck(const nsAString& aValue, 549 nsCaseTreatment aCaseSensitive) const; 550 551 static MiscContainer* AllocMiscContainer(); 552 static void DeallocMiscContainer(MiscContainer* aCont); 553 554 static nsTArray<EnumTableSpan>* sEnumTableArray; 555 556 /** 557 * Helper for ParseHTMLDimension and ParseNonzeroHTMLDimension. 558 * 559 * @param aInput the string to parse 560 * @param aEnsureNonzero whether to fail the parse if the value is 0 561 * @return whether the value could be parsed 562 */ 563 bool DoParseHTMLDimension(const nsAString& aInput, bool aEnsureNonzero); 564 565 uintptr_t mBits; 566 }; 567 568 inline const nsAttrValue& nsAttrValue::operator=(const nsAttrValue& aOther) { 569 SetTo(aOther); 570 return *this; 571 } 572 573 inline nsAttrValue::ValueBaseType nsAttrValue::BaseType() const { 574 return static_cast<ValueBaseType>(mBits & NS_ATTRVALUE_BASETYPE_MASK); 575 } 576 577 inline void* nsAttrValue::GetPtr() const { 578 NS_ASSERTION(BaseType() != eIntegerBase, "getting pointer from non-pointer"); 579 return reinterpret_cast<void*>(mBits & NS_ATTRVALUE_POINTERVALUE_MASK); 580 } 581 582 inline bool nsAttrValue::IsEmptyString() const { return !mBits; } 583 584 #endif