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"
61 #include "nsGenericDOMNodeList.h"
63 #include "nsXBLBinding.h"
64 #include "nsXBLPrototypeBinding.h"
65 #include "nsIXBLDocumentInfo.h"
66 #include "nsXBLInsertionPoint.h"
68 #include "nsIStyleSheet.h"
69 #include "nsHTMLStyleSheet.h"
70 #include "nsIHTMLCSSStyleSheet.h"
72 #include "nsIStyleRuleProcessor.h"
73 #include "nsIWeakReference.h"
76 #include "nsIXPConnect.h"
78 #include "nsIDOMScriptObjectFactory.h"
79 #include "nsIScriptGlobalObject.h"
80 #include "nsTHashtable.h"
82 #include "nsIScriptContext.h"
83 #include "nsBindingManager.h"
85 #include "nsThreadUtils.h"
87 // ==================================================================
88 // = nsAnonymousContentList
89 // ==================================================================
91 #define NS_ANONYMOUS_CONTENT_LIST_IID \
92 { 0xa29df1f8, 0xaeca, 0x4356, \
93 { 0xa8, 0xc2, 0xa7, 0x24, 0xa2, 0x11, 0x73, 0xac } }
95 class nsAnonymousContentList
: public nsIDOMNodeList
,
99 nsAnonymousContentList(nsInsertionPointList
* aElements
);
100 virtual ~nsAnonymousContentList();
102 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
103 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsAnonymousContentList
, nsIDOMNodeList
)
104 // nsIDOMNodeList interface
105 NS_DECL_NSIDOMNODELIST
107 // nsINodeList interface
108 virtual nsINode
* GetNodeAt(PRUint32 aIndex
);
110 PRInt32
GetInsertionPointCount() { return mElements
->Length(); }
112 nsXBLInsertionPoint
* GetInsertionPointAt(PRInt32 i
) { return static_cast<nsXBLInsertionPoint
*>(mElements
->ElementAt(i
)); }
113 void RemoveInsertionPointAt(PRInt32 i
) { mElements
->RemoveElementAt(i
); }
114 NS_DECLARE_STATIC_IID_ACCESSOR(NS_ANONYMOUS_CONTENT_LIST_IID
)
116 nsInsertionPointList
* mElements
;
119 NS_DEFINE_STATIC_IID_ACCESSOR(nsAnonymousContentList
,
120 NS_ANONYMOUS_CONTENT_LIST_IID
)
122 nsAnonymousContentList::nsAnonymousContentList(nsInsertionPointList
* aElements
)
123 : mElements(aElements
)
125 MOZ_COUNT_CTOR(nsAnonymousContentList
);
127 // We don't reference count our Anonymous reference (to avoid circular
128 // references). We'll be told when the Anonymous goes away.
131 nsAnonymousContentList::~nsAnonymousContentList()
133 MOZ_COUNT_DTOR(nsAnonymousContentList
);
137 NS_IMPL_CYCLE_COLLECTION_CLASS(nsAnonymousContentList
)
139 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList
)
140 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnonymousContentList
)
142 NS_INTERFACE_MAP_BEGIN(nsAnonymousContentList
)
143 NS_INTERFACE_MAP_ENTRY(nsINodeList
)
144 NS_INTERFACE_MAP_ENTRY(nsIDOMNodeList
)
145 if (aIID
.Equals(NS_GET_IID(nsAnonymousContentList
)))
146 foundInterface
= static_cast<nsIDOMNodeList
*>(this);
148 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMNodeList
)
149 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(NodeList
)
150 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsAnonymousContentList
)
153 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsAnonymousContentList
)
154 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAnonymousContentList
)
156 PRInt32 i
, count
= tmp
->mElements
->Length();
157 for (i
= 0; i
< count
; ++i
) {
158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mElements
->ElementAt(i
),
159 nsXBLInsertionPoint
);
162 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
165 nsAnonymousContentList::GetLength(PRUint32
* aLength
)
167 NS_ASSERTION(aLength
!= nsnull
, "null ptr");
169 return NS_ERROR_NULL_POINTER
;
171 PRInt32 cnt
= mElements
->Length();
174 for (PRInt32 i
= 0; i
< cnt
; i
++)
175 *aLength
+= static_cast<nsXBLInsertionPoint
*>(mElements
->ElementAt(i
))->ChildCount();
181 nsAnonymousContentList::Item(PRUint32 aIndex
, nsIDOMNode
** aReturn
)
183 nsINode
* item
= GetNodeAt(aIndex
);
185 return NS_ERROR_FAILURE
;
187 return CallQueryInterface(item
, aReturn
);
191 nsAnonymousContentList::GetNodeAt(PRUint32 aIndex
)
193 PRInt32 cnt
= mElements
->Length();
194 PRUint32 pointCount
= 0;
196 for (PRInt32 i
= 0; i
< cnt
; i
++) {
197 aIndex
-= pointCount
;
199 nsXBLInsertionPoint
* point
= static_cast<nsXBLInsertionPoint
*>(mElements
->ElementAt(i
));
200 pointCount
= point
->ChildCount();
202 if (aIndex
< pointCount
) {
203 return point
->ChildAt(aIndex
);
211 // Generic pldhash table stuff for mapping one nsISupports to another
213 // These values are never null - a null value implies that this
214 // whole key should be removed (See SetOrRemoveObject)
215 class ObjectEntry
: public PLDHashEntryHdr
219 // note that these are allocated within the PLDHashTable, but we
220 // want to keep track of them anyway
221 ObjectEntry() { MOZ_COUNT_CTOR(ObjectEntry
); }
222 ~ObjectEntry() { MOZ_COUNT_DTOR(ObjectEntry
); }
224 nsISupports
* GetValue() { return mValue
; }
225 nsISupports
* GetKey() { return mKey
; }
226 void SetValue(nsISupports
* aValue
) { mValue
= aValue
; }
227 void SetKey(nsISupports
* aKey
) { mKey
= aKey
; }
230 nsCOMPtr
<nsISupports
> mKey
;
231 nsCOMPtr
<nsISupports
> mValue
;
234 PR_STATIC_CALLBACK(void)
235 ClearObjectEntry(PLDHashTable
* table
, PLDHashEntryHdr
*entry
)
237 ObjectEntry
* objEntry
= static_cast<ObjectEntry
*>(entry
);
238 objEntry
->~ObjectEntry();
241 PR_STATIC_CALLBACK(PRBool
)
242 InitObjectEntry(PLDHashTable
* table
, PLDHashEntryHdr
* entry
, const void* key
)
244 new (entry
) ObjectEntry
;
250 static PLDHashTableOps ObjectTableOps
= {
253 PL_DHashVoidPtrKeyStub
,
254 PL_DHashMatchEntryStub
,
255 PL_DHashMoveEntryStub
,
257 PL_DHashFinalizeStub
,
261 // helper routine for adding a new entry
263 AddObjectEntry(PLDHashTable
& table
, nsISupports
* aKey
, nsISupports
* aValue
)
265 NS_ASSERTION(aKey
, "key must be non-null");
266 if (!aKey
) return NS_ERROR_INVALID_ARG
;
269 static_cast<ObjectEntry
*>
270 (PL_DHashTableOperate(&table
, aKey
, PL_DHASH_ADD
));
273 return NS_ERROR_OUT_OF_MEMORY
;
275 // only add the key if the entry is new
276 if (!entry
->GetKey())
279 // now attach the new entry - note that entry->mValue could possibly
280 // have a value already, this will release that.
281 entry
->SetValue(aValue
);
286 // helper routine for looking up an existing entry. Note that the
287 // return result is NOT addreffed
289 LookupObject(PLDHashTable
& table
, nsIContent
* aKey
)
291 if (aKey
&& aKey
->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
293 static_cast<ObjectEntry
*>
294 (PL_DHashTableOperate(&table
, aKey
, PL_DHASH_LOOKUP
));
296 if (PL_DHASH_ENTRY_IS_BUSY(entry
))
297 return entry
->GetValue();
304 RemoveObjectEntry(PLDHashTable
& table
, nsISupports
* aKey
)
306 PL_DHashTableOperate(&table
, aKey
, PL_DHASH_REMOVE
);
310 SetOrRemoveObject(PLDHashTable
& table
, nsIContent
* aKey
, nsISupports
* aValue
)
313 // lazily create the table, but only when adding elements
315 !PL_DHashTableInit(&table
, &ObjectTableOps
, nsnull
,
316 sizeof(ObjectEntry
), 16)) {
318 return NS_ERROR_OUT_OF_MEMORY
;
320 aKey
->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR
);
321 return AddObjectEntry(table
, aKey
, aValue
);
324 // no value, so remove the key from the table
327 static_cast<ObjectEntry
*>
328 (PL_DHashTableOperate(&table
, aKey
, PL_DHASH_LOOKUP
));
329 if (entry
&& PL_DHASH_ENTRY_IS_BUSY(entry
)) {
330 // Keep key and value alive while removing the entry.
331 nsCOMPtr
<nsISupports
> key
= entry
->GetKey();
332 nsCOMPtr
<nsISupports
> value
= entry
->GetValue();
333 RemoveObjectEntry(table
, aKey
);
339 // Implementation /////////////////////////////////////////////////////////////////
341 // Static member variable initialization
343 // Implement our nsISupports methods
345 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager
)
346 tmp
->mDestroyed
= PR_TRUE
;
348 if (tmp
->mBindingTable
.IsInitialized())
349 tmp
->mBindingTable
.Clear();
351 if (tmp
->mDocumentTable
.IsInitialized())
352 tmp
->mDocumentTable
.Clear();
354 if (tmp
->mLoadingDocTable
.IsInitialized())
355 tmp
->mLoadingDocTable
.Clear();
357 if (tmp
->mContentListTable
.ops
)
358 PL_DHashTableFinish(&(tmp
->mContentListTable
));
359 tmp
->mContentListTable
.ops
= nsnull
;
361 if (tmp
->mAnonymousNodesTable
.ops
)
362 PL_DHashTableFinish(&(tmp
->mAnonymousNodesTable
));
363 tmp
->mAnonymousNodesTable
.ops
= nsnull
;
365 if (tmp
->mInsertionParentTable
.ops
)
366 PL_DHashTableFinish(&(tmp
->mInsertionParentTable
));
367 tmp
->mInsertionParentTable
.ops
= nsnull
;
369 if (tmp
->mWrapperTable
.ops
)
370 PL_DHashTableFinish(&(tmp
->mWrapperTable
));
371 tmp
->mWrapperTable
.ops
= nsnull
;
373 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mAttachedStack
)
374 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
377 static PLDHashOperator
378 DocumentInfoHashtableTraverser(nsIURI
* key
,
379 nsIXBLDocumentInfo
* di
,
382 nsCycleCollectionTraversalCallback
*cb
=
383 static_cast<nsCycleCollectionTraversalCallback
*>(userArg
);
384 cb
->NoteXPCOMChild(di
);
385 return PL_DHASH_NEXT
;
388 static PLDHashOperator
389 LoadingDocHashtableTraverser(nsIURI
* key
,
390 nsIStreamListener
* sl
,
393 nsCycleCollectionTraversalCallback
*cb
=
394 static_cast<nsCycleCollectionTraversalCallback
*>(userArg
);
395 cb
->NoteXPCOMChild(sl
);
396 return PL_DHASH_NEXT
;
399 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager
)
400 // The hashes keyed on nsIContent are traversed from the nsIContent itself.
401 if (tmp
->mDocumentTable
.IsInitialized())
402 tmp
->mDocumentTable
.EnumerateRead(&DocumentInfoHashtableTraverser
, &cb
);
403 if (tmp
->mLoadingDocTable
.IsInitialized())
404 tmp
->mLoadingDocTable
.EnumerateRead(&LoadingDocHashtableTraverser
, &cb
);
405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mAttachedStack
,
407 // No need to traverse mProcessAttachedQueueEvent, since it'll just
408 // fire at some point.
409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
411 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager
)
413 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager
)
414 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
415 NS_INTERFACE_MAP_ENTRY(nsISupports
)
418 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager
)
419 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager
)
421 // Constructors/Destructors
422 nsBindingManager::nsBindingManager(nsIDocument
* aDocument
)
423 : mProcessingAttachedStack(PR_FALSE
),
424 mDestroyed(PR_FALSE
),
425 mAttachedStackSizeOnOutermost(0),
428 mContentListTable
.ops
= nsnull
;
429 mAnonymousNodesTable
.ops
= nsnull
;
430 mInsertionParentTable
.ops
= nsnull
;
431 mWrapperTable
.ops
= nsnull
;
434 nsBindingManager::~nsBindingManager(void)
436 mDestroyed
= PR_TRUE
;
438 if (mContentListTable
.ops
)
439 PL_DHashTableFinish(&mContentListTable
);
440 if (mAnonymousNodesTable
.ops
)
441 PL_DHashTableFinish(&mAnonymousNodesTable
);
442 NS_ASSERTION(!mInsertionParentTable
.ops
|| !mInsertionParentTable
.entryCount
,
443 "Insertion parent table isn't empty!");
444 if (mInsertionParentTable
.ops
)
445 PL_DHashTableFinish(&mInsertionParentTable
);
446 if (mWrapperTable
.ops
)
447 PL_DHashTableFinish(&mWrapperTable
);
451 PR_CALLBACK
RemoveInsertionParentCB(PLDHashTable
* aTable
, PLDHashEntryHdr
* aEntry
,
452 PRUint32 aNumber
, void* aArg
)
454 return (static_cast<ObjectEntry
*>(aEntry
)->GetValue() ==
455 static_cast<nsISupports
*>(aArg
)) ? PL_DHASH_REMOVE
: PL_DHASH_NEXT
;
459 RemoveInsertionParentForNodeList(nsIDOMNodeList
* aList
, nsIContent
* aParent
)
461 nsAnonymousContentList
* list
= nsnull
;
463 CallQueryInterface(aList
, &list
);
466 PRInt32 count
= list
->GetInsertionPointCount();
467 for (PRInt32 i
= 0; i
< count
; ++i
) {
468 nsRefPtr
<nsXBLInsertionPoint
> currPoint
= list
->GetInsertionPointAt(i
);
469 currPoint
->UnbindDefaultContent();
471 nsCOMPtr
<nsIContent
> parent
= currPoint
->GetInsertionParent();
472 NS_ASSERTION(!parent
|| parent
== aParent
, "Wrong insertion parent!");
474 currPoint
->ClearInsertionParent();
481 nsBindingManager::RemoveInsertionParent(nsIContent
* aParent
)
483 nsCOMPtr
<nsIDOMNodeList
> contentlist
;
484 GetContentListFor(aParent
, getter_AddRefs(contentlist
));
485 RemoveInsertionParentForNodeList(contentlist
, aParent
);
487 nsCOMPtr
<nsIDOMNodeList
> anonnodes
;
488 GetAnonymousNodesFor(aParent
, getter_AddRefs(anonnodes
));
489 RemoveInsertionParentForNodeList(anonnodes
, aParent
);
491 if (mInsertionParentTable
.ops
) {
492 PL_DHashTableEnumerate(&mInsertionParentTable
, RemoveInsertionParentCB
,
493 static_cast<nsISupports
*>(aParent
));
498 nsBindingManager::GetBinding(nsIContent
* aContent
)
500 if (aContent
&& aContent
->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
) &&
501 mBindingTable
.IsInitialized()) {
502 return mBindingTable
.GetWeak(aContent
);
509 nsBindingManager::SetBinding(nsIContent
* aContent
, nsXBLBinding
* aBinding
)
511 if (!mBindingTable
.IsInitialized()) {
512 if (!mBindingTable
.Init())
513 return NS_ERROR_OUT_OF_MEMORY
;
516 // After this point, aBinding will be the most-derived binding for aContent.
517 // If we already have a binding for aContent in our table, make sure to
518 // remove it from the attached stack. Otherwise we might end up firing its
519 // constructor twice (if aBinding inherits from it) or firing its constructor
520 // after aContent has been deleted (if aBinding is null and the content node
521 // dies before we process mAttachedStack).
522 nsRefPtr
<nsXBLBinding
> oldBinding
= GetBinding(aContent
);
524 if (aContent
->HasFlag(NODE_IS_INSERTION_PARENT
)) {
525 nsRefPtr
<nsXBLBinding
> parentBinding
=
526 GetBinding(aContent
->GetBindingParent());
527 // Clear insertion parent only if we don't have a parent binding which
528 // marked content to be an insertion parent. See also ChangeDocumentFor().
529 if (!parentBinding
|| !parentBinding
->HasInsertionParent(aContent
)) {
530 RemoveInsertionParent(aContent
);
531 aContent
->UnsetFlags(NODE_IS_INSERTION_PARENT
);
534 // Don't remove items here as that could mess up an executing
535 // ProcessAttachedQueue
536 PRUint32 index
= mAttachedStack
.IndexOf(oldBinding
);
537 if (index
!= mAttachedStack
.NoIndex
) {
538 mAttachedStack
[index
] = nsnull
;
542 PRBool result
= PR_TRUE
;
545 aContent
->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR
);
546 result
= mBindingTable
.Put(aContent
, aBinding
);
548 mBindingTable
.Remove(aContent
);
550 // The death of the bindings means the death of the JS wrapper,
551 // and the flushing of our explicit and anonymous insertion point
553 SetWrappedJS(aContent
, nsnull
);
554 SetContentListFor(aContent
, nsnull
);
555 SetAnonymousNodesFor(aContent
, nsnull
);
558 return result
? NS_OK
: NS_ERROR_FAILURE
;
562 nsBindingManager::GetInsertionParent(nsIContent
* aContent
)
564 if (mInsertionParentTable
.ops
) {
565 return static_cast<nsIContent
*>
566 (LookupObject(mInsertionParentTable
, aContent
));
573 nsBindingManager::SetInsertionParent(nsIContent
* aContent
, nsIContent
* aParent
)
575 NS_ASSERTION(!aParent
|| aParent
->HasFlag(NODE_IS_INSERTION_PARENT
),
576 "Insertion parent should have NODE_IS_INSERTION_PARENT flag!");
582 return SetOrRemoveObject(mInsertionParentTable
, aContent
, aParent
);
585 nsIXPConnectWrappedJS
*
586 nsBindingManager::GetWrappedJS(nsIContent
* aContent
)
588 if (mWrapperTable
.ops
) {
589 return static_cast<nsIXPConnectWrappedJS
*>(LookupObject(mWrapperTable
, aContent
));
596 nsBindingManager::SetWrappedJS(nsIContent
* aContent
, nsIXPConnectWrappedJS
* aWrappedJS
)
602 return SetOrRemoveObject(mWrapperTable
, aContent
, aWrappedJS
);
606 nsBindingManager::ChangeDocumentFor(nsIContent
* aContent
, nsIDocument
* aOldDocument
,
607 nsIDocument
* aNewDocument
)
609 // XXXbz this code is pretty broken, since moving from one document
610 // to another always passes through a null document!
611 NS_PRECONDITION(aOldDocument
!= nsnull
, "no old document");
612 NS_PRECONDITION(!aNewDocument
,
613 "Changing to a non-null new document not supported yet");
615 return NS_ERROR_NULL_POINTER
;
617 // Hold a ref to the binding so it won't die when we remove it from our
619 nsRefPtr
<nsXBLBinding
> binding
= GetBinding(aContent
);
620 if (aContent
->HasFlag(NODE_IS_INSERTION_PARENT
)) {
621 nsRefPtr
<nsXBLBinding
> parentBinding
= GetBinding(aContent
->GetBindingParent());
623 parentBinding
->RemoveInsertionParent(aContent
);
624 // Clear insertion parent only if we don't have a binding which
625 // marked content to be an insertion parent. See also SetBinding().
626 if (!binding
|| !binding
->HasInsertionParent(aContent
)) {
627 RemoveInsertionParent(aContent
);
628 aContent
->UnsetFlags(NODE_IS_INSERTION_PARENT
);
634 binding
->ChangeDocument(aOldDocument
, aNewDocument
);
635 SetBinding(aContent
, nsnull
);
637 aNewDocument
->BindingManager()->SetBinding(aContent
, binding
);
640 // Clear out insertion parents and content lists.
641 SetInsertionParent(aContent
, nsnull
);
642 SetContentListFor(aContent
, nsnull
);
643 SetAnonymousNodesFor(aContent
, nsnull
);
649 nsBindingManager::ResolveTag(nsIContent
* aContent
, PRInt32
* aNameSpaceID
)
651 nsXBLBinding
*binding
= GetBinding(aContent
);
654 nsIAtom
* base
= binding
->GetBaseTag(aNameSpaceID
);
661 *aNameSpaceID
= aContent
->GetNameSpaceID();
662 return aContent
->Tag();
666 nsBindingManager::GetContentListFor(nsIContent
* aContent
, nsIDOMNodeList
** aResult
)
668 // Locate the primary binding and get its node list of anonymous children.
671 if (mContentListTable
.ops
) {
672 *aResult
= static_cast<nsIDOMNodeList
*>
673 (LookupObject(mContentListTable
, aContent
));
674 NS_IF_ADDREF(*aResult
);
678 nsCOMPtr
<nsIDOMNode
> node(do_QueryInterface(aContent
));
679 node
->GetChildNodes(aResult
);
686 nsBindingManager::SetContentListFor(nsIContent
* aContent
,
687 nsInsertionPointList
* aList
)
693 nsIDOMNodeList
* contentList
= nsnull
;
695 contentList
= new nsAnonymousContentList(aList
);
698 return NS_ERROR_OUT_OF_MEMORY
;
702 return SetOrRemoveObject(mContentListTable
, aContent
, contentList
);
706 nsBindingManager::HasContentListFor(nsIContent
* aContent
)
708 return mContentListTable
.ops
&& LookupObject(mContentListTable
, aContent
);
712 nsBindingManager::GetAnonymousNodesInternal(nsIContent
* aContent
,
713 nsIDOMNodeList
** aResult
,
714 PRBool
* aIsAnonymousContentList
)
716 // Locate the primary binding and get its node list of anonymous children.
718 if (mAnonymousNodesTable
.ops
) {
719 *aResult
= static_cast<nsIDOMNodeList
*>
720 (LookupObject(mAnonymousNodesTable
, aContent
));
721 NS_IF_ADDREF(*aResult
);
725 *aIsAnonymousContentList
= PR_FALSE
;
726 nsXBLBinding
*binding
= GetBinding(aContent
);
728 *aResult
= binding
->GetAnonymousNodes().get();
732 *aIsAnonymousContentList
= PR_TRUE
;
738 nsBindingManager::GetAnonymousNodesFor(nsIContent
* aContent
,
739 nsIDOMNodeList
** aResult
)
742 return GetAnonymousNodesInternal(aContent
, aResult
, &dummy
);
746 nsBindingManager::SetAnonymousNodesFor(nsIContent
* aContent
,
747 nsInsertionPointList
* aList
)
753 nsIDOMNodeList
* contentList
= nsnull
;
755 contentList
= new nsAnonymousContentList(aList
);
758 return NS_ERROR_OUT_OF_MEMORY
;
762 return SetOrRemoveObject(mAnonymousNodesTable
, aContent
, contentList
);
766 nsBindingManager::GetXBLChildNodesInternal(nsIContent
* aContent
,
767 nsIDOMNodeList
** aResult
,
768 PRBool
* aIsAnonymousContentList
)
774 // Retrieve the anonymous content that we should build.
775 nsCOMPtr
<nsIDOMNodeList
> result
;
776 GetAnonymousNodesInternal(aContent
, getter_AddRefs(result
),
777 aIsAnonymousContentList
);
779 result
->GetLength(&length
);
784 // We may have an altered list of children from XBL insertion points.
785 // If we don't have any anonymous kids, we next check to see if we have
788 if (mContentListTable
.ops
) {
789 result
= static_cast<nsIDOMNodeList
*>
790 (LookupObject(mContentListTable
, aContent
));
791 *aIsAnonymousContentList
= PR_TRUE
;
795 result
.swap(*aResult
);
801 nsBindingManager::GetXBLChildNodesFor(nsIContent
* aContent
, nsIDOMNodeList
** aResult
)
804 return GetXBLChildNodesInternal(aContent
, aResult
, &dummy
);
808 nsBindingManager::GetInsertionPoint(nsIContent
* aParent
, nsIContent
* aChild
,
811 nsXBLBinding
*binding
= GetBinding(aParent
);
812 return binding
? binding
->GetInsertionPoint(aChild
, aIndex
) : nsnull
;
816 nsBindingManager::GetSingleInsertionPoint(nsIContent
* aParent
,
818 PRBool
* aMultipleInsertionPoints
)
820 nsXBLBinding
*binding
= GetBinding(aParent
);
822 return binding
->GetSingleInsertionPoint(aIndex
, aMultipleInsertionPoints
);
824 *aMultipleInsertionPoints
= PR_FALSE
;
829 nsBindingManager::AddLayeredBinding(nsIContent
* aContent
, nsIURI
* aURL
,
830 nsIPrincipal
* aOriginPrincipal
)
832 // First we need to load our binding.
834 nsCOMPtr
<nsIXBLService
> xblService
=
835 do_GetService("@mozilla.org/xbl;1", &rv
);
839 // Load the bindings.
840 nsRefPtr
<nsXBLBinding
> binding
;
842 xblService
->LoadBindings(aContent
, aURL
, aOriginPrincipal
, PR_TRUE
,
843 getter_AddRefs(binding
), &dummy
);
845 AddToAttachedQueue(binding
);
846 ProcessAttachedQueue();
853 nsBindingManager::RemoveLayeredBinding(nsIContent
* aContent
, nsIURI
* aURL
)
855 // Hold a ref to the binding so it won't die when we remove it from our table
856 nsRefPtr
<nsXBLBinding
> binding
= GetBinding(aContent
);
862 // For now we can only handle removing a binding if it's the only one
863 NS_ENSURE_FALSE(binding
->GetBaseBinding(), NS_ERROR_FAILURE
);
865 // Make sure that the binding has the URI that is requested to be removed
866 nsIURI
* bindingUri
= binding
->PrototypeBinding()->BindingURI();
869 nsresult rv
= aURL
->Equals(bindingUri
, &equalUri
);
870 NS_ENSURE_SUCCESS(rv
, rv
);
875 // Make sure it isn't a style binding
876 if (binding
->IsStyleBinding()) {
880 // Hold strong ref in case removing the binding tries to close the
881 // window or something.
882 // XXXbz should that be ownerdoc? Wouldn't we need a ref to the
883 // currentdoc too? What's the one that should be passed to
885 nsCOMPtr
<nsIDocument
> doc
= aContent
->GetOwnerDoc();
886 NS_ASSERTION(doc
, "No owner document?");
888 // Finally remove the binding...
889 // XXXbz this doesn't remove the implementation! Should fix! Until
890 // then we need the explicit UnhookEventHandlers here.
891 binding
->UnhookEventHandlers();
892 binding
->ChangeDocument(doc
, nsnull
);
893 SetBinding(aContent
, nsnull
);
894 binding
->MarkForDeath();
896 // ...and recreate it's frames. We need to do this since the frames may have
897 // been removed and style may have changed due to the removal of the
898 // anonymous children.
899 // XXXbz this should be using the current doc (if any), not the owner doc.
900 nsIPresShell
*presShell
= doc
->GetPrimaryShell();
901 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
903 return presShell
->RecreateFramesFor(aContent
);;
907 nsBindingManager::LoadBindingDocument(nsIDocument
* aBoundDoc
,
909 nsIPrincipal
* aOriginPrincipal
)
911 NS_PRECONDITION(aURL
, "Must have a URI to load!");
913 // First we need to load our binding.
915 nsCOMPtr
<nsIXBLService
> xblService
=
916 do_GetService("@mozilla.org/xbl;1", &rv
);
920 // Load the binding doc.
921 nsCOMPtr
<nsIXBLDocumentInfo
> info
;
922 xblService
->LoadBindingDocumentInfo(nsnull
, aBoundDoc
, aURL
,
923 aOriginPrincipal
, PR_TRUE
,
924 getter_AddRefs(info
));
926 return NS_ERROR_FAILURE
;
932 nsBindingManager::AddToAttachedQueue(nsXBLBinding
* aBinding
)
934 if (!mAttachedStack
.AppendElement(aBinding
))
935 return NS_ERROR_OUT_OF_MEMORY
;
937 // If we're in the middle of processing our queue already, don't
938 // bother posting the event.
939 if (!mProcessingAttachedStack
&& !mProcessAttachedQueueEvent
) {
940 PostProcessAttachedQueueEvent();
948 nsBindingManager::PostProcessAttachedQueueEvent()
950 mProcessAttachedQueueEvent
=
951 new nsRunnableMethod
<nsBindingManager
>(
952 this, &nsBindingManager::DoProcessAttachedQueue
);
953 nsresult rv
= NS_DispatchToCurrentThread(mProcessAttachedQueueEvent
);
954 if (NS_SUCCEEDED(rv
) && mDocument
) {
955 mDocument
->BlockOnload();
960 nsBindingManager::DoProcessAttachedQueue()
962 if (!mProcessingAttachedStack
) {
963 ProcessAttachedQueue();
965 NS_ASSERTION(mAttachedStack
.Length() == 0,
966 "Shouldn't have pending bindings!");
968 mProcessAttachedQueueEvent
= nsnull
;
970 // Someone's doing event processing from inside a constructor.
971 // They're evil, but we'll fight back! Just poll on them being
972 // done and repost the attached queue event.
973 PostProcessAttachedQueueEvent();
976 // No matter what, unblock onload for the event that's fired.
978 // Hold a strong reference while calling UnblockOnload since that might
980 nsCOMPtr
<nsIDocument
> doc
= mDocument
;
981 doc
->UnblockOnload(PR_TRUE
);
986 nsBindingManager::ProcessAttachedQueue(PRUint32 aSkipSize
)
988 if (mProcessingAttachedStack
|| mAttachedStack
.Length() <= aSkipSize
)
991 mProcessingAttachedStack
= PR_TRUE
;
993 // Excute constructors. Do this from high index to low
994 while (mAttachedStack
.Length() > aSkipSize
) {
995 PRUint32 lastItem
= mAttachedStack
.Length() - 1;
996 nsRefPtr
<nsXBLBinding
> binding
= mAttachedStack
.ElementAt(lastItem
);
997 mAttachedStack
.RemoveElementAt(lastItem
);
999 binding
->ExecuteAttachedHandler();
1003 // If NodeWillBeDestroyed has run we don't want to clobber
1004 // mProcessingAttachedStack set there.
1006 mProcessingAttachedStack
= PR_FALSE
;
1009 NS_ASSERTION(mAttachedStack
.Length() == aSkipSize
, "How did we get here?");
1011 mAttachedStack
.Compact();
1014 // Keep bindings and bound elements alive while executing detached handlers.
1015 struct BindingTableReadClosure
1017 nsCOMArray
<nsIContent
> mBoundElements
;
1018 nsBindingList mBindings
;
1021 PR_STATIC_CALLBACK(PLDHashOperator
)
1022 AccumulateBindingsToDetach(nsISupports
*aKey
, nsXBLBinding
*aBinding
,
1025 BindingTableReadClosure
* closure
=
1026 static_cast<BindingTableReadClosure
*>(aClosure
);
1027 if (aBinding
&& closure
->mBindings
.AppendElement(aBinding
)) {
1028 if (!closure
->mBoundElements
.AppendObject(aBinding
->GetBoundElement())) {
1029 closure
->mBindings
.RemoveElementAt(closure
->mBindings
.Length() - 1);
1032 return PL_DHASH_NEXT
;
1036 nsBindingManager::ExecuteDetachedHandlers()
1038 // Walk our hashtable of bindings.
1039 if (mBindingTable
.IsInitialized()) {
1040 BindingTableReadClosure closure
;
1041 mBindingTable
.EnumerateRead(AccumulateBindingsToDetach
, &closure
);
1042 PRUint32 i
, count
= closure
.mBindings
.Length();
1043 for (i
= 0; i
< count
; ++i
) {
1044 closure
.mBindings
[i
]->ExecuteDetachedHandler();
1050 nsBindingManager::PutXBLDocumentInfo(nsIXBLDocumentInfo
* aDocumentInfo
)
1052 NS_PRECONDITION(aDocumentInfo
, "Must have a non-null documentinfo!");
1054 NS_ENSURE_TRUE(mDocumentTable
.IsInitialized() || mDocumentTable
.Init(16),
1055 NS_ERROR_OUT_OF_MEMORY
);
1057 NS_ENSURE_TRUE(mDocumentTable
.Put(aDocumentInfo
->DocumentURI(),
1059 NS_ERROR_OUT_OF_MEMORY
);
1065 nsBindingManager::RemoveXBLDocumentInfo(nsIXBLDocumentInfo
* aDocumentInfo
)
1067 if (mDocumentTable
.IsInitialized()) {
1068 mDocumentTable
.Remove(aDocumentInfo
->DocumentURI());
1073 nsBindingManager::GetXBLDocumentInfo(nsIURI
* aURL
)
1075 if (!mDocumentTable
.IsInitialized())
1078 return mDocumentTable
.GetWeak(aURL
);
1082 nsBindingManager::PutLoadingDocListener(nsIURI
* aURL
, nsIStreamListener
* aListener
)
1084 NS_PRECONDITION(aListener
, "Must have a non-null listener!");
1086 NS_ENSURE_TRUE(mLoadingDocTable
.IsInitialized() || mLoadingDocTable
.Init(16),
1087 NS_ERROR_OUT_OF_MEMORY
);
1089 NS_ENSURE_TRUE(mLoadingDocTable
.Put(aURL
, aListener
),
1090 NS_ERROR_OUT_OF_MEMORY
);
1096 nsBindingManager::GetLoadingDocListener(nsIURI
* aURL
)
1098 if (!mLoadingDocTable
.IsInitialized())
1101 return mLoadingDocTable
.GetWeak(aURL
);
1105 nsBindingManager::RemoveLoadingDocListener(nsIURI
* aURL
)
1107 if (mLoadingDocTable
.IsInitialized()) {
1108 mLoadingDocTable
.Remove(aURL
);
1112 PR_STATIC_CALLBACK(PLDHashOperator
)
1113 MarkForDeath(nsISupports
*aKey
, nsXBLBinding
*aBinding
, void* aClosure
)
1115 if (aBinding
->MarkedForDeath())
1116 return PL_DHASH_NEXT
; // Already marked for death.
1119 aBinding
->PrototypeBinding()->DocURI()->GetPath(path
);
1121 if (!strncmp(path
.get(), "/skin", 5))
1122 aBinding
->MarkForDeath();
1124 return PL_DHASH_NEXT
;
1128 nsBindingManager::FlushSkinBindings()
1130 if (mBindingTable
.IsInitialized())
1131 mBindingTable
.EnumerateRead(MarkForDeath
, nsnull
);
1134 // Used below to protect from recurring in QI calls through XPConnect.
1135 struct AntiRecursionData
{
1136 nsIContent
* element
;
1138 AntiRecursionData
* next
;
1140 AntiRecursionData(nsIContent
* aElement
,
1142 AntiRecursionData
* aNext
)
1143 : element(aElement
), iid(aIID
), next(aNext
) {}
1147 nsBindingManager::GetBindingImplementation(nsIContent
* aContent
, REFNSIID aIID
,
1151 nsXBLBinding
*binding
= GetBinding(aContent
);
1153 // The binding should not be asked for nsISupports
1154 NS_ASSERTION(!aIID
.Equals(NS_GET_IID(nsISupports
)), "Asking a binding for nsISupports");
1155 if (binding
->ImplementsInterface(aIID
)) {
1156 nsCOMPtr
<nsIXPConnectWrappedJS
> wrappedJS
= GetWrappedJS(aContent
);
1159 // Protect from recurring in QI calls through XPConnect.
1160 // This can happen when a second binding is being resolved.
1161 // At that point a wrappedJS exists, but it doesn't yet know about
1162 // the iid we are asking for. So, without this protection,
1163 // AggregatedQueryInterface would end up recurring back into itself
1164 // through this code.
1166 // With this protection, when we detect the recursion we return
1167 // NS_NOINTERFACE in the inner call. The outer call will then fall
1168 // through (see below) and build a new chained wrappedJS for the iid.
1170 // We're careful to not assume that only one direct nesting can occur
1171 // because there is a call into JS in the middle and we can't assume
1172 // that this code won't be reached by some more complex nesting path.
1174 // NOTE: We *assume* this is single threaded, so we can use a
1175 // static linked list to do the check.
1177 static AntiRecursionData
* list
= nsnull
;
1179 for (AntiRecursionData
* p
= list
; p
; p
= p
->next
) {
1180 if (p
->element
== aContent
&& p
->iid
.Equals(aIID
)) {
1182 return NS_NOINTERFACE
;
1186 AntiRecursionData
item(aContent
, aIID
, list
);
1189 nsresult rv
= wrappedJS
->AggregatedQueryInterface(aIID
, aResult
);
1196 // No result was found, so this must be another XBL interface.
1197 // Fall through to create a new wrapper.
1200 // We have never made a wrapper for this implementation.
1201 // Create an XPC wrapper for the script object and hand it back.
1203 nsIDocument
* doc
= aContent
->GetOwnerDoc();
1205 return NS_NOINTERFACE
;
1207 nsIScriptGlobalObject
*global
= doc
->GetScriptGlobalObject();
1209 return NS_NOINTERFACE
;
1211 nsIScriptContext
*context
= global
->GetContext();
1213 return NS_NOINTERFACE
;
1215 JSContext
* jscontext
= (JSContext
*)context
->GetNativeContext();
1217 return NS_NOINTERFACE
;
1219 nsIXPConnect
*xpConnect
= nsContentUtils::XPConnect();
1221 nsCOMPtr
<nsIXPConnectWrappedNative
> wrapper
;
1222 xpConnect
->GetWrappedNativeOfNativeObject(jscontext
,
1223 global
->GetGlobalJSObject(),
1225 NS_GET_IID(nsISupports
),
1226 getter_AddRefs(wrapper
));
1227 NS_ENSURE_TRUE(wrapper
, NS_NOINTERFACE
);
1229 JSObject
* jsobj
= nsnull
;
1231 wrapper
->GetJSObject(&jsobj
);
1232 NS_ENSURE_TRUE(jsobj
, NS_NOINTERFACE
);
1234 nsresult rv
= xpConnect
->WrapJSAggregatedToNative(aContent
, jscontext
,
1235 jsobj
, aIID
, aResult
);
1239 // We successfully created a wrapper. We will own this wrapper for as long as the binding remains
1240 // alive. At the time the binding is cleared out of the bindingManager, we will remove the wrapper
1241 // from the bindingManager as well.
1242 nsISupports
* supp
= static_cast<nsISupports
*>(*aResult
);
1243 wrappedJS
= do_QueryInterface(supp
);
1244 SetWrappedJS(aContent
, wrappedJS
);
1251 return NS_NOINTERFACE
;
1255 nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc
,
1256 RuleProcessorData
* aData
,
1257 PRBool
* aCutOffInheritance
)
1259 *aCutOffInheritance
= PR_FALSE
;
1261 if (!aData
->mContent
)
1264 // Walk the binding scope chain, starting with the binding attached to our
1265 // content, up till we run out of scopes or we get cut off.
1266 nsIContent
*content
= aData
->mContent
;
1269 nsXBLBinding
*binding
= GetBinding(content
);
1271 aData
->mScopedRoot
= content
;
1272 binding
->WalkRules(aFunc
, aData
);
1273 // If we're not looking at our original content, allow the binding to cut
1274 // off style inheritance
1275 if (content
!= aData
->mContent
) {
1276 if (!binding
->InheritsStyle()) {
1277 // Go no further; we're not inheriting style from anything above here
1283 if (content
->IsRootOfNativeAnonymousSubtree()) {
1284 break; // Deliberately cut off style inheritance here.
1287 content
= content
->GetBindingParent();
1290 // If "content" is non-null that means we cut off inheritance at some point
1292 *aCutOffInheritance
= (content
!= nsnull
);
1294 // Null out the scoped root that we set repeatedly
1295 aData
->mScopedRoot
= nsnull
;
1300 typedef nsTHashtable
<nsVoidPtrHashKey
> RuleProcessorSet
;
1302 PR_STATIC_CALLBACK(PLDHashOperator
)
1303 EnumRuleProcessors(nsISupports
*aKey
, nsXBLBinding
*aBinding
, void* aClosure
)
1305 RuleProcessorSet
*set
= static_cast<RuleProcessorSet
*>(aClosure
);
1306 for (nsXBLBinding
*binding
= aBinding
; binding
;
1307 binding
= binding
->GetBaseBinding()) {
1308 nsIStyleRuleProcessor
*ruleProc
=
1309 binding
->PrototypeBinding()->GetRuleProcessor();
1311 if (!set
->IsInitialized() && !set
->Init(16))
1312 return PL_DHASH_STOP
;
1313 set
->PutEntry(ruleProc
);
1316 return PL_DHASH_NEXT
;
1319 struct MediumFeaturesChangedData
{
1320 nsPresContext
*mPresContext
;
1321 PRBool
*mRulesChanged
;
1324 PR_STATIC_CALLBACK(PLDHashOperator
)
1325 EnumMediumFeaturesChanged(nsVoidPtrHashKey
*aKey
, void* aClosure
)
1327 nsIStyleRuleProcessor
*ruleProcessor
=
1328 static_cast<nsIStyleRuleProcessor
*>(const_cast<void*>(aKey
->GetKey()));
1329 MediumFeaturesChangedData
*data
=
1330 static_cast<MediumFeaturesChangedData
*>(aClosure
);
1332 PRBool thisChanged
= PR_FALSE
;
1333 ruleProcessor
->MediumFeaturesChanged(data
->mPresContext
, &thisChanged
);
1334 *data
->mRulesChanged
= *data
->mRulesChanged
|| thisChanged
;
1336 return PL_DHASH_NEXT
;
1340 nsBindingManager::MediumFeaturesChanged(nsPresContext
* aPresContext
,
1341 PRBool
* aRulesChanged
)
1343 *aRulesChanged
= PR_FALSE
;
1344 if (!mBindingTable
.IsInitialized())
1347 RuleProcessorSet set
;
1348 mBindingTable
.EnumerateRead(EnumRuleProcessors
, &set
);
1349 if (!set
.IsInitialized())
1352 MediumFeaturesChangedData data
= { aPresContext
, aRulesChanged
};
1353 set
.EnumerateEntries(EnumMediumFeaturesChanged
, &data
);
1358 nsBindingManager::ShouldBuildChildFrames(nsIContent
* aContent
)
1360 nsXBLBinding
*binding
= GetBinding(aContent
);
1362 return !binding
|| binding
->ShouldBuildChildFrames();
1366 nsBindingManager::GetNestedInsertionPoint(nsIContent
* aParent
, nsIContent
* aChild
)
1368 // Check to see if the content is anonymous.
1369 if (aChild
->GetBindingParent() == aParent
)
1370 return nsnull
; // It is anonymous. Don't use the insertion point, since that's only
1371 // for the explicit kids.
1374 nsIContent
*insertionElement
= GetInsertionPoint(aParent
, aChild
, &index
);
1375 if (insertionElement
&& insertionElement
!= aParent
) {
1376 // See if we nest even further in.
1377 nsIContent
* nestedPoint
= GetNestedInsertionPoint(insertionElement
, aChild
);
1379 insertionElement
= nestedPoint
;
1382 return insertionElement
;
1386 nsBindingManager::GetNestedSingleInsertionPoint(nsIContent
* aParent
,
1387 PRBool
* aMultipleInsertionPoints
)
1389 *aMultipleInsertionPoints
= PR_FALSE
;
1392 nsIContent
*insertionElement
=
1393 GetSingleInsertionPoint(aParent
, &index
, aMultipleInsertionPoints
);
1394 if (*aMultipleInsertionPoints
) {
1397 if (insertionElement
&& insertionElement
!= aParent
) {
1398 // See if we nest even further in.
1399 nsIContent
* nestedPoint
=
1400 GetNestedSingleInsertionPoint(insertionElement
,
1401 aMultipleInsertionPoints
);
1403 insertionElement
= nestedPoint
;
1406 return insertionElement
;
1410 nsBindingManager::ContentAppended(nsIDocument
* aDocument
,
1411 nsIContent
* aContainer
,
1412 PRInt32 aNewIndexInContainer
)
1414 // XXX This is hacked and not quite correct. See below.
1415 if (aNewIndexInContainer
!= -1 &&
1416 (mContentListTable
.ops
|| mAnonymousNodesTable
.ops
)) {
1417 // It's not anonymous.
1419 nsIContent
* ins
= GetNestedSingleInsertionPoint(aContainer
, &multiple
);
1422 // Do each kid individually
1423 PRInt32 childCount
= aContainer
->GetChildCount();
1424 NS_ASSERTION(aNewIndexInContainer
>= 0, "Bogus index");
1425 for (PRInt32 idx
= aNewIndexInContainer
; idx
< childCount
; ++idx
) {
1426 HandleChildInsertion(aContainer
, aContainer
->GetChildAt(idx
),
1431 nsCOMPtr
<nsIDOMNodeList
> nodeList
;
1432 PRBool isAnonymousContentList
;
1433 GetXBLChildNodesInternal(ins
, getter_AddRefs(nodeList
),
1434 &isAnonymousContentList
);
1436 if (nodeList
&& isAnonymousContentList
) {
1437 // Find the one non-pseudo-insertion point and just add ourselves.
1438 nsAnonymousContentList
* contentList
=
1439 static_cast<nsAnonymousContentList
*>(nodeList
.get());
1441 PRInt32 count
= contentList
->GetInsertionPointCount();
1442 for (PRInt32 i
= 0; i
< count
; i
++) {
1443 nsXBLInsertionPoint
* point
= contentList
->GetInsertionPointAt(i
);
1444 PRInt32 index
= point
->GetInsertionIndex();
1446 // We're real. Jam all the kids in.
1447 PRInt32 childCount
= aContainer
->GetChildCount();
1448 for (PRInt32 j
= aNewIndexInContainer
; j
< childCount
; j
++) {
1449 nsIContent
* child
= aContainer
->GetChildAt(j
);
1450 point
->AddChild(child
);
1451 SetInsertionParent(child
, ins
);
1462 nsBindingManager::ContentInserted(nsIDocument
* aDocument
,
1463 nsIContent
* aContainer
,
1465 PRInt32 aIndexInContainer
)
1467 // XXX This is hacked just to make menus work again.
1468 if (aIndexInContainer
!= -1 &&
1469 (mContentListTable
.ops
|| mAnonymousNodesTable
.ops
)) {
1470 // It's not anonymous.
1471 NS_ASSERTION(aIndexInContainer
>= 0, "Bogus index");
1472 HandleChildInsertion(aContainer
, aChild
, aIndexInContainer
, PR_FALSE
);
1477 RemoveChildFromInsertionPoint(nsAnonymousContentList
* aInsertionPointList
,
1479 PRBool aRemoveFromPseudoPoints
)
1481 // We need to find the insertion point that contains aChild and remove it
1482 // from that insertion point. Sadly, we don't know which point it is, or
1483 // when we've hit it, but just trying to remove from all the pseudo or
1484 // non-pseudo insertion points, depending on the value of
1485 // aRemoveFromPseudoPoints, should work.
1486 PRInt32 count
= aInsertionPointList
->GetInsertionPointCount();
1487 for (PRInt32 i
= 0; i
< count
; i
++) {
1488 nsXBLInsertionPoint
* point
=
1489 aInsertionPointList
->GetInsertionPointAt(i
);
1490 if ((point
->GetInsertionIndex() == -1) == aRemoveFromPseudoPoints
) {
1491 point
->RemoveChild(aChild
);
1497 nsBindingManager::ContentRemoved(nsIDocument
* aDocument
,
1498 nsIContent
* aContainer
,
1500 PRInt32 aIndexInContainer
)
1502 if (aContainer
&& aIndexInContainer
!= -1 &&
1503 (mContentListTable
.ops
|| mAnonymousNodesTable
.ops
)) {
1504 // It's not anonymous
1505 nsCOMPtr
<nsIContent
> point
= GetNestedInsertionPoint(aContainer
, aChild
);
1508 nsCOMPtr
<nsIDOMNodeList
> nodeList
;
1509 PRBool isAnonymousContentList
;
1510 GetXBLChildNodesInternal(point
, getter_AddRefs(nodeList
),
1511 &isAnonymousContentList
);
1513 if (nodeList
&& isAnonymousContentList
) {
1514 // Find a non-pseudo-insertion point and remove ourselves.
1515 RemoveChildFromInsertionPoint(static_cast<nsAnonymousContentList
*>
1516 (static_cast<nsIDOMNodeList
*>
1520 SetInsertionParent(aChild
, nsnull
);
1524 // Whether the child has a nested insertion point or not, aContainer might
1525 // have insertion points under it. If that's the case, we need to remove
1526 // aChild from the pseudo insertion point it's in.
1527 if (mContentListTable
.ops
) {
1528 nsAnonymousContentList
* insertionPointList
=
1529 static_cast<nsAnonymousContentList
*>(
1530 static_cast<nsIDOMNodeList
*>(LookupObject(mContentListTable
,
1532 if (insertionPointList
) {
1533 RemoveChildFromInsertionPoint(insertionPointList
, aChild
, PR_TRUE
);
1540 nsBindingManager::DropDocumentReference()
1542 // Make sure to not run any more XBL constructors
1543 mProcessingAttachedStack
= PR_TRUE
;
1548 nsBindingManager::Traverse(nsIContent
*aContent
,
1549 nsCycleCollectionTraversalCallback
&cb
)
1551 if (!aContent
->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
1556 if (mInsertionParentTable
.ops
&&
1557 (value
= LookupObject(mInsertionParentTable
, aContent
))) {
1558 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mInsertionParentTable key");
1559 cb
.NoteXPCOMChild(aContent
);
1560 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mInsertionParentTable value");
1561 cb
.NoteXPCOMChild(value
);
1564 if (!aContent
->IsNodeOfType(nsINode::eELEMENT
)) {
1568 nsXBLBinding
*binding
= GetBinding(aContent
);
1570 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mBindingTable key");
1571 cb
.NoteXPCOMChild(aContent
);
1572 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(binding
, nsXBLBinding
,
1573 "[via binding manager] mBindingTable value")
1575 if (mContentListTable
.ops
&&
1576 (value
= LookupObject(mContentListTable
, aContent
))) {
1577 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mContentListTable key");
1578 cb
.NoteXPCOMChild(aContent
);
1579 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mContentListTable value");
1580 cb
.NoteXPCOMChild(value
);
1582 if (mAnonymousNodesTable
.ops
&&
1583 (value
= LookupObject(mAnonymousNodesTable
, aContent
))) {
1584 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mAnonymousNodesTable key");
1585 cb
.NoteXPCOMChild(aContent
);
1586 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mAnonymousNodesTable value");
1587 cb
.NoteXPCOMChild(value
);
1589 if (mWrapperTable
.ops
&&
1590 (value
= LookupObject(mWrapperTable
, aContent
))) {
1591 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mWrapperTable key");
1592 cb
.NoteXPCOMChild(aContent
);
1593 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "[via binding manager] mWrapperTable value");
1594 cb
.NoteXPCOMChild(value
);
1599 nsBindingManager::BeginOutermostUpdate()
1601 mAttachedStackSizeOnOutermost
= mAttachedStack
.Length();
1605 nsBindingManager::EndOutermostUpdate()
1607 if (!mProcessingAttachedStack
) {
1608 ProcessAttachedQueue(mAttachedStackSizeOnOutermost
);
1609 mAttachedStackSizeOnOutermost
= 0;
1614 nsBindingManager::HandleChildInsertion(nsIContent
* aContainer
,
1616 PRUint32 aIndexInContainer
,
1619 NS_PRECONDITION(aChild
, "Must have child");
1620 NS_PRECONDITION(!aContainer
||
1621 PRUint32(aContainer
->IndexOf(aChild
)) == aIndexInContainer
,
1622 "Child not at the right index?");
1624 nsIContent
* ins
= GetNestedInsertionPoint(aContainer
, aChild
);
1627 nsCOMPtr
<nsIDOMNodeList
> nodeList
;
1628 PRBool isAnonymousContentList
;
1629 GetXBLChildNodesInternal(ins
, getter_AddRefs(nodeList
),
1630 &isAnonymousContentList
);
1632 if (nodeList
&& isAnonymousContentList
) {
1633 // Find a non-pseudo-insertion point and just jam ourselves in. This is
1634 // not 100% correct, since there might be multiple insertion points under
1635 // this insertion parent, and we should really be using the one that
1636 // matches our content... Hack city, baby.
1637 nsAnonymousContentList
* contentList
=
1638 static_cast<nsAnonymousContentList
*>(nodeList
.get());
1640 PRInt32 count
= contentList
->GetInsertionPointCount();
1641 for (PRInt32 i
= 0; i
< count
; i
++) {
1642 nsXBLInsertionPoint
* point
= contentList
->GetInsertionPointAt(i
);
1643 if (point
->GetInsertionIndex() != -1) {
1644 // We're real. Jam the kid in.
1646 // Find the right insertion spot. Can't just insert in the insertion
1647 // point at aIndexInContainer since the point may contain anonymous
1648 // content, not all of aContainer's kids, etc. So find the last
1649 // child of aContainer that comes before aIndexInContainer and is in
1650 // the insertion point and insert right after it.
1651 PRInt32 pointSize
= point
->ChildCount();
1652 PRBool inserted
= PR_FALSE
;
1653 for (PRInt32 parentIndex
= aIndexInContainer
- 1;
1654 parentIndex
>= 0 && !inserted
; --parentIndex
) {
1655 nsIContent
* currentSibling
= aContainer
->GetChildAt(parentIndex
);
1656 for (PRInt32 pointIndex
= pointSize
- 1; pointIndex
>= 0;
1658 nsCOMPtr
<nsIContent
> currContent
= point
->ChildAt(pointIndex
);
1659 if (currContent
== currentSibling
) {
1660 point
->InsertChildAt(pointIndex
+ 1, aChild
);
1667 // None of our previous siblings are in here... just stick
1668 // ourselves in at the end of the insertion point if we're
1669 // appending, and at the beginning otherwise.
1670 // XXXbz if we ever start doing the filter thing right, this may be
1671 // no good, since we may _still_ have anonymous kids in there and
1672 // may need to get the ordering with those right.
1674 point
->AddChild(aChild
);
1676 point
->InsertChildAt(0, aChild
);
1679 SetInsertionParent(aChild
, ins
);