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