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
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/
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
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Original Author: David W. Hyatt (hyatt@netscape.com)
24 * Alec Flett <alecf@netscape.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 #include "nsIXBLService.h"
42 #include "nsIInputStream.h"
43 #include "nsDoubleHashtable.h"
46 #include "nsIChannel.h"
47 #include "nsXPIDLString.h"
48 #include "nsIParser.h"
49 #include "nsParserCIID.h"
50 #include "nsNetUtil.h"
52 #include "nsIContent.h"
53 #include "nsIDOMElement.h"
54 #include "nsIDocument.h"
55 #include "nsContentUtils.h"
56 #include "nsIPresShell.h"
57 #include "nsIXMLContentSink.h"
58 #include "nsContentCID.h"
59 #include "nsXMLDocument.h"
60 #include "nsIStreamListener.h"
62 #include "nsXBLBinding.h"
63 #include "nsXBLPrototypeBinding.h"
64 #include "nsIXBLDocumentInfo.h"
65 #include "nsXBLInsertionPoint.h"
67 #include "nsIStyleSheet.h"
68 #include "nsHTMLStyleSheet.h"
69 #include "nsIHTMLCSSStyleSheet.h"
71 #include "nsIStyleRuleProcessor.h"
72 #include "nsIWeakReference.h"
75 #include "nsIXPConnect.h"
77 #include "nsIDOMScriptObjectFactory.h"
78 #include "nsIScriptGlobalObject.h"
79 #include "nsTHashtable.h"
81 #include "nsIScriptContext.h"
82 #include "nsBindingManager.h"
84 #include "nsThreadUtils.h"
86 // ==================================================================
87 // = nsAnonymousContentList
88 // ==================================================================
90 #define NS_ANONYMOUS_CONTENT_LIST_IID \
91 { 0xa29df1f8, 0xaeca, 0x4356, \
92 { 0xa8, 0xc2, 0xa7, 0x24, 0xa2, 0x11, 0x73, 0xac } }
94 class nsAnonymousContentList
: public nsINodeList
97 nsAnonymousContentList(nsInsertionPointList
* aElements
);
98 virtual ~nsAnonymousContentList();
100 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
101 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAnonymousContentList
, nsINodeList
)
102 // nsIDOMNodeList interface
103 NS_DECL_NSIDOMNODELIST
105 // nsINodeList interface
106 virtual nsINode
* GetNodeAt(PRUint32 aIndex
);
108 PRInt32
GetInsertionPointCount() { return mElements
->Length(); }
110 nsXBLInsertionPoint
* GetInsertionPointAt(PRInt32 i
) { return static_cast<nsXBLInsertionPoint
*>(mElements
->ElementAt(i
)); }
111 void RemoveInsertionPointAt(PRInt32 i
) { mElements
->RemoveElementAt(i
); }
112 NS_DECLARE_STATIC_IID_ACCESSOR(NS_ANONYMOUS_CONTENT_LIST_IID
)
114 nsInsertionPointList
* mElements
;
117 NS_DEFINE_STATIC_IID_ACCESSOR(nsAnonymousContentList
,
118 NS_ANONYMOUS_CONTENT_LIST_IID
)
120 nsAnonymousContentList::nsAnonymousContentList(nsInsertionPointList
* aElements
)
121 : mElements(aElements
)
123 MOZ_COUNT_CTOR(nsAnonymousContentList
);
125 // We don't reference count our Anonymous reference (to avoid circular
126 // references). We'll be told when the Anonymous goes away.
129 nsAnonymousContentList::~nsAnonymousContentList()
131 MOZ_COUNT_DTOR(nsAnonymousContentList
);
135 NS_IMPL_CYCLE_COLLECTION_CLASS(nsAnonymousContentList
)
137 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList
)
138 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnonymousContentList
)
140 NS_INTERFACE_TABLE_HEAD(nsAnonymousContentList
)
141 NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsAnonymousContentList
)
142 NS_INTERFACE_TABLE_ENTRY(nsAnonymousContentList
, nsINodeList
)
143 NS_INTERFACE_TABLE_ENTRY(nsAnonymousContentList
, nsIDOMNodeList
)
144 NS_INTERFACE_TABLE_ENTRY(nsAnonymousContentList
, nsAnonymousContentList
)
145 NS_OFFSET_AND_INTERFACE_TABLE_END
146 NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
147 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(NodeList
)
148 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsAnonymousContentList
)
151 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsAnonymousContentList
)
152 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAnonymousContentList
)
154 PRInt32 i
, count
= tmp
->mElements
->Length();
155 for (i
= 0; i
< count
; ++i
) {
156 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mElements
->ElementAt(i
),
157 nsXBLInsertionPoint
);
160 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
163 nsAnonymousContentList::GetLength(PRUint32
* aLength
)
165 NS_ASSERTION(aLength
!= nsnull
, "null ptr");
167 return NS_ERROR_NULL_POINTER
;
169 PRInt32 cnt
= mElements
->Length();
172 for (PRInt32 i
= 0; i
< cnt
; i
++)
173 *aLength
+= static_cast<nsXBLInsertionPoint
*>(mElements
->ElementAt(i
))->ChildCount();
179 nsAnonymousContentList::Item(PRUint32 aIndex
, nsIDOMNode
** aReturn
)
181 nsINode
* item
= GetNodeAt(aIndex
);
183 return NS_ERROR_FAILURE
;
185 return CallQueryInterface(item
, aReturn
);
189 nsAnonymousContentList::GetNodeAt(PRUint32 aIndex
)
191 PRInt32 cnt
= mElements
->Length();
192 PRUint32 pointCount
= 0;
194 for (PRInt32 i
= 0; i
< cnt
; i
++) {
195 aIndex
-= pointCount
;
197 nsXBLInsertionPoint
* point
= static_cast<nsXBLInsertionPoint
*>(mElements
->ElementAt(i
));
198 pointCount
= point
->ChildCount();
200 if (aIndex
< pointCount
) {
201 return point
->ChildAt(aIndex
);
209 // Generic pldhash table stuff for mapping one nsISupports to another
211 // These values are never null - a null value implies that this
212 // whole key should be removed (See SetOrRemoveObject)
213 class ObjectEntry
: public PLDHashEntryHdr
217 // note that these are allocated within the PLDHashTable, but we
218 // want to keep track of them anyway
219 ObjectEntry() { MOZ_COUNT_CTOR(ObjectEntry
); }
220 ~ObjectEntry() { MOZ_COUNT_DTOR(ObjectEntry
); }
222 nsISupports
* GetValue() { return mValue
; }
223 nsISupports
* GetKey() { return mKey
; }
224 void SetValue(nsISupports
* aValue
) { mValue
= aValue
; }
225 void SetKey(nsISupports
* aKey
) { mKey
= aKey
; }
228 nsCOMPtr
<nsISupports
> mKey
;
229 nsCOMPtr
<nsISupports
> mValue
;
233 ClearObjectEntry(PLDHashTable
* table
, PLDHashEntryHdr
*entry
)
235 ObjectEntry
* objEntry
= static_cast<ObjectEntry
*>(entry
);
236 objEntry
->~ObjectEntry();
240 InitObjectEntry(PLDHashTable
* table
, PLDHashEntryHdr
* entry
, const void* key
)
242 new (entry
) ObjectEntry
;
248 static PLDHashTableOps ObjectTableOps
= {
251 PL_DHashVoidPtrKeyStub
,
252 PL_DHashMatchEntryStub
,
253 PL_DHashMoveEntryStub
,
255 PL_DHashFinalizeStub
,
259 // helper routine for adding a new entry
261 AddObjectEntry(PLDHashTable
& table
, nsISupports
* aKey
, nsISupports
* aValue
)
263 NS_ASSERTION(aKey
, "key must be non-null");
264 if (!aKey
) return NS_ERROR_INVALID_ARG
;
267 static_cast<ObjectEntry
*>
268 (PL_DHashTableOperate(&table
, aKey
, PL_DHASH_ADD
));
271 return NS_ERROR_OUT_OF_MEMORY
;
273 // only add the key if the entry is new
274 if (!entry
->GetKey())
277 // now attach the new entry - note that entry->mValue could possibly
278 // have a value already, this will release that.
279 entry
->SetValue(aValue
);
284 // helper routine for looking up an existing entry. Note that the
285 // return result is NOT addreffed
287 LookupObject(PLDHashTable
& table
, nsIContent
* aKey
)
289 if (aKey
&& aKey
->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
291 static_cast<ObjectEntry
*>
292 (PL_DHashTableOperate(&table
, aKey
, PL_DHASH_LOOKUP
));
294 if (PL_DHASH_ENTRY_IS_BUSY(entry
))
295 return entry
->GetValue();
302 RemoveObjectEntry(PLDHashTable
& table
, nsISupports
* aKey
)
304 PL_DHashTableOperate(&table
, aKey
, PL_DHASH_REMOVE
);
308 SetOrRemoveObject(PLDHashTable
& table
, nsIContent
* aKey
, nsISupports
* aValue
)
311 // lazily create the table, but only when adding elements
313 !PL_DHashTableInit(&table
, &ObjectTableOps
, nsnull
,
314 sizeof(ObjectEntry
), 16)) {
316 return NS_ERROR_OUT_OF_MEMORY
;
318 aKey
->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR
);
319 return AddObjectEntry(table
, aKey
, aValue
);
322 // no value, so remove the key from the table
325 static_cast<ObjectEntry
*>
326 (PL_DHashTableOperate(&table
, aKey
, PL_DHASH_LOOKUP
));
327 if (entry
&& PL_DHASH_ENTRY_IS_BUSY(entry
)) {
328 // Keep key and value alive while removing the entry.
329 nsCOMPtr
<nsISupports
> key
= entry
->GetKey();
330 nsCOMPtr
<nsISupports
> value
= entry
->GetValue();
331 RemoveObjectEntry(table
, aKey
);
337 // Implementation /////////////////////////////////////////////////////////////////
339 // Static member variable initialization
341 // Implement our nsISupports methods
343 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager
)
344 tmp
->mDestroyed
= PR_TRUE
;
346 if (tmp
->mBindingTable
.IsInitialized())
347 tmp
->mBindingTable
.Clear();
349 if (tmp
->mDocumentTable
.IsInitialized())
350 tmp
->mDocumentTable
.Clear();
352 if (tmp
->mLoadingDocTable
.IsInitialized())
353 tmp
->mLoadingDocTable
.Clear();
355 if (tmp
->mContentListTable
.ops
)
356 PL_DHashTableFinish(&(tmp
->mContentListTable
));
357 tmp
->mContentListTable
.ops
= nsnull
;
359 if (tmp
->mAnonymousNodesTable
.ops
)
360 PL_DHashTableFinish(&(tmp
->mAnonymousNodesTable
));
361 tmp
->mAnonymousNodesTable
.ops
= nsnull
;
363 if (tmp
->mInsertionParentTable
.ops
)
364 PL_DHashTableFinish(&(tmp
->mInsertionParentTable
));
365 tmp
->mInsertionParentTable
.ops
= nsnull
;
367 if (tmp
->mWrapperTable
.ops
)
368 PL_DHashTableFinish(&(tmp
->mWrapperTable
));
369 tmp
->mWrapperTable
.ops
= nsnull
;
371 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mAttachedStack
)
372 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
375 static PLDHashOperator
376 DocumentInfoHashtableTraverser(nsIURI
* key
,
377 nsIXBLDocumentInfo
* di
,
380 nsCycleCollectionTraversalCallback
*cb
=
381 static_cast<nsCycleCollectionTraversalCallback
*>(userArg
);
382 cb
->NoteXPCOMChild(di
);
383 return PL_DHASH_NEXT
;
386 static PLDHashOperator
387 LoadingDocHashtableTraverser(nsIURI
* key
,
388 nsIStreamListener
* sl
,
391 nsCycleCollectionTraversalCallback
*cb
=
392 static_cast<nsCycleCollectionTraversalCallback
*>(userArg
);
393 cb
->NoteXPCOMChild(sl
);
394 return PL_DHASH_NEXT
;
397 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager
)
398 // The hashes keyed on nsIContent are traversed from the nsIContent itself.
399 if (tmp
->mDocumentTable
.IsInitialized())
400 tmp
->mDocumentTable
.EnumerateRead(&DocumentInfoHashtableTraverser
, &cb
);
401 if (tmp
->mLoadingDocTable
.IsInitialized())
402 tmp
->mLoadingDocTable
.EnumerateRead(&LoadingDocHashtableTraverser
, &cb
);
403 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mAttachedStack
,
405 // No need to traverse mProcessAttachedQueueEvent, since it'll just
406 // fire at some point.
407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
409 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager
)
411 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager
)
412 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
413 NS_INTERFACE_MAP_ENTRY(nsISupports
)
416 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager
)
417 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager
)
419 // Constructors/Destructors
420 nsBindingManager::nsBindingManager(nsIDocument
* aDocument
)
421 : mProcessingAttachedStack(PR_FALSE
),
422 mDestroyed(PR_FALSE
),
423 mAttachedStackSizeOnOutermost(0),
426 mContentListTable
.ops
= nsnull
;
427 mAnonymousNodesTable
.ops
= nsnull
;
428 mInsertionParentTable
.ops
= nsnull
;
429 mWrapperTable
.ops
= nsnull
;
432 nsBindingManager::~nsBindingManager(void)
434 mDestroyed
= PR_TRUE
;
436 if (mContentListTable
.ops
)
437 PL_DHashTableFinish(&mContentListTable
);
438 if (mAnonymousNodesTable
.ops
)
439 PL_DHashTableFinish(&mAnonymousNodesTable
);
440 NS_ASSERTION(!mInsertionParentTable
.ops
|| !mInsertionParentTable
.entryCount
,
441 "Insertion parent table isn't empty!");
442 if (mInsertionParentTable
.ops
)
443 PL_DHashTableFinish(&mInsertionParentTable
);
444 if (mWrapperTable
.ops
)
445 PL_DHashTableFinish(&mWrapperTable
);
449 RemoveInsertionParentCB(PLDHashTable
* aTable
, PLDHashEntryHdr
* aEntry
,
450 PRUint32 aNumber
, void* aArg
)
452 return (static_cast<ObjectEntry
*>(aEntry
)->GetValue() ==
453 static_cast<nsISupports
*>(aArg
)) ? PL_DHASH_REMOVE
: PL_DHASH_NEXT
;
457 RemoveInsertionParentForNodeList(nsIDOMNodeList
* aList
, nsIContent
* aParent
)
459 nsAnonymousContentList
* list
= nsnull
;
461 CallQueryInterface(aList
, &list
);
464 PRInt32 count
= list
->GetInsertionPointCount();
465 for (PRInt32 i
= 0; i
< count
; ++i
) {
466 nsRefPtr
<nsXBLInsertionPoint
> currPoint
= list
->GetInsertionPointAt(i
);
467 currPoint
->UnbindDefaultContent();
469 nsCOMPtr
<nsIContent
> parent
= currPoint
->GetInsertionParent();
470 NS_ASSERTION(!parent
|| parent
== aParent
, "Wrong insertion parent!");
472 currPoint
->ClearInsertionParent();
479 nsBindingManager::RemoveInsertionParent(nsIContent
* aParent
)
481 nsCOMPtr
<nsIDOMNodeList
> contentlist
;
482 GetContentListFor(aParent
, getter_AddRefs(contentlist
));
483 RemoveInsertionParentForNodeList(contentlist
, aParent
);
485 nsCOMPtr
<nsIDOMNodeList
> anonnodes
;
486 GetAnonymousNodesFor(aParent
, getter_AddRefs(anonnodes
));
487 RemoveInsertionParentForNodeList(anonnodes
, aParent
);
489 if (mInsertionParentTable
.ops
) {
490 PL_DHashTableEnumerate(&mInsertionParentTable
, RemoveInsertionParentCB
,
491 static_cast<nsISupports
*>(aParent
));
496 nsBindingManager::GetBinding(nsIContent
* aContent
)
498 if (aContent
&& aContent
->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
) &&
499 mBindingTable
.IsInitialized()) {
500 return mBindingTable
.GetWeak(aContent
);
507 nsBindingManager::SetBinding(nsIContent
* aContent
, nsXBLBinding
* aBinding
)
509 if (!mBindingTable
.IsInitialized()) {
510 if (!mBindingTable
.Init())
511 return NS_ERROR_OUT_OF_MEMORY
;
514 // After this point, aBinding will be the most-derived binding for aContent.
515 // If we already have a binding for aContent in our table, make sure to
516 // remove it from the attached stack. Otherwise we might end up firing its
517 // constructor twice (if aBinding inherits from it) or firing its constructor
518 // after aContent has been deleted (if aBinding is null and the content node
519 // dies before we process mAttachedStack).
520 nsRefPtr
<nsXBLBinding
> oldBinding
= GetBinding(aContent
);
522 if (aContent
->HasFlag(NODE_IS_INSERTION_PARENT
)) {
523 nsRefPtr
<nsXBLBinding
> parentBinding
=
524 GetBinding(aContent
->GetBindingParent());
525 // Clear insertion parent only if we don't have a parent binding which
526 // marked content to be an insertion parent. See also ChangeDocumentFor().
527 if (!parentBinding
|| !parentBinding
->HasInsertionParent(aContent
)) {
528 RemoveInsertionParent(aContent
);
529 aContent
->UnsetFlags(NODE_IS_INSERTION_PARENT
);
532 // Don't remove items here as that could mess up an executing
533 // ProcessAttachedQueue
534 PRUint32 index
= mAttachedStack
.IndexOf(oldBinding
);
535 if (index
!= mAttachedStack
.NoIndex
) {
536 mAttachedStack
[index
] = nsnull
;
540 PRBool result
= PR_TRUE
;
543 aContent
->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR
);
544 result
= mBindingTable
.Put(aContent
, aBinding
);
546 mBindingTable
.Remove(aContent
);
548 // The death of the bindings means the death of the JS wrapper,
549 // and the flushing of our explicit and anonymous insertion point
551 SetWrappedJS(aContent
, nsnull
);
552 SetContentListFor(aContent
, nsnull
);
553 SetAnonymousNodesFor(aContent
, nsnull
);
556 return result
? NS_OK
: NS_ERROR_FAILURE
;
560 nsBindingManager::GetInsertionParent(nsIContent
* aContent
)
562 if (mInsertionParentTable
.ops
) {
563 return static_cast<nsIContent
*>
564 (LookupObject(mInsertionParentTable
, aContent
));
571 nsBindingManager::SetInsertionParent(nsIContent
* aContent
, nsIContent
* aParent
)
573 NS_ASSERTION(!aParent
|| aParent
->HasFlag(NODE_IS_INSERTION_PARENT
),
574 "Insertion parent should have NODE_IS_INSERTION_PARENT flag!");
580 return SetOrRemoveObject(mInsertionParentTable
, aContent
, aParent
);
583 nsIXPConnectWrappedJS
*
584 nsBindingManager::GetWrappedJS(nsIContent
* aContent
)
586 if (mWrapperTable
.ops
) {
587 return static_cast<nsIXPConnectWrappedJS
*>(LookupObject(mWrapperTable
, aContent
));
594 nsBindingManager::SetWrappedJS(nsIContent
* aContent
, nsIXPConnectWrappedJS
* aWrappedJS
)
600 return SetOrRemoveObject(mWrapperTable
, aContent
, aWrappedJS
);
604 nsBindingManager::ChangeDocumentFor(nsIContent
* aContent
, nsIDocument
* aOldDocument
,
605 nsIDocument
* aNewDocument
)
607 // XXXbz this code is pretty broken, since moving from one document
608 // to another always passes through a null document!
609 NS_PRECONDITION(aOldDocument
!= nsnull
, "no old document");
610 NS_PRECONDITION(!aNewDocument
,
611 "Changing to a non-null new document not supported yet");
613 return NS_ERROR_NULL_POINTER
;
615 // Hold a ref to the binding so it won't die when we remove it from our
617 nsRefPtr
<nsXBLBinding
> binding
= GetBinding(aContent
);
618 if (aContent
->HasFlag(NODE_IS_INSERTION_PARENT
)) {
619 nsRefPtr
<nsXBLBinding
> parentBinding
= GetBinding(aContent
->GetBindingParent());
621 parentBinding
->RemoveInsertionParent(aContent
);
622 // Clear insertion parent only if we don't have a binding which
623 // marked content to be an insertion parent. See also SetBinding().
624 if (!binding
|| !binding
->HasInsertionParent(aContent
)) {
625 RemoveInsertionParent(aContent
);
626 aContent
->UnsetFlags(NODE_IS_INSERTION_PARENT
);
632 binding
->ChangeDocument(aOldDocument
, aNewDocument
);
633 SetBinding(aContent
, nsnull
);
635 aNewDocument
->BindingManager()->SetBinding(aContent
, binding
);
638 // Clear out insertion parents and content lists.
639 SetInsertionParent(aContent
, nsnull
);
640 SetContentListFor(aContent
, nsnull
);
641 SetAnonymousNodesFor(aContent
, nsnull
);
647 nsBindingManager::ResolveTag(nsIContent
* aContent
, PRInt32
* aNameSpaceID
)
649 nsXBLBinding
*binding
= GetBinding(aContent
);
652 nsIAtom
* base
= binding
->GetBaseTag(aNameSpaceID
);
659 *aNameSpaceID
= aContent
->GetNameSpaceID();
660 return aContent
->Tag();
664 nsBindingManager::GetContentListFor(nsIContent
* aContent
, nsIDOMNodeList
** aResult
)
666 // Locate the primary binding and get its node list of anonymous children.
669 if (mContentListTable
.ops
) {
670 *aResult
= static_cast<nsIDOMNodeList
*>
671 (LookupObject(mContentListTable
, aContent
));
672 NS_IF_ADDREF(*aResult
);
676 nsCOMPtr
<nsIDOMNode
> node(do_QueryInterface(aContent
));
677 node
->GetChildNodes(aResult
);
684 nsBindingManager::SetContentListFor(nsIContent
* aContent
,
685 nsInsertionPointList
* aList
)
691 nsIDOMNodeList
* contentList
= nsnull
;
693 contentList
= new nsAnonymousContentList(aList
);
696 return NS_ERROR_OUT_OF_MEMORY
;
700 return SetOrRemoveObject(mContentListTable
, aContent
, contentList
);
704 nsBindingManager::HasContentListFor(nsIContent
* aContent
)
706 return mContentListTable
.ops
&& LookupObject(mContentListTable
, aContent
);
710 nsBindingManager::GetAnonymousNodesInternal(nsIContent
* aContent
,
711 nsIDOMNodeList
** aResult
,
712 PRBool
* aIsAnonymousContentList
)
714 // Locate the primary binding and get its node list of anonymous children.
716 if (mAnonymousNodesTable
.ops
) {
717 *aResult
= static_cast<nsIDOMNodeList
*>
718 (LookupObject(mAnonymousNodesTable
, aContent
));
719 NS_IF_ADDREF(*aResult
);
723 *aIsAnonymousContentList
= PR_FALSE
;
724 nsXBLBinding
*binding
= GetBinding(aContent
);
726 *aResult
= binding
->GetAnonymousNodes().get();
730 *aIsAnonymousContentList
= PR_TRUE
;
736 nsBindingManager::GetAnonymousNodesFor(nsIContent
* aContent
,
737 nsIDOMNodeList
** aResult
)
740 return GetAnonymousNodesInternal(aContent
, aResult
, &dummy
);
744 nsBindingManager::SetAnonymousNodesFor(nsIContent
* aContent
,
745 nsInsertionPointList
* aList
)
751 nsIDOMNodeList
* contentList
= nsnull
;
753 contentList
= new nsAnonymousContentList(aList
);
756 return NS_ERROR_OUT_OF_MEMORY
;
760 return SetOrRemoveObject(mAnonymousNodesTable
, aContent
, contentList
);
764 nsBindingManager::GetXBLChildNodesInternal(nsIContent
* aContent
,
765 nsIDOMNodeList
** aResult
,
766 PRBool
* aIsAnonymousContentList
)
772 // Retrieve the anonymous content that we should build.
773 nsCOMPtr
<nsIDOMNodeList
> result
;
774 GetAnonymousNodesInternal(aContent
, getter_AddRefs(result
),
775 aIsAnonymousContentList
);
777 result
->GetLength(&length
);
782 // We may have an altered list of children from XBL insertion points.
783 // If we don't have any anonymous kids, we next check to see if we have
786 if (mContentListTable
.ops
) {
787 result
= static_cast<nsIDOMNodeList
*>
788 (LookupObject(mContentListTable
, aContent
));
789 *aIsAnonymousContentList
= PR_TRUE
;
793 result
.swap(*aResult
);
799 nsBindingManager::GetXBLChildNodesFor(nsIContent
* aContent
, nsIDOMNodeList
** aResult
)
802 return GetXBLChildNodesInternal(aContent
, aResult
, &dummy
);
806 nsBindingManager::GetInsertionPoint(nsIContent
* aParent
, nsIContent
* aChild
,
809 nsXBLBinding
*binding
= GetBinding(aParent
);
810 return binding
? binding
->GetInsertionPoint(aChild
, aIndex
) : nsnull
;
814 nsBindingManager::GetSingleInsertionPoint(nsIContent
* aParent
,
816 PRBool
* aMultipleInsertionPoints
)
818 nsXBLBinding
*binding
= GetBinding(aParent
);
820 return binding
->GetSingleInsertionPoint(aIndex
, aMultipleInsertionPoints
);
822 *aMultipleInsertionPoints
= PR_FALSE
;
827 nsBindingManager::AddLayeredBinding(nsIContent
* aContent
, nsIURI
* aURL
,
828 nsIPrincipal
* aOriginPrincipal
)
830 // First we need to load our binding.
832 nsCOMPtr
<nsIXBLService
> xblService
=
833 do_GetService("@mozilla.org/xbl;1", &rv
);
837 // Load the bindings.
838 nsRefPtr
<nsXBLBinding
> binding
;
840 xblService
->LoadBindings(aContent
, aURL
, aOriginPrincipal
, PR_TRUE
,
841 getter_AddRefs(binding
), &dummy
);
843 AddToAttachedQueue(binding
);
844 ProcessAttachedQueue();
851 nsBindingManager::RemoveLayeredBinding(nsIContent
* aContent
, nsIURI
* aURL
)
853 // Hold a ref to the binding so it won't die when we remove it from our table
854 nsRefPtr
<nsXBLBinding
> binding
= GetBinding(aContent
);
860 // For now we can only handle removing a binding if it's the only one
861 NS_ENSURE_FALSE(binding
->GetBaseBinding(), NS_ERROR_FAILURE
);
863 // Make sure that the binding has the URI that is requested to be removed
864 nsIURI
* bindingUri
= binding
->PrototypeBinding()->BindingURI();
867 nsresult rv
= aURL
->Equals(bindingUri
, &equalUri
);
868 NS_ENSURE_SUCCESS(rv
, rv
);
873 // Make sure it isn't a style binding
874 if (binding
->IsStyleBinding()) {
878 // Hold strong ref in case removing the binding tries to close the
879 // window or something.
880 // XXXbz should that be ownerdoc? Wouldn't we need a ref to the
881 // currentdoc too? What's the one that should be passed to
883 nsCOMPtr
<nsIDocument
> doc
= aContent
->GetOwnerDoc();
884 NS_ASSERTION(doc
, "No owner document?");
886 // Finally remove the binding...
887 // XXXbz this doesn't remove the implementation! Should fix! Until
888 // then we need the explicit UnhookEventHandlers here.
889 binding
->UnhookEventHandlers();
890 binding
->ChangeDocument(doc
, nsnull
);
891 SetBinding(aContent
, nsnull
);
892 binding
->MarkForDeath();
894 // ...and recreate it's frames. We need to do this since the frames may have
895 // been removed and style may have changed due to the removal of the
896 // anonymous children.
897 // XXXbz this should be using the current doc (if any), not the owner doc.
898 nsIPresShell
*presShell
= doc
->GetPrimaryShell();
899 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
901 return presShell
->RecreateFramesFor(aContent
);;
905 nsBindingManager::LoadBindingDocument(nsIDocument
* aBoundDoc
,
907 nsIPrincipal
* aOriginPrincipal
)
909 NS_PRECONDITION(aURL
, "Must have a URI to load!");
911 // First we need to load our binding.
913 nsCOMPtr
<nsIXBLService
> xblService
=
914 do_GetService("@mozilla.org/xbl;1", &rv
);
918 // Load the binding doc.
919 nsCOMPtr
<nsIXBLDocumentInfo
> info
;
920 xblService
->LoadBindingDocumentInfo(nsnull
, aBoundDoc
, aURL
,
921 aOriginPrincipal
, PR_TRUE
,
922 getter_AddRefs(info
));
924 return NS_ERROR_FAILURE
;
930 nsBindingManager::AddToAttachedQueue(nsXBLBinding
* aBinding
)
932 if (!mAttachedStack
.AppendElement(aBinding
))
933 return NS_ERROR_OUT_OF_MEMORY
;
935 // If we're in the middle of processing our queue already, don't
936 // bother posting the event.
937 if (!mProcessingAttachedStack
&& !mProcessAttachedQueueEvent
) {
938 PostProcessAttachedQueueEvent();
946 nsBindingManager::PostProcessAttachedQueueEvent()
948 mProcessAttachedQueueEvent
=
949 new nsRunnableMethod
<nsBindingManager
>(
950 this, &nsBindingManager::DoProcessAttachedQueue
);
951 nsresult rv
= NS_DispatchToCurrentThread(mProcessAttachedQueueEvent
);
952 if (NS_SUCCEEDED(rv
) && mDocument
) {
953 mDocument
->BlockOnload();
958 nsBindingManager::DoProcessAttachedQueue()
960 if (!mProcessingAttachedStack
) {
961 ProcessAttachedQueue();
963 NS_ASSERTION(mAttachedStack
.Length() == 0,
964 "Shouldn't have pending bindings!");
966 mProcessAttachedQueueEvent
= nsnull
;
968 // Someone's doing event processing from inside a constructor.
969 // They're evil, but we'll fight back! Just poll on them being
970 // done and repost the attached queue event.
971 PostProcessAttachedQueueEvent();
974 // No matter what, unblock onload for the event that's fired.
976 // Hold a strong reference while calling UnblockOnload since that might
978 nsCOMPtr
<nsIDocument
> doc
= mDocument
;
979 doc
->UnblockOnload(PR_TRUE
);
984 nsBindingManager::ProcessAttachedQueue(PRUint32 aSkipSize
)
986 if (mProcessingAttachedStack
|| mAttachedStack
.Length() <= aSkipSize
)
989 mProcessingAttachedStack
= PR_TRUE
;
991 // Excute constructors. Do this from high index to low
992 while (mAttachedStack
.Length() > aSkipSize
) {
993 PRUint32 lastItem
= mAttachedStack
.Length() - 1;
994 nsRefPtr
<nsXBLBinding
> binding
= mAttachedStack
.ElementAt(lastItem
);
995 mAttachedStack
.RemoveElementAt(lastItem
);
997 binding
->ExecuteAttachedHandler();
1001 // If NodeWillBeDestroyed has run we don't want to clobber
1002 // mProcessingAttachedStack set there.
1004 mProcessingAttachedStack
= PR_FALSE
;
1007 NS_ASSERTION(mAttachedStack
.Length() == aSkipSize
, "How did we get here?");
1009 mAttachedStack
.Compact();
1012 // Keep bindings and bound elements alive while executing detached handlers.
1013 struct BindingTableReadClosure
1015 nsCOMArray
<nsIContent
> mBoundElements
;
1016 nsBindingList mBindings
;
1019 static PLDHashOperator
1020 AccumulateBindingsToDetach(nsISupports
*aKey
, nsXBLBinding
*aBinding
,
1023 BindingTableReadClosure
* closure
=
1024 static_cast<BindingTableReadClosure
*>(aClosure
);
1025 if (aBinding
&& closure
->mBindings
.AppendElement(aBinding
)) {
1026 if (!closure
->mBoundElements
.AppendObject(aBinding
->GetBoundElement())) {
1027 closure
->mBindings
.RemoveElementAt(closure
->mBindings
.Length() - 1);
1030 return PL_DHASH_NEXT
;
1034 nsBindingManager::ExecuteDetachedHandlers()
1036 // Walk our hashtable of bindings.
1037 if (mBindingTable
.IsInitialized()) {
1038 BindingTableReadClosure closure
;
1039 mBindingTable
.EnumerateRead(AccumulateBindingsToDetach
, &closure
);
1040 PRUint32 i
, count
= closure
.mBindings
.Length();
1041 for (i
= 0; i
< count
; ++i
) {
1042 closure
.mBindings
[i
]->ExecuteDetachedHandler();
1048 nsBindingManager::PutXBLDocumentInfo(nsIXBLDocumentInfo
* aDocumentInfo
)
1050 NS_PRECONDITION(aDocumentInfo
, "Must have a non-null documentinfo!");
1052 NS_ENSURE_TRUE(mDocumentTable
.IsInitialized() || mDocumentTable
.Init(16),
1053 NS_ERROR_OUT_OF_MEMORY
);
1055 NS_ENSURE_TRUE(mDocumentTable
.Put(aDocumentInfo
->DocumentURI(),
1057 NS_ERROR_OUT_OF_MEMORY
);
1063 nsBindingManager::RemoveXBLDocumentInfo(nsIXBLDocumentInfo
* aDocumentInfo
)
1065 if (mDocumentTable
.IsInitialized()) {
1066 mDocumentTable
.Remove(aDocumentInfo
->DocumentURI());
1071 nsBindingManager::GetXBLDocumentInfo(nsIURI
* aURL
)
1073 if (!mDocumentTable
.IsInitialized())
1076 return mDocumentTable
.GetWeak(aURL
);
1080 nsBindingManager::PutLoadingDocListener(nsIURI
* aURL
, nsIStreamListener
* aListener
)
1082 NS_PRECONDITION(aListener
, "Must have a non-null listener!");
1084 NS_ENSURE_TRUE(mLoadingDocTable
.IsInitialized() || mLoadingDocTable
.Init(16),
1085 NS_ERROR_OUT_OF_MEMORY
);
1087 NS_ENSURE_TRUE(mLoadingDocTable
.Put(aURL
, aListener
),
1088 NS_ERROR_OUT_OF_MEMORY
);
1094 nsBindingManager::GetLoadingDocListener(nsIURI
* aURL
)
1096 if (!mLoadingDocTable
.IsInitialized())
1099 return mLoadingDocTable
.GetWeak(aURL
);
1103 nsBindingManager::RemoveLoadingDocListener(nsIURI
* aURL
)
1105 if (mLoadingDocTable
.IsInitialized()) {
1106 mLoadingDocTable
.Remove(aURL
);
1110 static PLDHashOperator
1111 MarkForDeath(nsISupports
*aKey
, nsXBLBinding
*aBinding
, void* aClosure
)
1113 if (aBinding
->MarkedForDeath())
1114 return PL_DHASH_NEXT
; // Already marked for death.
1117 aBinding
->PrototypeBinding()->DocURI()->GetPath(path
);
1119 if (!strncmp(path
.get(), "/skin", 5))
1120 aBinding
->MarkForDeath();
1122 return PL_DHASH_NEXT
;
1126 nsBindingManager::FlushSkinBindings()
1128 if (mBindingTable
.IsInitialized())
1129 mBindingTable
.EnumerateRead(MarkForDeath
, nsnull
);
1132 // Used below to protect from recurring in QI calls through XPConnect.
1133 struct AntiRecursionData
{
1134 nsIContent
* element
;
1136 AntiRecursionData
* next
;
1138 AntiRecursionData(nsIContent
* aElement
,
1140 AntiRecursionData
* aNext
)
1141 : element(aElement
), iid(aIID
), next(aNext
) {}
1145 nsBindingManager::GetBindingImplementation(nsIContent
* aContent
, REFNSIID aIID
,
1149 nsXBLBinding
*binding
= GetBinding(aContent
);
1151 // The binding should not be asked for nsISupports
1152 NS_ASSERTION(!aIID
.Equals(NS_GET_IID(nsISupports
)), "Asking a binding for nsISupports");
1153 if (binding
->ImplementsInterface(aIID
)) {
1154 nsCOMPtr
<nsIXPConnectWrappedJS
> wrappedJS
= GetWrappedJS(aContent
);
1157 // Protect from recurring in QI calls through XPConnect.
1158 // This can happen when a second binding is being resolved.
1159 // At that point a wrappedJS exists, but it doesn't yet know about
1160 // the iid we are asking for. So, without this protection,
1161 // AggregatedQueryInterface would end up recurring back into itself
1162 // through this code.
1164 // With this protection, when we detect the recursion we return
1165 // NS_NOINTERFACE in the inner call. The outer call will then fall
1166 // through (see below) and build a new chained wrappedJS for the iid.
1168 // We're careful to not assume that only one direct nesting can occur
1169 // because there is a call into JS in the middle and we can't assume
1170 // that this code won't be reached by some more complex nesting path.
1172 // NOTE: We *assume* this is single threaded, so we can use a
1173 // static linked list to do the check.
1175 static AntiRecursionData
* list
= nsnull
;
1177 for (AntiRecursionData
* p
= list
; p
; p
= p
->next
) {
1178 if (p
->element
== aContent
&& p
->iid
.Equals(aIID
)) {
1180 return NS_NOINTERFACE
;
1184 AntiRecursionData
item(aContent
, aIID
, list
);
1187 nsresult rv
= wrappedJS
->AggregatedQueryInterface(aIID
, aResult
);
1194 // No result was found, so this must be another XBL interface.
1195 // Fall through to create a new wrapper.
1198 // We have never made a wrapper for this implementation.
1199 // Create an XPC wrapper for the script object and hand it back.
1201 nsIDocument
* doc
= aContent
->GetOwnerDoc();
1203 return NS_NOINTERFACE
;
1205 nsIScriptGlobalObject
*global
= doc
->GetScriptGlobalObject();
1207 return NS_NOINTERFACE
;
1209 nsIScriptContext
*context
= global
->GetContext();
1211 return NS_NOINTERFACE
;
1213 JSContext
* jscontext
= (JSContext
*)context
->GetNativeContext();
1215 return NS_NOINTERFACE
;
1217 nsIXPConnect
*xpConnect
= nsContentUtils::XPConnect();
1219 nsCOMPtr
<nsIXPConnectWrappedNative
> wrapper
;
1220 xpConnect
->GetWrappedNativeOfNativeObject(jscontext
,
1221 global
->GetGlobalJSObject(),
1223 NS_GET_IID(nsISupports
),
1224 getter_AddRefs(wrapper
));
1225 NS_ENSURE_TRUE(wrapper
, NS_NOINTERFACE
);
1227 JSObject
* jsobj
= nsnull
;
1229 wrapper
->GetJSObject(&jsobj
);
1230 NS_ENSURE_TRUE(jsobj
, NS_NOINTERFACE
);
1232 nsresult rv
= xpConnect
->WrapJSAggregatedToNative(aContent
, jscontext
,
1233 jsobj
, aIID
, aResult
);
1237 // We successfully created a wrapper. We will own this wrapper for as long as the binding remains
1238 // alive. At the time the binding is cleared out of the bindingManager, we will remove the wrapper
1239 // from the bindingManager as well.
1240 nsISupports
* supp
= static_cast<nsISupports
*>(*aResult
);
1241 wrappedJS
= do_QueryInterface(supp
);
1242 SetWrappedJS(aContent
, wrappedJS
);
1249 return NS_NOINTERFACE
;
1253 nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc
,
1254 RuleProcessorData
* aData
,
1255 PRBool
* aCutOffInheritance
)
1257 *aCutOffInheritance
= PR_FALSE
;
1259 if (!aData
->mContent
)
1262 // Walk the binding scope chain, starting with the binding attached to our
1263 // content, up till we run out of scopes or we get cut off.
1264 nsIContent
*content
= aData
->mContent
;
1267 nsXBLBinding
*binding
= GetBinding(content
);
1269 aData
->mScopedRoot
= content
;
1270 binding
->WalkRules(aFunc
, aData
);
1271 // If we're not looking at our original content, allow the binding to cut
1272 // off style inheritance
1273 if (content
!= aData
->mContent
) {
1274 if (!binding
->InheritsStyle()) {
1275 // Go no further; we're not inheriting style from anything above here
1281 if (content
->IsRootOfNativeAnonymousSubtree()) {
1282 break; // Deliberately cut off style inheritance here.
1285 content
= content
->GetBindingParent();
1288 // If "content" is non-null that means we cut off inheritance at some point
1290 *aCutOffInheritance
= (content
!= nsnull
);
1292 // Null out the scoped root that we set repeatedly
1293 aData
->mScopedRoot
= nsnull
;
1298 typedef nsTHashtable
<nsVoidPtrHashKey
> RuleProcessorSet
;
1300 static PLDHashOperator
1301 EnumRuleProcessors(nsISupports
*aKey
, nsXBLBinding
*aBinding
, void* aClosure
)
1303 RuleProcessorSet
*set
= static_cast<RuleProcessorSet
*>(aClosure
);
1304 for (nsXBLBinding
*binding
= aBinding
; binding
;
1305 binding
= binding
->GetBaseBinding()) {
1306 nsIStyleRuleProcessor
*ruleProc
=
1307 binding
->PrototypeBinding()->GetRuleProcessor();
1309 if (!set
->IsInitialized() && !set
->Init(16))
1310 return PL_DHASH_STOP
;
1311 set
->PutEntry(ruleProc
);
1314 return PL_DHASH_NEXT
;
1317 struct MediumFeaturesChangedData
{
1318 nsPresContext
*mPresContext
;
1319 PRBool
*mRulesChanged
;
1322 static PLDHashOperator
1323 EnumMediumFeaturesChanged(nsVoidPtrHashKey
*aKey
, void* aClosure
)
1325 nsIStyleRuleProcessor
*ruleProcessor
=
1326 static_cast<nsIStyleRuleProcessor
*>(const_cast<void*>(aKey
->GetKey()));
1327 MediumFeaturesChangedData
*data
=
1328 static_cast<MediumFeaturesChangedData
*>(aClosure
);
1330 PRBool thisChanged
= PR_FALSE
;
1331 ruleProcessor
->MediumFeaturesChanged(data
->mPresContext
, &thisChanged
);
1332 *data
->mRulesChanged
= *data
->mRulesChanged
|| thisChanged
;
1334 return PL_DHASH_NEXT
;
1338 nsBindingManager::MediumFeaturesChanged(nsPresContext
* aPresContext
,
1339 PRBool
* aRulesChanged
)
1341 *aRulesChanged
= PR_FALSE
;
1342 if (!mBindingTable
.IsInitialized())
1345 RuleProcessorSet set
;
1346 mBindingTable
.EnumerateRead(EnumRuleProcessors
, &set
);
1347 if (!set
.IsInitialized())
1350 MediumFeaturesChangedData data
= { aPresContext
, aRulesChanged
};
1351 set
.EnumerateEntries(EnumMediumFeaturesChanged
, &data
);
1356 nsBindingManager::ShouldBuildChildFrames(nsIContent
* aContent
)
1358 nsXBLBinding
*binding
= GetBinding(aContent
);
1360 return !binding
|| binding
->ShouldBuildChildFrames();
1364 nsBindingManager::GetNestedInsertionPoint(nsIContent
* aParent
, nsIContent
* aChild
)
1366 // Check to see if the content is anonymous.
1367 if (aChild
->GetBindingParent() == aParent
)
1368 return nsnull
; // It is anonymous. Don't use the insertion point, since that's only
1369 // for the explicit kids.
1372 nsIContent
*insertionElement
= GetInsertionPoint(aParent
, aChild
, &index
);
1373 if (insertionElement
&& insertionElement
!= aParent
) {
1374 // See if we nest even further in.
1375 nsIContent
* nestedPoint
= GetNestedInsertionPoint(insertionElement
, aChild
);
1377 insertionElement
= nestedPoint
;
1380 return insertionElement
;
1384 nsBindingManager::GetNestedSingleInsertionPoint(nsIContent
* aParent
,
1385 PRBool
* aMultipleInsertionPoints
)
1387 *aMultipleInsertionPoints
= PR_FALSE
;
1390 nsIContent
*insertionElement
=
1391 GetSingleInsertionPoint(aParent
, &index
, aMultipleInsertionPoints
);
1392 if (*aMultipleInsertionPoints
) {
1395 if (insertionElement
&& insertionElement
!= aParent
) {
1396 // See if we nest even further in.
1397 nsIContent
* nestedPoint
=
1398 GetNestedSingleInsertionPoint(insertionElement
,
1399 aMultipleInsertionPoints
);
1401 insertionElement
= nestedPoint
;
1404 return insertionElement
;
1408 nsBindingManager::ContentAppended(nsIDocument
* aDocument
,
1409 nsIContent
* aContainer
,
1410 PRInt32 aNewIndexInContainer
)
1412 // XXX This is hacked and not quite correct. See below.
1413 if (aNewIndexInContainer
!= -1 &&
1414 (mContentListTable
.ops
|| mAnonymousNodesTable
.ops
)) {
1415 // It's not anonymous.
1417 nsIContent
* ins
= GetNestedSingleInsertionPoint(aContainer
, &multiple
);
1420 // Do each kid individually
1421 PRInt32 childCount
= aContainer
->GetChildCount();
1422 NS_ASSERTION(aNewIndexInContainer
>= 0, "Bogus index");
1423 for (PRInt32 idx
= aNewIndexInContainer
; idx
< childCount
; ++idx
) {
1424 HandleChildInsertion(aContainer
, aContainer
->GetChildAt(idx
),
1429 nsCOMPtr
<nsIDOMNodeList
> nodeList
;
1430 PRBool isAnonymousContentList
;
1431 GetXBLChildNodesInternal(ins
, getter_AddRefs(nodeList
),
1432 &isAnonymousContentList
);
1434 if (nodeList
&& isAnonymousContentList
) {
1435 // Find the one non-pseudo-insertion point and just add ourselves.
1436 nsAnonymousContentList
* contentList
=
1437 static_cast<nsAnonymousContentList
*>(nodeList
.get());
1439 PRInt32 count
= contentList
->GetInsertionPointCount();
1440 for (PRInt32 i
= 0; i
< count
; i
++) {
1441 nsXBLInsertionPoint
* point
= contentList
->GetInsertionPointAt(i
);
1442 PRInt32 index
= point
->GetInsertionIndex();
1444 // We're real. Jam all the kids in.
1445 PRInt32 childCount
= aContainer
->GetChildCount();
1446 for (PRInt32 j
= aNewIndexInContainer
; j
< childCount
; j
++) {
1447 nsIContent
* child
= aContainer
->GetChildAt(j
);
1448 point
->AddChild(child
);
1449 SetInsertionParent(child
, ins
);
1460 nsBindingManager::ContentInserted(nsIDocument
* aDocument
,
1461 nsIContent
* aContainer
,
1463 PRInt32 aIndexInContainer
)
1465 // XXX This is hacked just to make menus work again.
1466 if (aIndexInContainer
!= -1 &&
1467 (mContentListTable
.ops
|| mAnonymousNodesTable
.ops
)) {
1468 // It's not anonymous.
1469 NS_ASSERTION(aIndexInContainer
>= 0, "Bogus index");
1470 HandleChildInsertion(aContainer
, aChild
, aIndexInContainer
, PR_FALSE
);
1475 RemoveChildFromInsertionPoint(nsAnonymousContentList
* aInsertionPointList
,
1477 PRBool aRemoveFromPseudoPoints
)
1479 // We need to find the insertion point that contains aChild and remove it
1480 // from that insertion point. Sadly, we don't know which point it is, or
1481 // when we've hit it, but just trying to remove from all the pseudo or
1482 // non-pseudo insertion points, depending on the value of
1483 // aRemoveFromPseudoPoints, should work.
1484 PRInt32 count
= aInsertionPointList
->GetInsertionPointCount();
1485 for (PRInt32 i
= 0; i
< count
; i
++) {
1486 nsXBLInsertionPoint
* point
=
1487 aInsertionPointList
->GetInsertionPointAt(i
);
1488 if ((point
->GetInsertionIndex() == -1) == aRemoveFromPseudoPoints
) {
1489 point
->RemoveChild(aChild
);
1495 nsBindingManager::ContentRemoved(nsIDocument
* aDocument
,
1496 nsIContent
* aContainer
,
1498 PRInt32 aIndexInContainer
)
1500 if (aContainer
&& aIndexInContainer
!= -1 &&
1501 (mContentListTable
.ops
|| mAnonymousNodesTable
.ops
)) {
1502 // It's not anonymous
1503 nsCOMPtr
<nsIContent
> point
= GetNestedInsertionPoint(aContainer
, aChild
);
1506 nsCOMPtr
<nsIDOMNodeList
> nodeList
;
1507 PRBool isAnonymousContentList
;
1508 GetXBLChildNodesInternal(point
, getter_AddRefs(nodeList
),
1509 &isAnonymousContentList
);
1511 if (nodeList
&& isAnonymousContentList
) {
1512 // Find a non-pseudo-insertion point and remove ourselves.
1513 RemoveChildFromInsertionPoint(static_cast<nsAnonymousContentList
*>
1514 (static_cast<nsIDOMNodeList
*>
1518 SetInsertionParent(aChild
, nsnull
);
1522 // Whether the child has a nested insertion point or not, aContainer might
1523 // have insertion points under it. If that's the case, we need to remove
1524 // aChild from the pseudo insertion point it's in.
1525 if (mContentListTable
.ops
) {
1526 nsAnonymousContentList
* insertionPointList
=
1527 static_cast<nsAnonymousContentList
*>(
1528 static_cast<nsIDOMNodeList
*>(LookupObject(mContentListTable
,
1530 if (insertionPointList
) {
1531 RemoveChildFromInsertionPoint(insertionPointList
, aChild
, PR_TRUE
);
1538 nsBindingManager::DropDocumentReference()
1540 // Make sure to not run any more XBL constructors
1541 mProcessingAttachedStack
= PR_TRUE
;
1546 nsBindingManager::Traverse(nsIContent
*aContent
,
1547 nsCycleCollectionTraversalCallback
&cb
)
1549 if (!aContent
->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
1554 if (mInsertionParentTable
.ops
&&
1555 (value
= LookupObject(mInsertionParentTable
, aContent
))) {
1556 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mInsertionParentTable key");
1557 cb
.NoteXPCOMChild(aContent
);
1558 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mInsertionParentTable value");
1559 cb
.NoteXPCOMChild(value
);
1562 if (!aContent
->IsNodeOfType(nsINode::eELEMENT
)) {
1566 nsXBLBinding
*binding
= GetBinding(aContent
);
1568 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mBindingTable key");
1569 cb
.NoteXPCOMChild(aContent
);
1570 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(binding
, nsXBLBinding
,
1571 "[via binding manager] mBindingTable value")
1573 if (mContentListTable
.ops
&&
1574 (value
= LookupObject(mContentListTable
, aContent
))) {
1575 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mContentListTable key");
1576 cb
.NoteXPCOMChild(aContent
);
1577 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mContentListTable value");
1578 cb
.NoteXPCOMChild(value
);
1580 if (mAnonymousNodesTable
.ops
&&
1581 (value
= LookupObject(mAnonymousNodesTable
, aContent
))) {
1582 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mAnonymousNodesTable key");
1583 cb
.NoteXPCOMChild(aContent
);
1584 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mAnonymousNodesTable value");
1585 cb
.NoteXPCOMChild(value
);
1587 if (mWrapperTable
.ops
&&
1588 (value
= LookupObject(mWrapperTable
, aContent
))) {
1589 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mWrapperTable key");
1590 cb
.NoteXPCOMChild(aContent
);
1591 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mWrapperTable value");
1592 cb
.NoteXPCOMChild(value
);
1597 nsBindingManager::BeginOutermostUpdate()
1599 mAttachedStackSizeOnOutermost
= mAttachedStack
.Length();
1603 nsBindingManager::EndOutermostUpdate()
1605 if (!mProcessingAttachedStack
) {
1606 ProcessAttachedQueue(mAttachedStackSizeOnOutermost
);
1607 mAttachedStackSizeOnOutermost
= 0;
1612 nsBindingManager::HandleChildInsertion(nsIContent
* aContainer
,
1614 PRUint32 aIndexInContainer
,
1617 NS_PRECONDITION(aChild
, "Must have child");
1618 NS_PRECONDITION(!aContainer
||
1619 PRUint32(aContainer
->IndexOf(aChild
)) == aIndexInContainer
,
1620 "Child not at the right index?");
1622 nsIContent
* ins
= GetNestedInsertionPoint(aContainer
, aChild
);
1625 nsCOMPtr
<nsIDOMNodeList
> nodeList
;
1626 PRBool isAnonymousContentList
;
1627 GetXBLChildNodesInternal(ins
, getter_AddRefs(nodeList
),
1628 &isAnonymousContentList
);
1630 if (nodeList
&& isAnonymousContentList
) {
1631 // Find a non-pseudo-insertion point and just jam ourselves in. This is
1632 // not 100% correct, since there might be multiple insertion points under
1633 // this insertion parent, and we should really be using the one that
1634 // matches our content... Hack city, baby.
1635 nsAnonymousContentList
* contentList
=
1636 static_cast<nsAnonymousContentList
*>(nodeList
.get());
1638 PRInt32 count
= contentList
->GetInsertionPointCount();
1639 for (PRInt32 i
= 0; i
< count
; i
++) {
1640 nsXBLInsertionPoint
* point
= contentList
->GetInsertionPointAt(i
);
1641 if (point
->GetInsertionIndex() != -1) {
1642 // We're real. Jam the kid in.
1644 // Find the right insertion spot. Can't just insert in the insertion
1645 // point at aIndexInContainer since the point may contain anonymous
1646 // content, not all of aContainer's kids, etc. So find the last
1647 // child of aContainer that comes before aIndexInContainer and is in
1648 // the insertion point and insert right after it.
1649 PRInt32 pointSize
= point
->ChildCount();
1650 PRBool inserted
= PR_FALSE
;
1651 for (PRInt32 parentIndex
= aIndexInContainer
- 1;
1652 parentIndex
>= 0 && !inserted
; --parentIndex
) {
1653 nsIContent
* currentSibling
= aContainer
->GetChildAt(parentIndex
);
1654 for (PRInt32 pointIndex
= pointSize
- 1; pointIndex
>= 0;
1656 nsCOMPtr
<nsIContent
> currContent
= point
->ChildAt(pointIndex
);
1657 if (currContent
== currentSibling
) {
1658 point
->InsertChildAt(pointIndex
+ 1, aChild
);
1665 // None of our previous siblings are in here... just stick
1666 // ourselves in at the end of the insertion point if we're
1667 // appending, and at the beginning otherwise.
1668 // XXXbz if we ever start doing the filter thing right, this may be
1669 // no good, since we may _still_ have anonymous kids in there and
1670 // may need to get the ordering with those right.
1672 point
->AddChild(aChild
);
1674 point
->InsertChildAt(0, aChild
);
1677 SetInsertionParent(aChild
, ins
);