Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * A struct that represents the value (type and actual data) of an
8 * attribute.
9 */
11 #include "mozilla/DebugOnly.h"
12 #include "mozilla/HashFunctions.h"
14 #include "nsAttrValue.h"
15 #include "nsAttrValueInlines.h"
16 #include "nsIAtom.h"
17 #include "nsUnicharUtils.h"
18 #include "mozilla/MemoryReporting.h"
19 #include "mozilla/css/StyleRule.h"
20 #include "mozilla/css/Declaration.h"
21 #include "nsContentUtils.h"
22 #include "nsReadableUtils.h"
23 #include "prprf.h"
24 #include "nsHTMLCSSStyleSheet.h"
25 #include "nsCSSParser.h"
26 #include "nsStyledElement.h"
27 #include "nsIURI.h"
28 #include "nsIDocument.h"
29 #include <algorithm>
31 using namespace mozilla;
33 #define MISC_STR_PTR(_cont) \
34 reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
36 bool
37 MiscContainer::GetString(nsAString& aString) const
38 {
39 void* ptr = MISC_STR_PTR(this);
41 if (!ptr) {
42 return false;
43 }
45 if (static_cast<nsAttrValue::ValueBaseType>(mStringBits &
46 NS_ATTRVALUE_BASETYPE_MASK) ==
47 nsAttrValue::eStringBase) {
48 nsStringBuffer* buffer = static_cast<nsStringBuffer*>(ptr);
49 if (!buffer) {
50 return false;
51 }
53 buffer->ToString(buffer->StorageSize() / sizeof(char16_t) - 1, aString);
54 return true;
55 }
57 nsIAtom* atom = static_cast<nsIAtom*>(ptr);
58 if (!atom) {
59 return false;
60 }
62 atom->ToString(aString);
63 return true;
64 }
66 void
67 MiscContainer::Cache()
68 {
69 // Not implemented for anything else yet.
70 MOZ_ASSERT(mType == nsAttrValue::eCSSStyleRule);
71 MOZ_ASSERT(IsRefCounted());
72 MOZ_ASSERT(mValue.mRefCount > 0);
73 MOZ_ASSERT(!mValue.mCached);
75 css::StyleRule* rule = mValue.mCSSStyleRule;
76 nsHTMLCSSStyleSheet* sheet = rule->GetHTMLCSSStyleSheet();
77 if (!sheet) {
78 return;
79 }
81 nsString str;
82 bool gotString = GetString(str);
83 if (!gotString) {
84 return;
85 }
87 sheet->CacheStyleAttr(str, this);
88 mValue.mCached = 1;
90 // This has to be immutable once it goes into the cache.
91 css::Declaration* decl = rule->GetDeclaration();
92 if (decl) {
93 decl->SetImmutable();
94 }
95 }
97 void
98 MiscContainer::Evict()
99 {
100 // Not implemented for anything else yet.
101 MOZ_ASSERT(mType == nsAttrValue::eCSSStyleRule);
102 MOZ_ASSERT(IsRefCounted());
103 MOZ_ASSERT(mValue.mRefCount == 0);
105 if (!mValue.mCached) {
106 return;
107 }
109 css::StyleRule* rule = mValue.mCSSStyleRule;
110 nsHTMLCSSStyleSheet* sheet = rule->GetHTMLCSSStyleSheet();
111 MOZ_ASSERT(sheet);
113 nsString str;
114 DebugOnly<bool> gotString = GetString(str);
115 MOZ_ASSERT(gotString);
117 sheet->EvictStyleAttr(str, this);
118 mValue.mCached = 0;
119 }
121 nsTArray<const nsAttrValue::EnumTable*>* nsAttrValue::sEnumTableArray = nullptr;
123 nsAttrValue::nsAttrValue()
124 : mBits(0)
125 {
126 }
128 nsAttrValue::nsAttrValue(const nsAttrValue& aOther)
129 : mBits(0)
130 {
131 SetTo(aOther);
132 }
134 nsAttrValue::nsAttrValue(const nsAString& aValue)
135 : mBits(0)
136 {
137 SetTo(aValue);
138 }
140 nsAttrValue::nsAttrValue(nsIAtom* aValue)
141 : mBits(0)
142 {
143 SetTo(aValue);
144 }
146 nsAttrValue::nsAttrValue(css::StyleRule* aValue, const nsAString* aSerialized)
147 : mBits(0)
148 {
149 SetTo(aValue, aSerialized);
150 }
152 nsAttrValue::nsAttrValue(const nsIntMargin& aValue)
153 : mBits(0)
154 {
155 SetTo(aValue);
156 }
158 nsAttrValue::~nsAttrValue()
159 {
160 ResetIfSet();
161 }
163 /* static */
164 nsresult
165 nsAttrValue::Init()
166 {
167 NS_ASSERTION(!sEnumTableArray, "nsAttrValue already initialized");
169 sEnumTableArray = new nsTArray<const EnumTable*>;
170 NS_ENSURE_TRUE(sEnumTableArray, NS_ERROR_OUT_OF_MEMORY);
172 return NS_OK;
173 }
175 /* static */
176 void
177 nsAttrValue::Shutdown()
178 {
179 delete sEnumTableArray;
180 sEnumTableArray = nullptr;
181 }
183 nsAttrValue::ValueType
184 nsAttrValue::Type() const
185 {
186 switch (BaseType()) {
187 case eIntegerBase:
188 {
189 return static_cast<ValueType>(mBits & NS_ATTRVALUE_INTEGERTYPE_MASK);
190 }
191 case eOtherBase:
192 {
193 return GetMiscContainer()->mType;
194 }
195 default:
196 {
197 return static_cast<ValueType>(static_cast<uint16_t>(BaseType()));
198 }
199 }
200 }
202 void
203 nsAttrValue::Reset()
204 {
205 switch(BaseType()) {
206 case eStringBase:
207 {
208 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
209 if (str) {
210 str->Release();
211 }
213 break;
214 }
215 case eOtherBase:
216 {
217 MiscContainer* cont = GetMiscContainer();
218 if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) {
219 NS_RELEASE(cont);
220 break;
221 }
223 delete ClearMiscContainer();
225 break;
226 }
227 case eAtomBase:
228 {
229 nsIAtom* atom = GetAtomValue();
230 NS_RELEASE(atom);
232 break;
233 }
234 case eIntegerBase:
235 {
236 break;
237 }
238 }
240 mBits = 0;
241 }
243 void
244 nsAttrValue::SetTo(const nsAttrValue& aOther)
245 {
246 if (this == &aOther) {
247 return;
248 }
250 switch (aOther.BaseType()) {
251 case eStringBase:
252 {
253 ResetIfSet();
254 nsStringBuffer* str = static_cast<nsStringBuffer*>(aOther.GetPtr());
255 if (str) {
256 str->AddRef();
257 SetPtrValueAndType(str, eStringBase);
258 }
259 return;
260 }
261 case eOtherBase:
262 {
263 break;
264 }
265 case eAtomBase:
266 {
267 ResetIfSet();
268 nsIAtom* atom = aOther.GetAtomValue();
269 NS_ADDREF(atom);
270 SetPtrValueAndType(atom, eAtomBase);
271 return;
272 }
273 case eIntegerBase:
274 {
275 ResetIfSet();
276 mBits = aOther.mBits;
277 return;
278 }
279 }
281 MiscContainer* otherCont = aOther.GetMiscContainer();
282 if (otherCont->IsRefCounted()) {
283 delete ClearMiscContainer();
284 NS_ADDREF(otherCont);
285 SetPtrValueAndType(otherCont, eOtherBase);
286 return;
287 }
289 MiscContainer* cont = EnsureEmptyMiscContainer();
290 switch (otherCont->mType) {
291 case eInteger:
292 {
293 cont->mValue.mInteger = otherCont->mValue.mInteger;
294 break;
295 }
296 case eEnum:
297 {
298 cont->mValue.mEnumValue = otherCont->mValue.mEnumValue;
299 break;
300 }
301 case ePercent:
302 {
303 cont->mValue.mPercent = otherCont->mValue.mPercent;
304 break;
305 }
306 case eColor:
307 {
308 cont->mValue.mColor = otherCont->mValue.mColor;
309 break;
310 }
311 case eCSSStyleRule:
312 {
313 MOZ_CRASH("These should be refcounted!");
314 }
315 case eURL:
316 {
317 NS_ADDREF(cont->mValue.mURL = otherCont->mValue.mURL);
318 break;
319 }
320 case eImage:
321 {
322 NS_ADDREF(cont->mValue.mImage = otherCont->mValue.mImage);
323 break;
324 }
325 case eAtomArray:
326 {
327 if (!EnsureEmptyAtomArray() ||
328 !GetAtomArrayValue()->AppendElements(*otherCont->mValue.mAtomArray)) {
329 Reset();
330 return;
331 }
332 break;
333 }
334 case eDoubleValue:
335 {
336 cont->mDoubleValue = otherCont->mDoubleValue;
337 break;
338 }
339 case eIntMarginValue:
340 {
341 if (otherCont->mValue.mIntMargin)
342 cont->mValue.mIntMargin =
343 new nsIntMargin(*otherCont->mValue.mIntMargin);
344 break;
345 }
346 default:
347 {
348 if (IsSVGType(otherCont->mType)) {
349 // All SVG types are just pointers to classes and will therefore have
350 // the same size so it doesn't really matter which one we assign
351 cont->mValue.mSVGAngle = otherCont->mValue.mSVGAngle;
352 } else {
353 NS_NOTREACHED("unknown type stored in MiscContainer");
354 }
355 break;
356 }
357 }
359 void* otherPtr = MISC_STR_PTR(otherCont);
360 if (otherPtr) {
361 if (static_cast<ValueBaseType>(otherCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
362 eStringBase) {
363 static_cast<nsStringBuffer*>(otherPtr)->AddRef();
364 } else {
365 static_cast<nsIAtom*>(otherPtr)->AddRef();
366 }
367 cont->mStringBits = otherCont->mStringBits;
368 }
369 // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't
370 // work correctly.
371 cont->mType = otherCont->mType;
372 }
374 void
375 nsAttrValue::SetTo(const nsAString& aValue)
376 {
377 ResetIfSet();
378 nsStringBuffer* buf = GetStringBuffer(aValue).take();
379 if (buf) {
380 SetPtrValueAndType(buf, eStringBase);
381 }
382 }
384 void
385 nsAttrValue::SetTo(nsIAtom* aValue)
386 {
387 ResetIfSet();
388 if (aValue) {
389 NS_ADDREF(aValue);
390 SetPtrValueAndType(aValue, eAtomBase);
391 }
392 }
394 void
395 nsAttrValue::SetTo(int16_t aInt)
396 {
397 ResetIfSet();
398 SetIntValueAndType(aInt, eInteger, nullptr);
399 }
401 void
402 nsAttrValue::SetTo(int32_t aInt, const nsAString* aSerialized)
403 {
404 ResetIfSet();
405 SetIntValueAndType(aInt, eInteger, aSerialized);
406 }
408 void
409 nsAttrValue::SetTo(double aValue, const nsAString* aSerialized)
410 {
411 MiscContainer* cont = EnsureEmptyMiscContainer();
412 cont->mDoubleValue = aValue;
413 cont->mType = eDoubleValue;
414 SetMiscAtomOrString(aSerialized);
415 }
417 void
418 nsAttrValue::SetTo(css::StyleRule* aValue, const nsAString* aSerialized)
419 {
420 MiscContainer* cont = EnsureEmptyMiscContainer();
421 MOZ_ASSERT(cont->mValue.mRefCount == 0);
422 NS_ADDREF(cont->mValue.mCSSStyleRule = aValue);
423 cont->mType = eCSSStyleRule;
424 NS_ADDREF(cont);
425 SetMiscAtomOrString(aSerialized);
426 MOZ_ASSERT(cont->mValue.mRefCount == 1);
427 }
429 void
430 nsAttrValue::SetTo(css::URLValue* aValue, const nsAString* aSerialized)
431 {
432 MiscContainer* cont = EnsureEmptyMiscContainer();
433 NS_ADDREF(cont->mValue.mURL = aValue);
434 cont->mType = eURL;
435 SetMiscAtomOrString(aSerialized);
436 }
438 void
439 nsAttrValue::SetTo(const nsIntMargin& aValue)
440 {
441 MiscContainer* cont = EnsureEmptyMiscContainer();
442 cont->mValue.mIntMargin = new nsIntMargin(aValue);
443 cont->mType = eIntMarginValue;
444 }
446 void
447 nsAttrValue::SetToSerialized(const nsAttrValue& aOther)
448 {
449 if (aOther.Type() != nsAttrValue::eString &&
450 aOther.Type() != nsAttrValue::eAtom) {
451 nsAutoString val;
452 aOther.ToString(val);
453 SetTo(val);
454 } else {
455 SetTo(aOther);
456 }
457 }
459 void
460 nsAttrValue::SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized)
461 {
462 SetSVGType(eSVGAngle, &aValue, aSerialized);
463 }
465 void
466 nsAttrValue::SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized)
467 {
468 SetSVGType(eSVGIntegerPair, &aValue, aSerialized);
469 }
471 void
472 nsAttrValue::SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized)
473 {
474 SetSVGType(eSVGLength, &aValue, aSerialized);
475 }
477 void
478 nsAttrValue::SetTo(const SVGLengthList& aValue,
479 const nsAString* aSerialized)
480 {
481 // While an empty string will parse as a length list, there's no need to store
482 // it (and SetMiscAtomOrString will assert if we try)
483 if (aSerialized && aSerialized->IsEmpty()) {
484 aSerialized = nullptr;
485 }
486 SetSVGType(eSVGLengthList, &aValue, aSerialized);
487 }
489 void
490 nsAttrValue::SetTo(const SVGNumberList& aValue,
491 const nsAString* aSerialized)
492 {
493 // While an empty string will parse as a number list, there's no need to store
494 // it (and SetMiscAtomOrString will assert if we try)
495 if (aSerialized && aSerialized->IsEmpty()) {
496 aSerialized = nullptr;
497 }
498 SetSVGType(eSVGNumberList, &aValue, aSerialized);
499 }
501 void
502 nsAttrValue::SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized)
503 {
504 SetSVGType(eSVGNumberPair, &aValue, aSerialized);
505 }
507 void
508 nsAttrValue::SetTo(const SVGPathData& aValue,
509 const nsAString* aSerialized)
510 {
511 // While an empty string will parse as path data, there's no need to store it
512 // (and SetMiscAtomOrString will assert if we try)
513 if (aSerialized && aSerialized->IsEmpty()) {
514 aSerialized = nullptr;
515 }
516 SetSVGType(eSVGPathData, &aValue, aSerialized);
517 }
519 void
520 nsAttrValue::SetTo(const SVGPointList& aValue,
521 const nsAString* aSerialized)
522 {
523 // While an empty string will parse as a point list, there's no need to store
524 // it (and SetMiscAtomOrString will assert if we try)
525 if (aSerialized && aSerialized->IsEmpty()) {
526 aSerialized = nullptr;
527 }
528 SetSVGType(eSVGPointList, &aValue, aSerialized);
529 }
531 void
532 nsAttrValue::SetTo(const SVGAnimatedPreserveAspectRatio& aValue,
533 const nsAString* aSerialized)
534 {
535 SetSVGType(eSVGPreserveAspectRatio, &aValue, aSerialized);
536 }
538 void
539 nsAttrValue::SetTo(const SVGStringList& aValue,
540 const nsAString* aSerialized)
541 {
542 // While an empty string will parse as a string list, there's no need to store
543 // it (and SetMiscAtomOrString will assert if we try)
544 if (aSerialized && aSerialized->IsEmpty()) {
545 aSerialized = nullptr;
546 }
547 SetSVGType(eSVGStringList, &aValue, aSerialized);
548 }
550 void
551 nsAttrValue::SetTo(const SVGTransformList& aValue,
552 const nsAString* aSerialized)
553 {
554 // While an empty string will parse as a transform list, there's no need to
555 // store it (and SetMiscAtomOrString will assert if we try)
556 if (aSerialized && aSerialized->IsEmpty()) {
557 aSerialized = nullptr;
558 }
559 SetSVGType(eSVGTransformList, &aValue, aSerialized);
560 }
562 void
563 nsAttrValue::SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized)
564 {
565 SetSVGType(eSVGViewBox, &aValue, aSerialized);
566 }
568 void
569 nsAttrValue::SwapValueWith(nsAttrValue& aOther)
570 {
571 uintptr_t tmp = aOther.mBits;
572 aOther.mBits = mBits;
573 mBits = tmp;
574 }
576 void
577 nsAttrValue::ToString(nsAString& aResult) const
578 {
579 MiscContainer* cont = nullptr;
580 if (BaseType() == eOtherBase) {
581 cont = GetMiscContainer();
583 if (cont->GetString(aResult)) {
584 return;
585 }
586 }
588 switch(Type()) {
589 case eString:
590 {
591 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
592 if (str) {
593 str->ToString(str->StorageSize()/sizeof(char16_t) - 1, aResult);
594 }
595 else {
596 aResult.Truncate();
597 }
598 break;
599 }
600 case eAtom:
601 {
602 nsIAtom *atom = static_cast<nsIAtom*>(GetPtr());
603 atom->ToString(aResult);
605 break;
606 }
607 case eInteger:
608 {
609 nsAutoString intStr;
610 intStr.AppendInt(GetIntegerValue());
611 aResult = intStr;
613 break;
614 }
615 #ifdef DEBUG
616 case eColor:
617 {
618 NS_NOTREACHED("color attribute without string data");
619 aResult.Truncate();
620 break;
621 }
622 #endif
623 case eEnum:
624 {
625 GetEnumString(aResult, false);
626 break;
627 }
628 case ePercent:
629 {
630 nsAutoString intStr;
631 intStr.AppendInt(cont ? cont->mValue.mPercent : GetIntInternal());
632 aResult = intStr + NS_LITERAL_STRING("%");
634 break;
635 }
636 case eCSSStyleRule:
637 {
638 aResult.Truncate();
639 MiscContainer *container = GetMiscContainer();
640 css::Declaration *decl =
641 container->mValue.mCSSStyleRule->GetDeclaration();
642 if (decl) {
643 decl->ToString(aResult);
644 }
645 const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
647 break;
648 }
649 case eDoubleValue:
650 {
651 aResult.Truncate();
652 aResult.AppendFloat(GetDoubleValue());
653 break;
654 }
655 case eSVGAngle:
656 {
657 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGAngle,
658 aResult);
659 break;
660 }
661 case eSVGIntegerPair:
662 {
663 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGIntegerPair,
664 aResult);
665 break;
666 }
667 case eSVGLength:
668 {
669 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLength,
670 aResult);
671 break;
672 }
673 case eSVGLengthList:
674 {
675 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLengthList,
676 aResult);
677 break;
678 }
679 case eSVGNumberList:
680 {
681 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGNumberList,
682 aResult);
683 break;
684 }
685 case eSVGNumberPair:
686 {
687 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGNumberPair,
688 aResult);
689 break;
690 }
691 case eSVGPathData:
692 {
693 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPathData,
694 aResult);
695 break;
696 }
697 case eSVGPointList:
698 {
699 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPointList,
700 aResult);
701 break;
702 }
703 case eSVGPreserveAspectRatio:
704 {
705 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPreserveAspectRatio,
706 aResult);
707 break;
708 }
709 case eSVGStringList:
710 {
711 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGStringList,
712 aResult);
713 break;
714 }
715 case eSVGTransformList:
716 {
717 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGTransformList,
718 aResult);
719 break;
720 }
721 case eSVGViewBox:
722 {
723 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGViewBox,
724 aResult);
725 break;
726 }
727 default:
728 {
729 aResult.Truncate();
730 break;
731 }
732 }
733 }
735 already_AddRefed<nsIAtom>
736 nsAttrValue::GetAsAtom() const
737 {
738 switch (Type()) {
739 case eString:
740 return do_GetAtom(GetStringValue());
742 case eAtom:
743 {
744 nsCOMPtr<nsIAtom> atom = GetAtomValue();
745 return atom.forget();
746 }
748 default:
749 {
750 nsAutoString val;
751 ToString(val);
752 return do_GetAtom(val);
753 }
754 }
755 }
757 const nsCheapString
758 nsAttrValue::GetStringValue() const
759 {
760 NS_PRECONDITION(Type() == eString, "wrong type");
762 return nsCheapString(static_cast<nsStringBuffer*>(GetPtr()));
763 }
765 bool
766 nsAttrValue::GetColorValue(nscolor& aColor) const
767 {
768 if (Type() != eColor) {
769 // Unparseable value, treat as unset.
770 NS_ASSERTION(Type() == eString, "unexpected type for color-valued attr");
771 return false;
772 }
774 aColor = GetMiscContainer()->mValue.mColor;
775 return true;
776 }
778 void
779 nsAttrValue::GetEnumString(nsAString& aResult, bool aRealTag) const
780 {
781 NS_PRECONDITION(Type() == eEnum, "wrong type");
783 uint32_t allEnumBits =
784 (BaseType() == eIntegerBase) ? static_cast<uint32_t>(GetIntInternal())
785 : GetMiscContainer()->mValue.mEnumValue;
786 int16_t val = allEnumBits >> NS_ATTRVALUE_ENUMTABLEINDEX_BITS;
787 const EnumTable* table = sEnumTableArray->
788 ElementAt(allEnumBits & NS_ATTRVALUE_ENUMTABLEINDEX_MASK);
790 while (table->tag) {
791 if (table->value == val) {
792 aResult.AssignASCII(table->tag);
793 if (!aRealTag && allEnumBits & NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER) {
794 nsContentUtils::ASCIIToUpper(aResult);
795 }
796 return;
797 }
798 table++;
799 }
801 NS_NOTREACHED("couldn't find value in EnumTable");
802 }
804 uint32_t
805 nsAttrValue::GetAtomCount() const
806 {
807 ValueType type = Type();
809 if (type == eAtom) {
810 return 1;
811 }
813 if (type == eAtomArray) {
814 return GetAtomArrayValue()->Length();
815 }
817 return 0;
818 }
820 nsIAtom*
821 nsAttrValue::AtomAt(int32_t aIndex) const
822 {
823 NS_PRECONDITION(aIndex >= 0, "Index must not be negative");
824 NS_PRECONDITION(GetAtomCount() > uint32_t(aIndex), "aIndex out of range");
826 if (BaseType() == eAtomBase) {
827 return GetAtomValue();
828 }
830 NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused");
832 return GetAtomArrayValue()->ElementAt(aIndex);
833 }
835 uint32_t
836 nsAttrValue::HashValue() const
837 {
838 switch(BaseType()) {
839 case eStringBase:
840 {
841 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
842 if (str) {
843 uint32_t len = str->StorageSize()/sizeof(char16_t) - 1;
844 return HashString(static_cast<char16_t*>(str->Data()), len);
845 }
847 return 0;
848 }
849 case eOtherBase:
850 {
851 break;
852 }
853 case eAtomBase:
854 case eIntegerBase:
855 {
856 // mBits and uint32_t might have different size. This should silence
857 // any warnings or compile-errors. This is what the implementation of
858 // NS_PTR_TO_INT32 does to take care of the same problem.
859 return mBits - 0;
860 }
861 }
863 MiscContainer* cont = GetMiscContainer();
864 if (static_cast<ValueBaseType>(cont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK)
865 == eAtomBase) {
866 return cont->mStringBits - 0;
867 }
869 switch (cont->mType) {
870 case eInteger:
871 {
872 return cont->mValue.mInteger;
873 }
874 case eEnum:
875 {
876 return cont->mValue.mEnumValue;
877 }
878 case ePercent:
879 {
880 return cont->mValue.mPercent;
881 }
882 case eColor:
883 {
884 return cont->mValue.mColor;
885 }
886 case eCSSStyleRule:
887 {
888 return NS_PTR_TO_INT32(cont->mValue.mCSSStyleRule);
889 }
890 // Intentionally identical, so that loading the image does not change the
891 // hash code.
892 case eURL:
893 case eImage:
894 {
895 nsString str;
896 ToString(str);
897 return HashString(str);
898 }
899 case eAtomArray:
900 {
901 uint32_t hash = 0;
902 uint32_t count = cont->mValue.mAtomArray->Length();
903 for (nsCOMPtr<nsIAtom> *cur = cont->mValue.mAtomArray->Elements(),
904 *end = cur + count;
905 cur != end; ++cur) {
906 hash = AddToHash(hash, cur->get());
907 }
908 return hash;
909 }
910 case eDoubleValue:
911 {
912 // XXX this is crappy, but oh well
913 return cont->mDoubleValue;
914 }
915 case eIntMarginValue:
916 {
917 return NS_PTR_TO_INT32(cont->mValue.mIntMargin);
918 }
919 default:
920 {
921 if (IsSVGType(cont->mType)) {
922 // All SVG types are just pointers to classes so we can treat them alike
923 return NS_PTR_TO_INT32(cont->mValue.mSVGAngle);
924 }
925 NS_NOTREACHED("unknown type stored in MiscContainer");
926 return 0;
927 }
928 }
929 }
931 bool
932 nsAttrValue::Equals(const nsAttrValue& aOther) const
933 {
934 if (BaseType() != aOther.BaseType()) {
935 return false;
936 }
938 switch(BaseType()) {
939 case eStringBase:
940 {
941 return GetStringValue().Equals(aOther.GetStringValue());
942 }
943 case eOtherBase:
944 {
945 break;
946 }
947 case eAtomBase:
948 case eIntegerBase:
949 {
950 return mBits == aOther.mBits;
951 }
952 }
954 MiscContainer* thisCont = GetMiscContainer();
955 MiscContainer* otherCont = aOther.GetMiscContainer();
956 if (thisCont == otherCont) {
957 return true;
958 }
960 if (thisCont->mType != otherCont->mType) {
961 return false;
962 }
964 bool needsStringComparison = false;
966 switch (thisCont->mType) {
967 case eInteger:
968 {
969 if (thisCont->mValue.mInteger == otherCont->mValue.mInteger) {
970 needsStringComparison = true;
971 }
972 break;
973 }
974 case eEnum:
975 {
976 if (thisCont->mValue.mEnumValue == otherCont->mValue.mEnumValue) {
977 needsStringComparison = true;
978 }
979 break;
980 }
981 case ePercent:
982 {
983 if (thisCont->mValue.mPercent == otherCont->mValue.mPercent) {
984 needsStringComparison = true;
985 }
986 break;
987 }
988 case eColor:
989 {
990 if (thisCont->mValue.mColor == otherCont->mValue.mColor) {
991 needsStringComparison = true;
992 }
993 break;
994 }
995 case eCSSStyleRule:
996 {
997 return thisCont->mValue.mCSSStyleRule == otherCont->mValue.mCSSStyleRule;
998 }
999 case eURL:
1000 {
1001 return thisCont->mValue.mURL == otherCont->mValue.mURL;
1002 }
1003 case eImage:
1004 {
1005 return thisCont->mValue.mImage == otherCont->mValue.mImage;
1006 }
1007 case eAtomArray:
1008 {
1009 // For classlists we could be insensitive to order, however
1010 // classlists are never mapped attributes so they are never compared.
1012 if (!(*thisCont->mValue.mAtomArray == *otherCont->mValue.mAtomArray)) {
1013 return false;
1014 }
1016 needsStringComparison = true;
1017 break;
1018 }
1019 case eDoubleValue:
1020 {
1021 return thisCont->mDoubleValue == otherCont->mDoubleValue;
1022 }
1023 case eIntMarginValue:
1024 {
1025 return thisCont->mValue.mIntMargin == otherCont->mValue.mIntMargin;
1026 }
1027 default:
1028 {
1029 if (IsSVGType(thisCont->mType)) {
1030 // Currently this method is never called for nsAttrValue objects that
1031 // point to SVG data types.
1032 // If that changes then we probably want to add methods to the
1033 // corresponding SVG types to compare their base values.
1034 // As a shortcut, however, we can begin by comparing the pointers.
1035 NS_ABORT_IF_FALSE(false, "Comparing nsAttrValues that point to SVG "
1036 "data");
1037 return false;
1038 }
1039 NS_NOTREACHED("unknown type stored in MiscContainer");
1040 return false;
1041 }
1042 }
1043 if (needsStringComparison) {
1044 if (thisCont->mStringBits == otherCont->mStringBits) {
1045 return true;
1046 }
1047 if ((static_cast<ValueBaseType>(thisCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
1048 eStringBase) &&
1049 (static_cast<ValueBaseType>(otherCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
1050 eStringBase)) {
1051 return nsCheapString(reinterpret_cast<nsStringBuffer*>(thisCont->mStringBits)).Equals(
1052 nsCheapString(reinterpret_cast<nsStringBuffer*>(otherCont->mStringBits)));
1053 }
1054 }
1055 return false;
1056 }
1058 bool
1059 nsAttrValue::Equals(const nsAString& aValue,
1060 nsCaseTreatment aCaseSensitive) const
1061 {
1062 switch (BaseType()) {
1063 case eStringBase:
1064 {
1065 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
1066 if (str) {
1067 nsDependentString dep(static_cast<char16_t*>(str->Data()),
1068 str->StorageSize()/sizeof(char16_t) - 1);
1069 return aCaseSensitive == eCaseMatters ? aValue.Equals(dep) :
1070 nsContentUtils::EqualsIgnoreASCIICase(aValue, dep);
1071 }
1072 return aValue.IsEmpty();
1073 }
1074 case eAtomBase:
1075 if (aCaseSensitive == eCaseMatters) {
1076 return static_cast<nsIAtom*>(GetPtr())->Equals(aValue);
1077 }
1078 return nsContentUtils::EqualsIgnoreASCIICase(
1079 nsDependentAtomString(static_cast<nsIAtom*>(GetPtr())),
1080 aValue);
1081 default:
1082 break;
1083 }
1085 nsAutoString val;
1086 ToString(val);
1087 return aCaseSensitive == eCaseMatters ? val.Equals(aValue) :
1088 nsContentUtils::EqualsIgnoreASCIICase(val, aValue);
1089 }
1091 bool
1092 nsAttrValue::Equals(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const
1093 {
1094 if (aCaseSensitive != eCaseMatters) {
1095 // Need a better way to handle this!
1096 nsAutoString value;
1097 aValue->ToString(value);
1098 return Equals(value, aCaseSensitive);
1099 }
1101 switch (BaseType()) {
1102 case eStringBase:
1103 {
1104 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
1105 if (str) {
1106 nsDependentString dep(static_cast<char16_t*>(str->Data()),
1107 str->StorageSize()/sizeof(char16_t) - 1);
1108 return aValue->Equals(dep);
1109 }
1110 return aValue == nsGkAtoms::_empty;
1111 }
1112 case eAtomBase:
1113 {
1114 return static_cast<nsIAtom*>(GetPtr()) == aValue;
1115 }
1116 default:
1117 break;
1118 }
1120 nsAutoString val;
1121 ToString(val);
1122 return aValue->Equals(val);
1123 }
1125 bool
1126 nsAttrValue::EqualsAsStrings(const nsAttrValue& aOther) const
1127 {
1128 if (Type() == aOther.Type()) {
1129 return Equals(aOther);
1130 }
1132 // We need to serialize at least one nsAttrValue before passing to
1133 // Equals(const nsAString&), but we can avoid unnecessarily serializing both
1134 // by checking if one is already of a string type.
1135 bool thisIsString = (BaseType() == eStringBase || BaseType() == eAtomBase);
1136 const nsAttrValue& lhs = thisIsString ? *this : aOther;
1137 const nsAttrValue& rhs = thisIsString ? aOther : *this;
1139 switch (rhs.BaseType()) {
1140 case eAtomBase:
1141 return lhs.Equals(rhs.GetAtomValue(), eCaseMatters);
1143 case eStringBase:
1144 return lhs.Equals(rhs.GetStringValue(), eCaseMatters);
1146 default:
1147 {
1148 nsAutoString val;
1149 rhs.ToString(val);
1150 return lhs.Equals(val, eCaseMatters);
1151 }
1152 }
1153 }
1155 bool
1156 nsAttrValue::Contains(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const
1157 {
1158 switch (BaseType()) {
1159 case eAtomBase:
1160 {
1161 nsIAtom* atom = GetAtomValue();
1163 if (aCaseSensitive == eCaseMatters) {
1164 return aValue == atom;
1165 }
1167 // For performance reasons, don't do a full on unicode case insensitive
1168 // string comparison. This is only used for quirks mode anyway.
1169 return
1170 nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(aValue),
1171 nsDependentAtomString(atom));
1172 }
1173 default:
1174 {
1175 if (Type() == eAtomArray) {
1176 AtomArray* array = GetAtomArrayValue();
1177 if (aCaseSensitive == eCaseMatters) {
1178 return array->Contains(aValue);
1179 }
1181 nsDependentAtomString val1(aValue);
1183 for (nsCOMPtr<nsIAtom> *cur = array->Elements(),
1184 *end = cur + array->Length();
1185 cur != end; ++cur) {
1186 // For performance reasons, don't do a full on unicode case
1187 // insensitive string comparison. This is only used for quirks mode
1188 // anyway.
1189 if (nsContentUtils::EqualsIgnoreASCIICase(val1,
1190 nsDependentAtomString(*cur))) {
1191 return true;
1192 }
1193 }
1194 }
1195 }
1196 }
1198 return false;
1199 }
1201 struct AtomArrayStringComparator {
1202 bool Equals(nsIAtom* atom, const nsAString& string) const {
1203 return atom->Equals(string);
1204 }
1205 };
1207 bool
1208 nsAttrValue::Contains(const nsAString& aValue) const
1209 {
1210 switch (BaseType()) {
1211 case eAtomBase:
1212 {
1213 nsIAtom* atom = GetAtomValue();
1214 return atom->Equals(aValue);
1215 }
1216 default:
1217 {
1218 if (Type() == eAtomArray) {
1219 AtomArray* array = GetAtomArrayValue();
1220 return array->Contains(aValue, AtomArrayStringComparator());
1221 }
1222 }
1223 }
1225 return false;
1226 }
1228 void
1229 nsAttrValue::ParseAtom(const nsAString& aValue)
1230 {
1231 ResetIfSet();
1233 nsCOMPtr<nsIAtom> atom = NS_NewAtom(aValue);
1234 if (atom) {
1235 SetPtrValueAndType(atom.forget().take(), eAtomBase);
1236 }
1237 }
1239 void
1240 nsAttrValue::ParseAtomArray(const nsAString& aValue)
1241 {
1242 nsAString::const_iterator iter, end;
1243 aValue.BeginReading(iter);
1244 aValue.EndReading(end);
1245 bool hasSpace = false;
1247 // skip initial whitespace
1248 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1249 hasSpace = true;
1250 ++iter;
1251 }
1253 if (iter == end) {
1254 SetTo(aValue);
1255 return;
1256 }
1258 nsAString::const_iterator start(iter);
1260 // get first - and often only - atom
1261 do {
1262 ++iter;
1263 } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
1265 nsCOMPtr<nsIAtom> classAtom = do_GetAtom(Substring(start, iter));
1266 if (!classAtom) {
1267 Reset();
1268 return;
1269 }
1271 // skip whitespace
1272 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1273 hasSpace = true;
1274 ++iter;
1275 }
1277 if (iter == end && !hasSpace) {
1278 // we only found one classname and there was no whitespace so
1279 // don't bother storing a list
1280 ResetIfSet();
1281 nsIAtom* atom = nullptr;
1282 classAtom.swap(atom);
1283 SetPtrValueAndType(atom, eAtomBase);
1284 return;
1285 }
1287 if (!EnsureEmptyAtomArray()) {
1288 return;
1289 }
1291 AtomArray* array = GetAtomArrayValue();
1293 if (!array->AppendElement(classAtom)) {
1294 Reset();
1295 return;
1296 }
1298 // parse the rest of the classnames
1299 while (iter != end) {
1300 start = iter;
1302 do {
1303 ++iter;
1304 } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
1306 classAtom = do_GetAtom(Substring(start, iter));
1308 if (!array->AppendElement(classAtom)) {
1309 Reset();
1310 return;
1311 }
1313 // skip whitespace
1314 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1315 ++iter;
1316 }
1317 }
1319 SetMiscAtomOrString(&aValue);
1320 return;
1321 }
1323 void
1324 nsAttrValue::ParseStringOrAtom(const nsAString& aValue)
1325 {
1326 uint32_t len = aValue.Length();
1327 // Don't bother with atoms if it's an empty string since
1328 // we can store those efficently anyway.
1329 if (len && len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
1330 ParseAtom(aValue);
1331 }
1332 else {
1333 SetTo(aValue);
1334 }
1335 }
1337 void
1338 nsAttrValue::SetIntValueAndType(int32_t aValue, ValueType aType,
1339 const nsAString* aStringValue)
1340 {
1341 if (aStringValue || aValue > NS_ATTRVALUE_INTEGERTYPE_MAXVALUE ||
1342 aValue < NS_ATTRVALUE_INTEGERTYPE_MINVALUE) {
1343 MiscContainer* cont = EnsureEmptyMiscContainer();
1344 switch (aType) {
1345 case eInteger:
1346 {
1347 cont->mValue.mInteger = aValue;
1348 break;
1349 }
1350 case ePercent:
1351 {
1352 cont->mValue.mPercent = aValue;
1353 break;
1354 }
1355 case eEnum:
1356 {
1357 cont->mValue.mEnumValue = aValue;
1358 break;
1359 }
1360 default:
1361 {
1362 NS_NOTREACHED("unknown integer type");
1363 break;
1364 }
1365 }
1366 cont->mType = aType;
1367 SetMiscAtomOrString(aStringValue);
1368 } else {
1369 NS_ASSERTION(!mBits, "Reset before calling SetIntValueAndType!");
1370 mBits = (aValue * NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER) | aType;
1371 }
1372 }
1374 int16_t
1375 nsAttrValue::GetEnumTableIndex(const EnumTable* aTable)
1376 {
1377 int16_t index = sEnumTableArray->IndexOf(aTable);
1378 if (index < 0) {
1379 index = sEnumTableArray->Length();
1380 NS_ASSERTION(index <= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE,
1381 "too many enum tables");
1382 sEnumTableArray->AppendElement(aTable);
1383 }
1385 return index;
1386 }
1388 int32_t
1389 nsAttrValue::EnumTableEntryToValue(const EnumTable* aEnumTable,
1390 const EnumTable* aTableEntry)
1391 {
1392 int16_t index = GetEnumTableIndex(aEnumTable);
1393 int32_t value = (aTableEntry->value << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) +
1394 index;
1395 return value;
1396 }
1398 bool
1399 nsAttrValue::ParseEnumValue(const nsAString& aValue,
1400 const EnumTable* aTable,
1401 bool aCaseSensitive,
1402 const EnumTable* aDefaultValue)
1403 {
1404 ResetIfSet();
1405 const EnumTable* tableEntry = aTable;
1407 while (tableEntry->tag) {
1408 if (aCaseSensitive ? aValue.EqualsASCII(tableEntry->tag) :
1409 aValue.LowerCaseEqualsASCII(tableEntry->tag)) {
1410 int32_t value = EnumTableEntryToValue(aTable, tableEntry);
1412 bool equals = aCaseSensitive || aValue.EqualsASCII(tableEntry->tag);
1413 if (!equals) {
1414 nsAutoString tag;
1415 tag.AssignASCII(tableEntry->tag);
1416 nsContentUtils::ASCIIToUpper(tag);
1417 if ((equals = tag.Equals(aValue))) {
1418 value |= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER;
1419 }
1420 }
1421 SetIntValueAndType(value, eEnum, equals ? nullptr : &aValue);
1422 NS_ASSERTION(GetEnumValue() == tableEntry->value,
1423 "failed to store enum properly");
1425 return true;
1426 }
1427 tableEntry++;
1428 }
1430 if (aDefaultValue) {
1431 NS_PRECONDITION(aTable <= aDefaultValue && aDefaultValue < tableEntry,
1432 "aDefaultValue not inside aTable?");
1433 SetIntValueAndType(EnumTableEntryToValue(aTable, aDefaultValue),
1434 eEnum, &aValue);
1435 return true;
1436 }
1438 return false;
1439 }
1441 bool
1442 nsAttrValue::ParseSpecialIntValue(const nsAString& aString)
1443 {
1444 ResetIfSet();
1446 nsresult ec;
1447 bool strict;
1448 bool isPercent = false;
1449 nsAutoString tmp(aString);
1450 int32_t originalVal = StringToInteger(aString, &strict, &ec, true, &isPercent);
1452 if (NS_FAILED(ec)) {
1453 return false;
1454 }
1456 int32_t val = std::max(originalVal, 0);
1458 // % (percent)
1459 if (isPercent || tmp.RFindChar('%') >= 0) {
1460 isPercent = true;
1461 }
1463 strict = strict && (originalVal == val);
1465 SetIntValueAndType(val,
1466 isPercent ? ePercent : eInteger,
1467 strict ? nullptr : &aString);
1468 return true;
1469 }
1471 bool
1472 nsAttrValue::ParseIntWithBounds(const nsAString& aString,
1473 int32_t aMin, int32_t aMax)
1474 {
1475 NS_PRECONDITION(aMin < aMax, "bad boundaries");
1477 ResetIfSet();
1479 nsresult ec;
1480 bool strict;
1481 int32_t originalVal = StringToInteger(aString, &strict, &ec);
1482 if (NS_FAILED(ec)) {
1483 return false;
1484 }
1486 int32_t val = std::max(originalVal, aMin);
1487 val = std::min(val, aMax);
1488 strict = strict && (originalVal == val);
1489 SetIntValueAndType(val, eInteger, strict ? nullptr : &aString);
1491 return true;
1492 }
1494 bool
1495 nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString)
1496 {
1497 ResetIfSet();
1499 nsresult ec;
1500 bool strict;
1501 int32_t originalVal = StringToInteger(aString, &strict, &ec);
1502 if (NS_FAILED(ec) || originalVal < 0) {
1503 return false;
1504 }
1506 SetIntValueAndType(originalVal, eInteger, strict ? nullptr : &aString);
1508 return true;
1509 }
1511 bool
1512 nsAttrValue::ParsePositiveIntValue(const nsAString& aString)
1513 {
1514 ResetIfSet();
1516 nsresult ec;
1517 bool strict;
1518 int32_t originalVal = StringToInteger(aString, &strict, &ec);
1519 if (NS_FAILED(ec) || originalVal <= 0) {
1520 return false;
1521 }
1523 SetIntValueAndType(originalVal, eInteger, strict ? nullptr : &aString);
1525 return true;
1526 }
1528 void
1529 nsAttrValue::SetColorValue(nscolor aColor, const nsAString& aString)
1530 {
1531 nsStringBuffer* buf = GetStringBuffer(aString).take();
1532 if (!buf) {
1533 return;
1534 }
1536 MiscContainer* cont = EnsureEmptyMiscContainer();
1537 cont->mValue.mColor = aColor;
1538 cont->mType = eColor;
1540 // Save the literal string we were passed for round-tripping.
1541 cont->mStringBits = reinterpret_cast<uintptr_t>(buf) | eStringBase;
1542 }
1544 bool
1545 nsAttrValue::ParseColor(const nsAString& aString)
1546 {
1547 ResetIfSet();
1549 // FIXME (partially, at least): HTML5's algorithm says we shouldn't do
1550 // the whitespace compression, trimming, or the test for emptiness.
1551 // (I'm a little skeptical that we shouldn't do the whitespace
1552 // trimming; WebKit also does it.)
1553 nsAutoString colorStr(aString);
1554 colorStr.CompressWhitespace(true, true);
1555 if (colorStr.IsEmpty()) {
1556 return false;
1557 }
1559 nscolor color;
1560 // No color names begin with a '#'; in standards mode, all acceptable
1561 // numeric colors do.
1562 if (colorStr.First() == '#') {
1563 nsDependentString withoutHash(colorStr.get() + 1, colorStr.Length() - 1);
1564 if (NS_HexToRGB(withoutHash, &color)) {
1565 SetColorValue(color, aString);
1566 return true;
1567 }
1568 } else {
1569 if (NS_ColorNameToRGB(colorStr, &color)) {
1570 SetColorValue(color, aString);
1571 return true;
1572 }
1573 }
1575 // FIXME (maybe): HTML5 says we should handle system colors. This
1576 // means we probably need another storage type, since we'd need to
1577 // handle dynamic changes. However, I think this is a bad idea:
1578 // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-May/026449.html
1580 // Use NS_LooseHexToRGB as a fallback if nothing above worked.
1581 if (NS_LooseHexToRGB(colorStr, &color)) {
1582 SetColorValue(color, aString);
1583 return true;
1584 }
1586 return false;
1587 }
1589 bool nsAttrValue::ParseDoubleValue(const nsAString& aString)
1590 {
1591 ResetIfSet();
1593 nsresult ec;
1594 double val = PromiseFlatString(aString).ToDouble(&ec);
1595 if (NS_FAILED(ec)) {
1596 return false;
1597 }
1599 MiscContainer* cont = EnsureEmptyMiscContainer();
1600 cont->mDoubleValue = val;
1601 cont->mType = eDoubleValue;
1602 nsAutoString serializedFloat;
1603 serializedFloat.AppendFloat(val);
1604 SetMiscAtomOrString(serializedFloat.Equals(aString) ? nullptr : &aString);
1605 return true;
1606 }
1608 bool
1609 nsAttrValue::ParseIntMarginValue(const nsAString& aString)
1610 {
1611 ResetIfSet();
1613 nsIntMargin margins;
1614 if (!nsContentUtils::ParseIntMarginValue(aString, margins))
1615 return false;
1617 MiscContainer* cont = EnsureEmptyMiscContainer();
1618 cont->mValue.mIntMargin = new nsIntMargin(margins);
1619 cont->mType = eIntMarginValue;
1620 SetMiscAtomOrString(&aString);
1621 return true;
1622 }
1624 void
1625 nsAttrValue::LoadImage(nsIDocument* aDocument)
1626 {
1627 NS_ASSERTION(Type() == eURL, "wrong type");
1629 #ifdef DEBUG
1630 {
1631 nsString val;
1632 ToString(val);
1633 NS_ASSERTION(!val.IsEmpty(),
1634 "How did we end up with an empty string for eURL");
1635 }
1636 #endif
1638 MiscContainer* cont = GetMiscContainer();
1639 mozilla::css::URLValue* url = cont->mValue.mURL;
1640 mozilla::css::ImageValue* image =
1641 new css::ImageValue(url->GetURI(), url->mString, url->mReferrer,
1642 url->mOriginPrincipal, aDocument);
1644 NS_ADDREF(image);
1645 cont->mValue.mImage = image;
1646 NS_RELEASE(url);
1647 cont->mType = eImage;
1648 }
1650 bool
1651 nsAttrValue::ParseStyleAttribute(const nsAString& aString,
1652 nsStyledElementNotElementCSSInlineStyle* aElement)
1653 {
1654 nsIDocument* ownerDoc = aElement->OwnerDoc();
1655 nsHTMLCSSStyleSheet* sheet = ownerDoc->GetInlineStyleSheet();
1656 nsCOMPtr<nsIURI> baseURI = aElement->GetBaseURI();
1657 nsIURI* docURI = ownerDoc->GetDocumentURI();
1659 NS_ASSERTION(aElement->NodePrincipal() == ownerDoc->NodePrincipal(),
1660 "This is unexpected");
1662 // If the (immutable) document URI does not match the element's base URI
1663 // (the common case is that they do match) do not cache the rule. This is
1664 // because the results of the CSS parser are dependent on these URIs, and we
1665 // do not want to have to account for the URIs in the hash lookup.
1666 bool cachingAllowed = sheet && baseURI == docURI;
1667 if (cachingAllowed) {
1668 MiscContainer* cont = sheet->LookupStyleAttr(aString);
1669 if (cont) {
1670 // Set our MiscContainer to the cached one.
1671 NS_ADDREF(cont);
1672 SetPtrValueAndType(cont, eOtherBase);
1673 return true;
1674 }
1675 }
1677 css::Loader* cssLoader = ownerDoc->CSSLoader();
1678 nsCSSParser cssParser(cssLoader);
1680 nsRefPtr<css::StyleRule> rule;
1681 cssParser.ParseStyleAttribute(aString, docURI, baseURI,
1682 aElement->NodePrincipal(),
1683 getter_AddRefs(rule));
1684 if (rule) {
1685 rule->SetHTMLCSSStyleSheet(sheet);
1686 SetTo(rule, &aString);
1687 if (cachingAllowed) {
1688 MiscContainer* cont = GetMiscContainer();
1689 cont->Cache();
1690 }
1692 return true;
1693 }
1695 return false;
1696 }
1698 void
1699 nsAttrValue::SetMiscAtomOrString(const nsAString* aValue)
1700 {
1701 NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
1702 NS_ASSERTION(!GetMiscContainer()->mStringBits,
1703 "Trying to re-set atom or string!");
1704 if (aValue) {
1705 uint32_t len = aValue->Length();
1706 // * We're allowing eCSSStyleRule attributes to store empty strings as it
1707 // can be beneficial to store an empty style attribute as a parsed rule.
1708 // * We're allowing enumerated values because sometimes the empty
1709 // string corresponds to a particular enumerated value, especially
1710 // for enumerated values that are not limited enumerated.
1711 // Add other types as needed.
1712 NS_ASSERTION(len || Type() == eCSSStyleRule || Type() == eEnum,
1713 "Empty string?");
1714 MiscContainer* cont = GetMiscContainer();
1715 if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
1716 nsCOMPtr<nsIAtom> atom = NS_NewAtom(*aValue);
1717 if (atom) {
1718 cont->mStringBits =
1719 reinterpret_cast<uintptr_t>(atom.forget().take()) | eAtomBase;
1720 }
1721 } else {
1722 nsStringBuffer* buf = GetStringBuffer(*aValue).take();
1723 if (buf) {
1724 cont->mStringBits = reinterpret_cast<uintptr_t>(buf) | eStringBase;
1725 }
1726 }
1727 }
1728 }
1730 void
1731 nsAttrValue::ResetMiscAtomOrString()
1732 {
1733 MiscContainer* cont = GetMiscContainer();
1734 void* ptr = MISC_STR_PTR(cont);
1735 if (ptr) {
1736 if (static_cast<ValueBaseType>(cont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
1737 eStringBase) {
1738 static_cast<nsStringBuffer*>(ptr)->Release();
1739 } else {
1740 static_cast<nsIAtom*>(ptr)->Release();
1741 }
1742 cont->mStringBits = 0;
1743 }
1744 }
1746 void
1747 nsAttrValue::SetSVGType(ValueType aType, const void* aValue,
1748 const nsAString* aSerialized) {
1749 NS_ABORT_IF_FALSE(IsSVGType(aType), "Not an SVG type");
1751 MiscContainer* cont = EnsureEmptyMiscContainer();
1752 // All SVG types are just pointers to classes so just setting any of them
1753 // will do. We'll lose type-safety but the signature of the calling
1754 // function should ensure we don't get anything unexpected, and once we
1755 // stick aValue in a union we lose type information anyway.
1756 cont->mValue.mSVGAngle = static_cast<const nsSVGAngle*>(aValue);
1757 cont->mType = aType;
1758 SetMiscAtomOrString(aSerialized);
1759 }
1761 MiscContainer*
1762 nsAttrValue::ClearMiscContainer()
1763 {
1764 MiscContainer* cont = nullptr;
1765 if (BaseType() == eOtherBase) {
1766 cont = GetMiscContainer();
1767 if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) {
1768 // This MiscContainer is shared, we need a new one.
1769 NS_RELEASE(cont);
1771 cont = new MiscContainer;
1772 SetPtrValueAndType(cont, eOtherBase);
1773 }
1774 else {
1775 switch (cont->mType) {
1776 case eCSSStyleRule:
1777 {
1778 MOZ_ASSERT(cont->mValue.mRefCount == 1);
1779 cont->Release();
1780 cont->Evict();
1781 NS_RELEASE(cont->mValue.mCSSStyleRule);
1782 break;
1783 }
1784 case eURL:
1785 {
1786 NS_RELEASE(cont->mValue.mURL);
1787 break;
1788 }
1789 case eImage:
1790 {
1791 NS_RELEASE(cont->mValue.mImage);
1792 break;
1793 }
1794 case eAtomArray:
1795 {
1796 delete cont->mValue.mAtomArray;
1797 break;
1798 }
1799 case eIntMarginValue:
1800 {
1801 delete cont->mValue.mIntMargin;
1802 break;
1803 }
1804 default:
1805 {
1806 break;
1807 }
1808 }
1809 }
1810 ResetMiscAtomOrString();
1811 }
1812 else {
1813 ResetIfSet();
1814 }
1816 return cont;
1817 }
1819 MiscContainer*
1820 nsAttrValue::EnsureEmptyMiscContainer()
1821 {
1822 MiscContainer* cont = ClearMiscContainer();
1823 if (cont) {
1824 MOZ_ASSERT(BaseType() == eOtherBase);
1825 ResetMiscAtomOrString();
1826 cont = GetMiscContainer();
1827 }
1828 else {
1829 cont = new MiscContainer;
1830 SetPtrValueAndType(cont, eOtherBase);
1831 }
1833 return cont;
1834 }
1836 bool
1837 nsAttrValue::EnsureEmptyAtomArray()
1838 {
1839 if (Type() == eAtomArray) {
1840 ResetMiscAtomOrString();
1841 GetAtomArrayValue()->Clear();
1842 return true;
1843 }
1845 AtomArray* array = new AtomArray;
1846 if (!array) {
1847 Reset();
1848 return false;
1849 }
1851 MiscContainer* cont = EnsureEmptyMiscContainer();
1852 cont->mValue.mAtomArray = array;
1853 cont->mType = eAtomArray;
1855 return true;
1856 }
1858 already_AddRefed<nsStringBuffer>
1859 nsAttrValue::GetStringBuffer(const nsAString& aValue) const
1860 {
1861 uint32_t len = aValue.Length();
1862 if (!len) {
1863 return nullptr;
1864 }
1866 nsRefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aValue);
1867 if (buf && (buf->StorageSize()/sizeof(char16_t) - 1) == len) {
1868 return buf.forget();
1869 }
1871 buf = nsStringBuffer::Alloc((len + 1) * sizeof(char16_t));
1872 if (!buf) {
1873 return nullptr;
1874 }
1875 char16_t *data = static_cast<char16_t*>(buf->Data());
1876 CopyUnicodeTo(aValue, 0, data, len);
1877 data[len] = char16_t(0);
1878 return buf.forget();
1879 }
1881 int32_t
1882 nsAttrValue::StringToInteger(const nsAString& aValue, bool* aStrict,
1883 nsresult* aErrorCode,
1884 bool aCanBePercent,
1885 bool* aIsPercent) const
1886 {
1887 *aStrict = true;
1888 *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
1889 if (aCanBePercent) {
1890 *aIsPercent = false;
1891 }
1893 nsAString::const_iterator iter, end;
1894 aValue.BeginReading(iter);
1895 aValue.EndReading(end);
1897 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1898 *aStrict = false;
1899 ++iter;
1900 }
1902 if (iter == end) {
1903 return 0;
1904 }
1906 bool negate = false;
1907 if (*iter == char16_t('-')) {
1908 negate = true;
1909 ++iter;
1910 } else if (*iter == char16_t('+')) {
1911 *aStrict = false;
1912 ++iter;
1913 }
1915 int32_t value = 0;
1916 int32_t pValue = 0; // Previous value, used to check integer overflow
1917 while (iter != end) {
1918 if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
1919 value = (value * 10) + (*iter - char16_t('0'));
1920 ++iter;
1921 // Checking for integer overflow.
1922 if (pValue > value) {
1923 *aStrict = false;
1924 *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
1925 break;
1926 } else {
1927 pValue = value;
1928 *aErrorCode = NS_OK;
1929 }
1930 } else if (aCanBePercent && *iter == char16_t('%')) {
1931 ++iter;
1932 *aIsPercent = true;
1933 if (iter != end) {
1934 *aStrict = false;
1935 break;
1936 }
1937 } else {
1938 *aStrict = false;
1939 break;
1940 }
1941 }
1942 if (negate) {
1943 value = -value;
1944 // Checking the special case of -0.
1945 if (!value) {
1946 *aStrict = false;
1947 }
1948 }
1950 return value;
1951 }
1953 size_t
1954 nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
1955 {
1956 size_t n = 0;
1958 switch (BaseType()) {
1959 case eStringBase:
1960 {
1961 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
1962 n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0;
1963 break;
1964 }
1965 case eOtherBase:
1966 {
1967 MiscContainer* container = GetMiscContainer();
1968 if (!container) {
1969 break;
1970 }
1971 n += aMallocSizeOf(container);
1973 void* otherPtr = MISC_STR_PTR(container);
1974 // We only count the size of the object pointed by otherPtr if it's a
1975 // string. When it's an atom, it's counted separatly.
1976 if (otherPtr &&
1977 static_cast<ValueBaseType>(container->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == eStringBase) {
1978 nsStringBuffer* str = static_cast<nsStringBuffer*>(otherPtr);
1979 n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0;
1980 }
1982 if (Type() == eCSSStyleRule && container->mValue.mCSSStyleRule) {
1983 // TODO: mCSSStyleRule might be owned by another object which would
1984 // make us count them twice, bug 677493.
1985 //n += container->mCSSStyleRule->SizeOfIncludingThis(aMallocSizeOf);
1986 } else if (Type() == eAtomArray && container->mValue.mAtomArray) {
1987 // Don't measure each nsIAtom, they are measured separatly.
1988 n += container->mValue.mAtomArray->SizeOfIncludingThis(aMallocSizeOf);
1989 }
1990 break;
1991 }
1992 case eAtomBase: // Atoms are counted separately.
1993 case eIntegerBase: // The value is in mBits, nothing to do.
1994 break;
1995 }
1997 return n;
1998 }