Coverage Report

Created: 2018-09-25 14:53

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