content/base/src/nsAttrValue.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial