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