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