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