content/base/src/nsAttrAndChildArray.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 * Storage of the children and attributes of a DOM node; storage for
michael@0 8 * the two is unified to minimize footprint.
michael@0 9 */
michael@0 10
michael@0 11 #include "nsAttrAndChildArray.h"
michael@0 12
michael@0 13 #include "mozilla/MathAlgorithms.h"
michael@0 14 #include "mozilla/MemoryReporting.h"
michael@0 15
michael@0 16 #include "nsMappedAttributeElement.h"
michael@0 17 #include "nsString.h"
michael@0 18 #include "nsHTMLStyleSheet.h"
michael@0 19 #include "nsRuleWalker.h"
michael@0 20 #include "nsMappedAttributes.h"
michael@0 21 #include "nsUnicharUtils.h"
michael@0 22 #include "nsAutoPtr.h"
michael@0 23 #include "nsContentUtils.h" // nsAutoScriptBlocker
michael@0 24
michael@0 25 /*
michael@0 26 CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer.
michael@0 27 It should be small enough to not cause collisions between adjecent arrays, and
michael@0 28 large enough to make sure that all indexes are used. The size below is based
michael@0 29 on the size of the smallest possible element (currently 24[*] bytes) which is
michael@0 30 the smallest distance between two nsAttrAndChildArray. 24/(2^_5_) is 0.75.
michael@0 31 This means that two adjacent nsAttrAndChildArrays will overlap one in 4 times.
michael@0 32 However not all elements will have enough children to get cached. And any
michael@0 33 allocator that doesn't return addresses aligned to 64 bytes will ensure that
michael@0 34 any index will get used.
michael@0 35
michael@0 36 [*] sizeof(Element) + 4 bytes for nsIDOMElement vtable pointer.
michael@0 37 */
michael@0 38
michael@0 39 #define CACHE_POINTER_SHIFT 5
michael@0 40 #define CACHE_NUM_SLOTS 128
michael@0 41 #define CACHE_CHILD_LIMIT 10
michael@0 42
michael@0 43 #define CACHE_GET_INDEX(_array) \
michael@0 44 ((NS_PTR_TO_INT32(_array) >> CACHE_POINTER_SHIFT) & \
michael@0 45 (CACHE_NUM_SLOTS - 1))
michael@0 46
michael@0 47 struct IndexCacheSlot
michael@0 48 {
michael@0 49 const nsAttrAndChildArray* array;
michael@0 50 int32_t index;
michael@0 51 };
michael@0 52
michael@0 53 // This is inited to all zeroes since it's static. Though even if it wasn't
michael@0 54 // the worst thing that'd happen is a small inefficency if you'd get a false
michael@0 55 // positive cachehit.
michael@0 56 static IndexCacheSlot indexCache[CACHE_NUM_SLOTS];
michael@0 57
michael@0 58 static
michael@0 59 inline
michael@0 60 void
michael@0 61 AddIndexToCache(const nsAttrAndChildArray* aArray, int32_t aIndex)
michael@0 62 {
michael@0 63 uint32_t ix = CACHE_GET_INDEX(aArray);
michael@0 64 indexCache[ix].array = aArray;
michael@0 65 indexCache[ix].index = aIndex;
michael@0 66 }
michael@0 67
michael@0 68 static
michael@0 69 inline
michael@0 70 int32_t
michael@0 71 GetIndexFromCache(const nsAttrAndChildArray* aArray)
michael@0 72 {
michael@0 73 uint32_t ix = CACHE_GET_INDEX(aArray);
michael@0 74 return indexCache[ix].array == aArray ? indexCache[ix].index : -1;
michael@0 75 }
michael@0 76
michael@0 77
michael@0 78 /**
michael@0 79 * Due to a compiler bug in VisualAge C++ for AIX, we need to return the
michael@0 80 * address of the first index into mBuffer here, instead of simply returning
michael@0 81 * mBuffer itself.
michael@0 82 *
michael@0 83 * See Bug 231104 for more information.
michael@0 84 */
michael@0 85 #define ATTRS(_impl) \
michael@0 86 reinterpret_cast<InternalAttr*>(&((_impl)->mBuffer[0]))
michael@0 87
michael@0 88
michael@0 89 #define NS_IMPL_EXTRA_SIZE \
michael@0 90 ((sizeof(Impl) - sizeof(mImpl->mBuffer)) / sizeof(void*))
michael@0 91
michael@0 92 nsAttrAndChildArray::nsAttrAndChildArray()
michael@0 93 : mImpl(nullptr)
michael@0 94 {
michael@0 95 }
michael@0 96
michael@0 97 nsAttrAndChildArray::~nsAttrAndChildArray()
michael@0 98 {
michael@0 99 if (!mImpl) {
michael@0 100 return;
michael@0 101 }
michael@0 102
michael@0 103 Clear();
michael@0 104
michael@0 105 moz_free(mImpl);
michael@0 106 }
michael@0 107
michael@0 108 nsIContent*
michael@0 109 nsAttrAndChildArray::GetSafeChildAt(uint32_t aPos) const
michael@0 110 {
michael@0 111 if (aPos < ChildCount()) {
michael@0 112 return ChildAt(aPos);
michael@0 113 }
michael@0 114
michael@0 115 return nullptr;
michael@0 116 }
michael@0 117
michael@0 118 nsIContent * const *
michael@0 119 nsAttrAndChildArray::GetChildArray(uint32_t* aChildCount) const
michael@0 120 {
michael@0 121 *aChildCount = ChildCount();
michael@0 122
michael@0 123 if (!*aChildCount) {
michael@0 124 return nullptr;
michael@0 125 }
michael@0 126
michael@0 127 return reinterpret_cast<nsIContent**>(mImpl->mBuffer + AttrSlotsSize());
michael@0 128 }
michael@0 129
michael@0 130 nsresult
michael@0 131 nsAttrAndChildArray::InsertChildAt(nsIContent* aChild, uint32_t aPos)
michael@0 132 {
michael@0 133 NS_ASSERTION(aChild, "nullchild");
michael@0 134 NS_ASSERTION(aPos <= ChildCount(), "out-of-bounds");
michael@0 135
michael@0 136 uint32_t offset = AttrSlotsSize();
michael@0 137 uint32_t childCount = ChildCount();
michael@0 138
michael@0 139 NS_ENSURE_TRUE(childCount < ATTRCHILD_ARRAY_MAX_CHILD_COUNT,
michael@0 140 NS_ERROR_FAILURE);
michael@0 141
michael@0 142 // First try to fit new child in existing childlist
michael@0 143 if (mImpl && offset + childCount < mImpl->mBufferSize) {
michael@0 144 void** pos = mImpl->mBuffer + offset + aPos;
michael@0 145 if (childCount != aPos) {
michael@0 146 memmove(pos + 1, pos, (childCount - aPos) * sizeof(nsIContent*));
michael@0 147 }
michael@0 148 SetChildAtPos(pos, aChild, aPos, childCount);
michael@0 149
michael@0 150 SetChildCount(childCount + 1);
michael@0 151
michael@0 152 return NS_OK;
michael@0 153 }
michael@0 154
michael@0 155 // Try to fit new child in existing buffer by compressing attrslots
michael@0 156 if (offset && !mImpl->mBuffer[offset - ATTRSIZE]) {
michael@0 157 // Compress away all empty slots while we're at it. This might not be the
michael@0 158 // optimal thing to do.
michael@0 159 uint32_t attrCount = NonMappedAttrCount();
michael@0 160 void** newStart = mImpl->mBuffer + attrCount * ATTRSIZE;
michael@0 161 void** oldStart = mImpl->mBuffer + offset;
michael@0 162 memmove(newStart, oldStart, aPos * sizeof(nsIContent*));
michael@0 163 memmove(&newStart[aPos + 1], &oldStart[aPos],
michael@0 164 (childCount - aPos) * sizeof(nsIContent*));
michael@0 165 SetChildAtPos(newStart + aPos, aChild, aPos, childCount);
michael@0 166
michael@0 167 SetAttrSlotAndChildCount(attrCount, childCount + 1);
michael@0 168
michael@0 169 return NS_OK;
michael@0 170 }
michael@0 171
michael@0 172 // We can't fit in current buffer, Realloc time!
michael@0 173 if (!GrowBy(1)) {
michael@0 174 return NS_ERROR_OUT_OF_MEMORY;
michael@0 175 }
michael@0 176
michael@0 177 void** pos = mImpl->mBuffer + offset + aPos;
michael@0 178 if (childCount != aPos) {
michael@0 179 memmove(pos + 1, pos, (childCount - aPos) * sizeof(nsIContent*));
michael@0 180 }
michael@0 181 SetChildAtPos(pos, aChild, aPos, childCount);
michael@0 182
michael@0 183 SetChildCount(childCount + 1);
michael@0 184
michael@0 185 return NS_OK;
michael@0 186 }
michael@0 187
michael@0 188 void
michael@0 189 nsAttrAndChildArray::RemoveChildAt(uint32_t aPos)
michael@0 190 {
michael@0 191 // Just store the return value of TakeChildAt in an nsCOMPtr to
michael@0 192 // trigger a release.
michael@0 193 nsCOMPtr<nsIContent> child = TakeChildAt(aPos);
michael@0 194 }
michael@0 195
michael@0 196 already_AddRefed<nsIContent>
michael@0 197 nsAttrAndChildArray::TakeChildAt(uint32_t aPos)
michael@0 198 {
michael@0 199 NS_ASSERTION(aPos < ChildCount(), "out-of-bounds");
michael@0 200
michael@0 201 uint32_t childCount = ChildCount();
michael@0 202 void** pos = mImpl->mBuffer + AttrSlotsSize() + aPos;
michael@0 203 nsIContent* child = static_cast<nsIContent*>(*pos);
michael@0 204 if (child->mPreviousSibling) {
michael@0 205 child->mPreviousSibling->mNextSibling = child->mNextSibling;
michael@0 206 }
michael@0 207 if (child->mNextSibling) {
michael@0 208 child->mNextSibling->mPreviousSibling = child->mPreviousSibling;
michael@0 209 }
michael@0 210 child->mPreviousSibling = child->mNextSibling = nullptr;
michael@0 211
michael@0 212 memmove(pos, pos + 1, (childCount - aPos - 1) * sizeof(nsIContent*));
michael@0 213 SetChildCount(childCount - 1);
michael@0 214
michael@0 215 return dont_AddRef(child);
michael@0 216 }
michael@0 217
michael@0 218 int32_t
michael@0 219 nsAttrAndChildArray::IndexOfChild(const nsINode* aPossibleChild) const
michael@0 220 {
michael@0 221 if (!mImpl) {
michael@0 222 return -1;
michael@0 223 }
michael@0 224 void** children = mImpl->mBuffer + AttrSlotsSize();
michael@0 225 // Use signed here since we compare count to cursor which has to be signed
michael@0 226 int32_t i, count = ChildCount();
michael@0 227
michael@0 228 if (count >= CACHE_CHILD_LIMIT) {
michael@0 229 int32_t cursor = GetIndexFromCache(this);
michael@0 230 // Need to compare to count here since we may have removed children since
michael@0 231 // the index was added to the cache.
michael@0 232 // We're also relying on that GetIndexFromCache returns -1 if no cached
michael@0 233 // index was found.
michael@0 234 if (cursor >= count) {
michael@0 235 cursor = -1;
michael@0 236 }
michael@0 237
michael@0 238 // Seek outward from the last found index. |inc| will change sign every
michael@0 239 // run through the loop. |sign| just exists to make sure the absolute
michael@0 240 // value of |inc| increases each time through.
michael@0 241 int32_t inc = 1, sign = 1;
michael@0 242 while (cursor >= 0 && cursor < count) {
michael@0 243 if (children[cursor] == aPossibleChild) {
michael@0 244 AddIndexToCache(this, cursor);
michael@0 245
michael@0 246 return cursor;
michael@0 247 }
michael@0 248
michael@0 249 cursor += inc;
michael@0 250 inc = -inc - sign;
michael@0 251 sign = -sign;
michael@0 252 }
michael@0 253
michael@0 254 // We ran into one 'edge'. Add inc to cursor once more to get back to
michael@0 255 // the 'side' where we still need to search, then step in the |sign|
michael@0 256 // direction.
michael@0 257 cursor += inc;
michael@0 258
michael@0 259 if (sign > 0) {
michael@0 260 for (; cursor < count; ++cursor) {
michael@0 261 if (children[cursor] == aPossibleChild) {
michael@0 262 AddIndexToCache(this, cursor);
michael@0 263
michael@0 264 return static_cast<int32_t>(cursor);
michael@0 265 }
michael@0 266 }
michael@0 267 }
michael@0 268 else {
michael@0 269 for (; cursor >= 0; --cursor) {
michael@0 270 if (children[cursor] == aPossibleChild) {
michael@0 271 AddIndexToCache(this, cursor);
michael@0 272
michael@0 273 return static_cast<int32_t>(cursor);
michael@0 274 }
michael@0 275 }
michael@0 276 }
michael@0 277
michael@0 278 // The child wasn't even in the remaining children
michael@0 279 return -1;
michael@0 280 }
michael@0 281
michael@0 282 for (i = 0; i < count; ++i) {
michael@0 283 if (children[i] == aPossibleChild) {
michael@0 284 return static_cast<int32_t>(i);
michael@0 285 }
michael@0 286 }
michael@0 287
michael@0 288 return -1;
michael@0 289 }
michael@0 290
michael@0 291 uint32_t
michael@0 292 nsAttrAndChildArray::AttrCount() const
michael@0 293 {
michael@0 294 return NonMappedAttrCount() + MappedAttrCount();
michael@0 295 }
michael@0 296
michael@0 297 const nsAttrValue*
michael@0 298 nsAttrAndChildArray::GetAttr(nsIAtom* aLocalName, int32_t aNamespaceID) const
michael@0 299 {
michael@0 300 uint32_t i, slotCount = AttrSlotCount();
michael@0 301 if (aNamespaceID == kNameSpaceID_None) {
michael@0 302 // This should be the common case so lets make an optimized loop
michael@0 303 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 304 if (ATTRS(mImpl)[i].mName.Equals(aLocalName)) {
michael@0 305 return &ATTRS(mImpl)[i].mValue;
michael@0 306 }
michael@0 307 }
michael@0 308
michael@0 309 if (mImpl && mImpl->mMappedAttrs) {
michael@0 310 return mImpl->mMappedAttrs->GetAttr(aLocalName);
michael@0 311 }
michael@0 312 }
michael@0 313 else {
michael@0 314 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 315 if (ATTRS(mImpl)[i].mName.Equals(aLocalName, aNamespaceID)) {
michael@0 316 return &ATTRS(mImpl)[i].mValue;
michael@0 317 }
michael@0 318 }
michael@0 319 }
michael@0 320
michael@0 321 return nullptr;
michael@0 322 }
michael@0 323
michael@0 324 const nsAttrValue*
michael@0 325 nsAttrAndChildArray::GetAttr(const nsAString& aLocalName) const
michael@0 326 {
michael@0 327 uint32_t i, slotCount = AttrSlotCount();
michael@0 328 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 329 if (ATTRS(mImpl)[i].mName.Equals(aLocalName)) {
michael@0 330 return &ATTRS(mImpl)[i].mValue;
michael@0 331 }
michael@0 332 }
michael@0 333
michael@0 334 if (mImpl && mImpl->mMappedAttrs) {
michael@0 335 return mImpl->mMappedAttrs->GetAttr(aLocalName);
michael@0 336 }
michael@0 337
michael@0 338 return nullptr;
michael@0 339 }
michael@0 340
michael@0 341 const nsAttrValue*
michael@0 342 nsAttrAndChildArray::GetAttr(const nsAString& aName,
michael@0 343 nsCaseTreatment aCaseSensitive) const
michael@0 344 {
michael@0 345 // Check whether someone is being silly and passing non-lowercase
michael@0 346 // attr names.
michael@0 347 if (aCaseSensitive == eIgnoreCase &&
michael@0 348 nsContentUtils::StringContainsASCIIUpper(aName)) {
michael@0 349 // Try again with a lowercased name, but make sure we can't reenter this
michael@0 350 // block by passing eCaseSensitive for aCaseSensitive.
michael@0 351 nsAutoString lowercase;
michael@0 352 nsContentUtils::ASCIIToLower(aName, lowercase);
michael@0 353 return GetAttr(lowercase, eCaseMatters);
michael@0 354 }
michael@0 355
michael@0 356 uint32_t i, slotCount = AttrSlotCount();
michael@0 357 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 358 if (ATTRS(mImpl)[i].mName.QualifiedNameEquals(aName)) {
michael@0 359 return &ATTRS(mImpl)[i].mValue;
michael@0 360 }
michael@0 361 }
michael@0 362
michael@0 363 if (mImpl && mImpl->mMappedAttrs) {
michael@0 364 const nsAttrValue* val =
michael@0 365 mImpl->mMappedAttrs->GetAttr(aName);
michael@0 366 if (val) {
michael@0 367 return val;
michael@0 368 }
michael@0 369 }
michael@0 370
michael@0 371 return nullptr;
michael@0 372 }
michael@0 373
michael@0 374 const nsAttrValue*
michael@0 375 nsAttrAndChildArray::AttrAt(uint32_t aPos) const
michael@0 376 {
michael@0 377 NS_ASSERTION(aPos < AttrCount(),
michael@0 378 "out-of-bounds access in nsAttrAndChildArray");
michael@0 379
michael@0 380 uint32_t mapped = MappedAttrCount();
michael@0 381 if (aPos < mapped) {
michael@0 382 return mImpl->mMappedAttrs->AttrAt(aPos);
michael@0 383 }
michael@0 384
michael@0 385 return &ATTRS(mImpl)[aPos - mapped].mValue;
michael@0 386 }
michael@0 387
michael@0 388 nsresult
michael@0 389 nsAttrAndChildArray::SetAndTakeAttr(nsIAtom* aLocalName, nsAttrValue& aValue)
michael@0 390 {
michael@0 391 uint32_t i, slotCount = AttrSlotCount();
michael@0 392 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 393 if (ATTRS(mImpl)[i].mName.Equals(aLocalName)) {
michael@0 394 ATTRS(mImpl)[i].mValue.Reset();
michael@0 395 ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
michael@0 396
michael@0 397 return NS_OK;
michael@0 398 }
michael@0 399 }
michael@0 400
michael@0 401 NS_ENSURE_TRUE(i < ATTRCHILD_ARRAY_MAX_ATTR_COUNT,
michael@0 402 NS_ERROR_FAILURE);
michael@0 403
michael@0 404 if (i == slotCount && !AddAttrSlot()) {
michael@0 405 return NS_ERROR_OUT_OF_MEMORY;
michael@0 406 }
michael@0 407
michael@0 408 new (&ATTRS(mImpl)[i].mName) nsAttrName(aLocalName);
michael@0 409 new (&ATTRS(mImpl)[i].mValue) nsAttrValue();
michael@0 410 ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
michael@0 411
michael@0 412 return NS_OK;
michael@0 413 }
michael@0 414
michael@0 415 nsresult
michael@0 416 nsAttrAndChildArray::SetAndTakeAttr(nsINodeInfo* aName, nsAttrValue& aValue)
michael@0 417 {
michael@0 418 int32_t namespaceID = aName->NamespaceID();
michael@0 419 nsIAtom* localName = aName->NameAtom();
michael@0 420 if (namespaceID == kNameSpaceID_None) {
michael@0 421 return SetAndTakeAttr(localName, aValue);
michael@0 422 }
michael@0 423
michael@0 424 uint32_t i, slotCount = AttrSlotCount();
michael@0 425 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 426 if (ATTRS(mImpl)[i].mName.Equals(localName, namespaceID)) {
michael@0 427 ATTRS(mImpl)[i].mName.SetTo(aName);
michael@0 428 ATTRS(mImpl)[i].mValue.Reset();
michael@0 429 ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
michael@0 430
michael@0 431 return NS_OK;
michael@0 432 }
michael@0 433 }
michael@0 434
michael@0 435 NS_ENSURE_TRUE(i < ATTRCHILD_ARRAY_MAX_ATTR_COUNT,
michael@0 436 NS_ERROR_FAILURE);
michael@0 437
michael@0 438 if (i == slotCount && !AddAttrSlot()) {
michael@0 439 return NS_ERROR_OUT_OF_MEMORY;
michael@0 440 }
michael@0 441
michael@0 442 new (&ATTRS(mImpl)[i].mName) nsAttrName(aName);
michael@0 443 new (&ATTRS(mImpl)[i].mValue) nsAttrValue();
michael@0 444 ATTRS(mImpl)[i].mValue.SwapValueWith(aValue);
michael@0 445
michael@0 446 return NS_OK;
michael@0 447 }
michael@0 448
michael@0 449
michael@0 450 nsresult
michael@0 451 nsAttrAndChildArray::RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue)
michael@0 452 {
michael@0 453 NS_ASSERTION(aPos < AttrCount(), "out-of-bounds");
michael@0 454
michael@0 455 uint32_t mapped = MappedAttrCount();
michael@0 456 if (aPos < mapped) {
michael@0 457 if (mapped == 1) {
michael@0 458 // We're removing the last mapped attribute. Can't swap in this
michael@0 459 // case; have to copy.
michael@0 460 aValue.SetTo(*mImpl->mMappedAttrs->AttrAt(0));
michael@0 461 NS_RELEASE(mImpl->mMappedAttrs);
michael@0 462
michael@0 463 return NS_OK;
michael@0 464 }
michael@0 465
michael@0 466 nsRefPtr<nsMappedAttributes> mapped =
michael@0 467 GetModifiableMapped(nullptr, nullptr, false);
michael@0 468
michael@0 469 mapped->RemoveAttrAt(aPos, aValue);
michael@0 470
michael@0 471 return MakeMappedUnique(mapped);
michael@0 472 }
michael@0 473
michael@0 474 aPos -= mapped;
michael@0 475 ATTRS(mImpl)[aPos].mValue.SwapValueWith(aValue);
michael@0 476 ATTRS(mImpl)[aPos].~InternalAttr();
michael@0 477
michael@0 478 uint32_t slotCount = AttrSlotCount();
michael@0 479 memmove(&ATTRS(mImpl)[aPos],
michael@0 480 &ATTRS(mImpl)[aPos + 1],
michael@0 481 (slotCount - aPos - 1) * sizeof(InternalAttr));
michael@0 482 memset(&ATTRS(mImpl)[slotCount - 1], 0, sizeof(InternalAttr));
michael@0 483
michael@0 484 return NS_OK;
michael@0 485 }
michael@0 486
michael@0 487 const nsAttrName*
michael@0 488 nsAttrAndChildArray::AttrNameAt(uint32_t aPos) const
michael@0 489 {
michael@0 490 NS_ASSERTION(aPos < AttrCount(),
michael@0 491 "out-of-bounds access in nsAttrAndChildArray");
michael@0 492
michael@0 493 uint32_t mapped = MappedAttrCount();
michael@0 494 if (aPos < mapped) {
michael@0 495 return mImpl->mMappedAttrs->NameAt(aPos);
michael@0 496 }
michael@0 497
michael@0 498 return &ATTRS(mImpl)[aPos - mapped].mName;
michael@0 499 }
michael@0 500
michael@0 501 const nsAttrName*
michael@0 502 nsAttrAndChildArray::GetSafeAttrNameAt(uint32_t aPos) const
michael@0 503 {
michael@0 504 uint32_t mapped = MappedAttrCount();
michael@0 505 if (aPos < mapped) {
michael@0 506 return mImpl->mMappedAttrs->NameAt(aPos);
michael@0 507 }
michael@0 508
michael@0 509 aPos -= mapped;
michael@0 510 if (aPos >= AttrSlotCount()) {
michael@0 511 return nullptr;
michael@0 512 }
michael@0 513
michael@0 514 void** pos = mImpl->mBuffer + aPos * ATTRSIZE;
michael@0 515 if (!*pos) {
michael@0 516 return nullptr;
michael@0 517 }
michael@0 518
michael@0 519 return &reinterpret_cast<InternalAttr*>(pos)->mName;
michael@0 520 }
michael@0 521
michael@0 522 const nsAttrName*
michael@0 523 nsAttrAndChildArray::GetExistingAttrNameFromQName(const nsAString& aName) const
michael@0 524 {
michael@0 525 uint32_t i, slotCount = AttrSlotCount();
michael@0 526 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 527 if (ATTRS(mImpl)[i].mName.QualifiedNameEquals(aName)) {
michael@0 528 return &ATTRS(mImpl)[i].mName;
michael@0 529 }
michael@0 530 }
michael@0 531
michael@0 532 if (mImpl && mImpl->mMappedAttrs) {
michael@0 533 return mImpl->mMappedAttrs->GetExistingAttrNameFromQName(aName);
michael@0 534 }
michael@0 535
michael@0 536 return nullptr;
michael@0 537 }
michael@0 538
michael@0 539 int32_t
michael@0 540 nsAttrAndChildArray::IndexOfAttr(nsIAtom* aLocalName, int32_t aNamespaceID) const
michael@0 541 {
michael@0 542 int32_t idx;
michael@0 543 if (mImpl && mImpl->mMappedAttrs && aNamespaceID == kNameSpaceID_None) {
michael@0 544 idx = mImpl->mMappedAttrs->IndexOfAttr(aLocalName);
michael@0 545 if (idx >= 0) {
michael@0 546 return idx;
michael@0 547 }
michael@0 548 }
michael@0 549
michael@0 550 uint32_t i;
michael@0 551 uint32_t mapped = MappedAttrCount();
michael@0 552 uint32_t slotCount = AttrSlotCount();
michael@0 553 if (aNamespaceID == kNameSpaceID_None) {
michael@0 554 // This should be the common case so lets make an optimized loop
michael@0 555 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 556 if (ATTRS(mImpl)[i].mName.Equals(aLocalName)) {
michael@0 557 return i + mapped;
michael@0 558 }
michael@0 559 }
michael@0 560 }
michael@0 561 else {
michael@0 562 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 563 if (ATTRS(mImpl)[i].mName.Equals(aLocalName, aNamespaceID)) {
michael@0 564 return i + mapped;
michael@0 565 }
michael@0 566 }
michael@0 567 }
michael@0 568
michael@0 569 return -1;
michael@0 570 }
michael@0 571
michael@0 572 nsresult
michael@0 573 nsAttrAndChildArray::SetAndTakeMappedAttr(nsIAtom* aLocalName,
michael@0 574 nsAttrValue& aValue,
michael@0 575 nsMappedAttributeElement* aContent,
michael@0 576 nsHTMLStyleSheet* aSheet)
michael@0 577 {
michael@0 578 bool willAdd = true;
michael@0 579 if (mImpl && mImpl->mMappedAttrs) {
michael@0 580 willAdd = !mImpl->mMappedAttrs->GetAttr(aLocalName);
michael@0 581 }
michael@0 582
michael@0 583 nsRefPtr<nsMappedAttributes> mapped =
michael@0 584 GetModifiableMapped(aContent, aSheet, willAdd);
michael@0 585
michael@0 586 mapped->SetAndTakeAttr(aLocalName, aValue);
michael@0 587
michael@0 588 return MakeMappedUnique(mapped);
michael@0 589 }
michael@0 590
michael@0 591 nsresult
michael@0 592 nsAttrAndChildArray::DoSetMappedAttrStyleSheet(nsHTMLStyleSheet* aSheet)
michael@0 593 {
michael@0 594 NS_PRECONDITION(mImpl && mImpl->mMappedAttrs,
michael@0 595 "Should have mapped attrs here!");
michael@0 596 if (aSheet == mImpl->mMappedAttrs->GetStyleSheet()) {
michael@0 597 return NS_OK;
michael@0 598 }
michael@0 599
michael@0 600 nsRefPtr<nsMappedAttributes> mapped =
michael@0 601 GetModifiableMapped(nullptr, nullptr, false);
michael@0 602
michael@0 603 mapped->SetStyleSheet(aSheet);
michael@0 604
michael@0 605 return MakeMappedUnique(mapped);
michael@0 606 }
michael@0 607
michael@0 608 void
michael@0 609 nsAttrAndChildArray::WalkMappedAttributeStyleRules(nsRuleWalker* aRuleWalker)
michael@0 610 {
michael@0 611 if (mImpl && mImpl->mMappedAttrs) {
michael@0 612 aRuleWalker->Forward(mImpl->mMappedAttrs);
michael@0 613 }
michael@0 614 }
michael@0 615
michael@0 616 void
michael@0 617 nsAttrAndChildArray::Compact()
michael@0 618 {
michael@0 619 if (!mImpl) {
michael@0 620 return;
michael@0 621 }
michael@0 622
michael@0 623 // First compress away empty attrslots
michael@0 624 uint32_t slotCount = AttrSlotCount();
michael@0 625 uint32_t attrCount = NonMappedAttrCount();
michael@0 626 uint32_t childCount = ChildCount();
michael@0 627
michael@0 628 if (attrCount < slotCount) {
michael@0 629 memmove(mImpl->mBuffer + attrCount * ATTRSIZE,
michael@0 630 mImpl->mBuffer + slotCount * ATTRSIZE,
michael@0 631 childCount * sizeof(nsIContent*));
michael@0 632 SetAttrSlotCount(attrCount);
michael@0 633 }
michael@0 634
michael@0 635 // Then resize or free buffer
michael@0 636 uint32_t newSize = attrCount * ATTRSIZE + childCount;
michael@0 637 if (!newSize && !mImpl->mMappedAttrs) {
michael@0 638 moz_free(mImpl);
michael@0 639 mImpl = nullptr;
michael@0 640 }
michael@0 641 else if (newSize < mImpl->mBufferSize) {
michael@0 642 mImpl = static_cast<Impl*>(moz_realloc(mImpl, (newSize + NS_IMPL_EXTRA_SIZE) * sizeof(nsIContent*)));
michael@0 643 NS_ASSERTION(mImpl, "failed to reallocate to smaller buffer");
michael@0 644
michael@0 645 mImpl->mBufferSize = newSize;
michael@0 646 }
michael@0 647 }
michael@0 648
michael@0 649 void
michael@0 650 nsAttrAndChildArray::Clear()
michael@0 651 {
michael@0 652 if (!mImpl) {
michael@0 653 return;
michael@0 654 }
michael@0 655
michael@0 656 if (mImpl->mMappedAttrs) {
michael@0 657 NS_RELEASE(mImpl->mMappedAttrs);
michael@0 658 }
michael@0 659
michael@0 660 uint32_t i, slotCount = AttrSlotCount();
michael@0 661 for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 662 ATTRS(mImpl)[i].~InternalAttr();
michael@0 663 }
michael@0 664
michael@0 665 nsAutoScriptBlocker scriptBlocker;
michael@0 666 uint32_t end = slotCount * ATTRSIZE + ChildCount();
michael@0 667 for (i = slotCount * ATTRSIZE; i < end; ++i) {
michael@0 668 nsIContent* child = static_cast<nsIContent*>(mImpl->mBuffer[i]);
michael@0 669 // making this false so tree teardown doesn't end up being
michael@0 670 // O(N*D) (number of nodes times average depth of tree).
michael@0 671 child->UnbindFromTree(false); // XXX is it better to let the owner do this?
michael@0 672 // Make sure to unlink our kids from each other, since someone
michael@0 673 // else could stil be holding references to some of them.
michael@0 674
michael@0 675 // XXXbz We probably can't push this assignment down into the |aNullParent|
michael@0 676 // case of UnbindFromTree because we still need the assignment in
michael@0 677 // RemoveChildAt. In particular, ContentRemoved fires between
michael@0 678 // RemoveChildAt and UnbindFromTree, and in ContentRemoved the sibling
michael@0 679 // chain needs to be correct. Though maybe we could set the prev and next
michael@0 680 // to point to each other but keep the kid being removed pointing to them
michael@0 681 // through ContentRemoved so consumers can find where it used to be in the
michael@0 682 // list?
michael@0 683 child->mPreviousSibling = child->mNextSibling = nullptr;
michael@0 684 NS_RELEASE(child);
michael@0 685 }
michael@0 686
michael@0 687 SetAttrSlotAndChildCount(0, 0);
michael@0 688 }
michael@0 689
michael@0 690 uint32_t
michael@0 691 nsAttrAndChildArray::NonMappedAttrCount() const
michael@0 692 {
michael@0 693 if (!mImpl) {
michael@0 694 return 0;
michael@0 695 }
michael@0 696
michael@0 697 uint32_t count = AttrSlotCount();
michael@0 698 while (count > 0 && !mImpl->mBuffer[(count - 1) * ATTRSIZE]) {
michael@0 699 --count;
michael@0 700 }
michael@0 701
michael@0 702 return count;
michael@0 703 }
michael@0 704
michael@0 705 uint32_t
michael@0 706 nsAttrAndChildArray::MappedAttrCount() const
michael@0 707 {
michael@0 708 return mImpl && mImpl->mMappedAttrs ? (uint32_t)mImpl->mMappedAttrs->Count() : 0;
michael@0 709 }
michael@0 710
michael@0 711 nsMappedAttributes*
michael@0 712 nsAttrAndChildArray::GetModifiableMapped(nsMappedAttributeElement* aContent,
michael@0 713 nsHTMLStyleSheet* aSheet,
michael@0 714 bool aWillAddAttr)
michael@0 715 {
michael@0 716 if (mImpl && mImpl->mMappedAttrs) {
michael@0 717 return mImpl->mMappedAttrs->Clone(aWillAddAttr);
michael@0 718 }
michael@0 719
michael@0 720 MOZ_ASSERT(aContent, "Trying to create modifiable without content");
michael@0 721
michael@0 722 nsMapRuleToAttributesFunc mapRuleFunc =
michael@0 723 aContent->GetAttributeMappingFunction();
michael@0 724 return new nsMappedAttributes(aSheet, mapRuleFunc);
michael@0 725 }
michael@0 726
michael@0 727 nsresult
michael@0 728 nsAttrAndChildArray::MakeMappedUnique(nsMappedAttributes* aAttributes)
michael@0 729 {
michael@0 730 NS_ASSERTION(aAttributes, "missing attributes");
michael@0 731
michael@0 732 if (!mImpl && !GrowBy(1)) {
michael@0 733 return NS_ERROR_OUT_OF_MEMORY;
michael@0 734 }
michael@0 735
michael@0 736 if (!aAttributes->GetStyleSheet()) {
michael@0 737 // This doesn't currently happen, but it could if we do loading right
michael@0 738
michael@0 739 nsRefPtr<nsMappedAttributes> mapped(aAttributes);
michael@0 740 mapped.swap(mImpl->mMappedAttrs);
michael@0 741
michael@0 742 return NS_OK;
michael@0 743 }
michael@0 744
michael@0 745 nsRefPtr<nsMappedAttributes> mapped =
michael@0 746 aAttributes->GetStyleSheet()->UniqueMappedAttributes(aAttributes);
michael@0 747 NS_ENSURE_TRUE(mapped, NS_ERROR_OUT_OF_MEMORY);
michael@0 748
michael@0 749 if (mapped != aAttributes) {
michael@0 750 // Reset the stylesheet of aAttributes so that it doesn't spend time
michael@0 751 // trying to remove itself from the hash. There is no risk that aAttributes
michael@0 752 // is in the hash since it will always have come from GetModifiableMapped,
michael@0 753 // which never returns maps that are in the hash (such hashes are by
michael@0 754 // nature not modifiable).
michael@0 755 aAttributes->DropStyleSheetReference();
michael@0 756 }
michael@0 757 mapped.swap(mImpl->mMappedAttrs);
michael@0 758
michael@0 759 return NS_OK;
michael@0 760 }
michael@0 761
michael@0 762
michael@0 763 bool
michael@0 764 nsAttrAndChildArray::GrowBy(uint32_t aGrowSize)
michael@0 765 {
michael@0 766 uint32_t size = mImpl ? mImpl->mBufferSize + NS_IMPL_EXTRA_SIZE : 0;
michael@0 767 uint32_t minSize = size + aGrowSize;
michael@0 768
michael@0 769 if (minSize <= ATTRCHILD_ARRAY_LINEAR_THRESHOLD) {
michael@0 770 do {
michael@0 771 size += ATTRCHILD_ARRAY_GROWSIZE;
michael@0 772 } while (size < minSize);
michael@0 773 }
michael@0 774 else {
michael@0 775 size = 1u << mozilla::CeilingLog2(minSize);
michael@0 776 }
michael@0 777
michael@0 778 bool needToInitialize = !mImpl;
michael@0 779 Impl* newImpl = static_cast<Impl*>(moz_realloc(mImpl, size * sizeof(void*)));
michael@0 780 NS_ENSURE_TRUE(newImpl, false);
michael@0 781
michael@0 782 mImpl = newImpl;
michael@0 783
michael@0 784 // Set initial counts if we didn't have a buffer before
michael@0 785 if (needToInitialize) {
michael@0 786 mImpl->mMappedAttrs = nullptr;
michael@0 787 SetAttrSlotAndChildCount(0, 0);
michael@0 788 }
michael@0 789
michael@0 790 mImpl->mBufferSize = size - NS_IMPL_EXTRA_SIZE;
michael@0 791
michael@0 792 return true;
michael@0 793 }
michael@0 794
michael@0 795 bool
michael@0 796 nsAttrAndChildArray::AddAttrSlot()
michael@0 797 {
michael@0 798 uint32_t slotCount = AttrSlotCount();
michael@0 799 uint32_t childCount = ChildCount();
michael@0 800
michael@0 801 // Grow buffer if needed
michael@0 802 if (!(mImpl && mImpl->mBufferSize >= (slotCount + 1) * ATTRSIZE + childCount) &&
michael@0 803 !GrowBy(ATTRSIZE)) {
michael@0 804 return false;
michael@0 805 }
michael@0 806 void** offset = mImpl->mBuffer + slotCount * ATTRSIZE;
michael@0 807
michael@0 808 if (childCount > 0) {
michael@0 809 memmove(&ATTRS(mImpl)[slotCount + 1], &ATTRS(mImpl)[slotCount],
michael@0 810 childCount * sizeof(nsIContent*));
michael@0 811 }
michael@0 812
michael@0 813 SetAttrSlotCount(slotCount + 1);
michael@0 814 offset[0] = nullptr;
michael@0 815 offset[1] = nullptr;
michael@0 816
michael@0 817 return true;
michael@0 818 }
michael@0 819
michael@0 820 inline void
michael@0 821 nsAttrAndChildArray::SetChildAtPos(void** aPos, nsIContent* aChild,
michael@0 822 uint32_t aIndex, uint32_t aChildCount)
michael@0 823 {
michael@0 824 NS_PRECONDITION(!aChild->GetNextSibling(), "aChild with next sibling?");
michael@0 825 NS_PRECONDITION(!aChild->GetPreviousSibling(), "aChild with prev sibling?");
michael@0 826
michael@0 827 *aPos = aChild;
michael@0 828 NS_ADDREF(aChild);
michael@0 829 if (aIndex != 0) {
michael@0 830 nsIContent* previous = static_cast<nsIContent*>(*(aPos - 1));
michael@0 831 aChild->mPreviousSibling = previous;
michael@0 832 previous->mNextSibling = aChild;
michael@0 833 }
michael@0 834 if (aIndex != aChildCount) {
michael@0 835 nsIContent* next = static_cast<nsIContent*>(*(aPos + 1));
michael@0 836 aChild->mNextSibling = next;
michael@0 837 next->mPreviousSibling = aChild;
michael@0 838 }
michael@0 839 }
michael@0 840
michael@0 841 size_t
michael@0 842 nsAttrAndChildArray::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
michael@0 843 {
michael@0 844 size_t n = 0;
michael@0 845 if (mImpl) {
michael@0 846 // Don't add the size taken by *mMappedAttrs because it's shared.
michael@0 847
michael@0 848 n += aMallocSizeOf(mImpl);
michael@0 849
michael@0 850 uint32_t slotCount = AttrSlotCount();
michael@0 851 for (uint32_t i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
michael@0 852 nsAttrValue* value = &ATTRS(mImpl)[i].mValue;
michael@0 853 n += value->SizeOfExcludingThis(aMallocSizeOf);
michael@0 854 }
michael@0 855 }
michael@0 856
michael@0 857 return n;
michael@0 858 }
michael@0 859

mercurial