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