Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / content / xbl / src / nsBindingManager.cpp
blobd48a3e5feb9a012d60b3506427b2be9e1dc2b440
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
40 #include "nsCOMPtr.h"
41 #include "nsIXBLService.h"
42 #include "nsIInputStream.h"
43 #include "nsDoubleHashtable.h"
44 #include "nsIURI.h"
45 #include "nsIURL.h"
46 #include "nsIChannel.h"
47 #include "nsXPIDLString.h"
48 #include "nsIParser.h"
49 #include "nsParserCIID.h"
50 #include "nsNetUtil.h"
51 #include "plstr.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"
74 #include "jsapi.h"
75 #include "nsIXPConnect.h"
76 #include "nsDOMCID.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
96 public:
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)
113 private:
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);
132 delete mElements;
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)
149 NS_INTERFACE_MAP_END
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
162 NS_IMETHODIMP
163 nsAnonymousContentList::GetLength(PRUint32* aLength)
165 NS_ASSERTION(aLength != nsnull, "null ptr");
166 if (! aLength)
167 return NS_ERROR_NULL_POINTER;
169 PRInt32 cnt = mElements->Length();
171 *aLength = 0;
172 for (PRInt32 i = 0; i < cnt; i++)
173 *aLength += static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i))->ChildCount();
175 return NS_OK;
178 NS_IMETHODIMP
179 nsAnonymousContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
181 nsINode* item = GetNodeAt(aIndex);
182 if (!item)
183 return NS_ERROR_FAILURE;
185 return CallQueryInterface(item, aReturn);
188 nsINode*
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);
205 return nsnull;
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
215 public:
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; }
227 private:
228 nsCOMPtr<nsISupports> mKey;
229 nsCOMPtr<nsISupports> mValue;
232 static void
233 ClearObjectEntry(PLDHashTable* table, PLDHashEntryHdr *entry)
235 ObjectEntry* objEntry = static_cast<ObjectEntry*>(entry);
236 objEntry->~ObjectEntry();
239 static PRBool
240 InitObjectEntry(PLDHashTable* table, PLDHashEntryHdr* entry, const void* key)
242 new (entry) ObjectEntry;
243 return PR_TRUE;
248 static PLDHashTableOps ObjectTableOps = {
249 PL_DHashAllocTable,
250 PL_DHashFreeTable,
251 PL_DHashVoidPtrKeyStub,
252 PL_DHashMatchEntryStub,
253 PL_DHashMoveEntryStub,
254 ClearObjectEntry,
255 PL_DHashFinalizeStub,
256 InitObjectEntry
259 // helper routine for adding a new entry
260 static nsresult
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;
266 ObjectEntry *entry =
267 static_cast<ObjectEntry*>
268 (PL_DHashTableOperate(&table, aKey, PL_DHASH_ADD));
270 if (!entry)
271 return NS_ERROR_OUT_OF_MEMORY;
273 // only add the key if the entry is new
274 if (!entry->GetKey())
275 entry->SetKey(aKey);
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);
281 return NS_OK;
284 // helper routine for looking up an existing entry. Note that the
285 // return result is NOT addreffed
286 static nsISupports*
287 LookupObject(PLDHashTable& table, nsIContent* aKey)
289 if (aKey && aKey->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
290 ObjectEntry *entry =
291 static_cast<ObjectEntry*>
292 (PL_DHashTableOperate(&table, aKey, PL_DHASH_LOOKUP));
294 if (PL_DHASH_ENTRY_IS_BUSY(entry))
295 return entry->GetValue();
298 return nsnull;
301 inline void
302 RemoveObjectEntry(PLDHashTable& table, nsISupports* aKey)
304 PL_DHashTableOperate(&table, aKey, PL_DHASH_REMOVE);
307 static nsresult
308 SetOrRemoveObject(PLDHashTable& table, nsIContent* aKey, nsISupports* aValue)
310 if (aValue) {
311 // lazily create the table, but only when adding elements
312 if (!table.ops &&
313 !PL_DHashTableInit(&table, &ObjectTableOps, nsnull,
314 sizeof(ObjectEntry), 16)) {
315 table.ops = nsnull;
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
323 if (table.ops) {
324 ObjectEntry* entry =
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);
334 return NS_OK;
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,
378 void* userArg)
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,
389 void* userArg)
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,
404 nsXBLBinding)
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)
414 NS_INTERFACE_MAP_END
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),
424 mDocument(aDocument)
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);
448 PLDHashOperator
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;
456 static void
457 RemoveInsertionParentForNodeList(nsIDOMNodeList* aList, nsIContent* aParent)
459 nsAnonymousContentList* list = nsnull;
460 if (aList) {
461 CallQueryInterface(aList, &list);
463 if (list) {
464 PRInt32 count = list->GetInsertionPointCount();
465 for (PRInt32 i = 0; i < count; ++i) {
466 nsRefPtr<nsXBLInsertionPoint> currPoint = list->GetInsertionPointAt(i);
467 currPoint->UnbindDefaultContent();
468 #ifdef DEBUG
469 nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
470 NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
471 #endif
472 currPoint->ClearInsertionParent();
474 NS_RELEASE(list);
478 void
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));
495 nsXBLBinding*
496 nsBindingManager::GetBinding(nsIContent* aContent)
498 if (aContent && aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
499 mBindingTable.IsInitialized()) {
500 return mBindingTable.GetWeak(aContent);
503 return nsnull;
506 nsresult
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);
521 if (oldBinding) {
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;
542 if (aBinding) {
543 aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
544 result = mBindingTable.Put(aContent, aBinding);
545 } else {
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
550 // lists.
551 SetWrappedJS(aContent, nsnull);
552 SetContentListFor(aContent, nsnull);
553 SetAnonymousNodesFor(aContent, nsnull);
556 return result ? NS_OK : NS_ERROR_FAILURE;
559 nsIContent*
560 nsBindingManager::GetInsertionParent(nsIContent* aContent)
562 if (mInsertionParentTable.ops) {
563 return static_cast<nsIContent*>
564 (LookupObject(mInsertionParentTable, aContent));
567 return nsnull;
570 nsresult
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!");
576 if (mDestroyed) {
577 return NS_OK;
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));
590 return nsnull;
593 nsresult
594 nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS)
596 if (mDestroyed) {
597 return NS_OK;
600 return SetOrRemoveObject(mWrapperTable, aContent, aWrappedJS);
603 nsresult
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");
612 if (! aOldDocument)
613 return NS_ERROR_NULL_POINTER;
615 // Hold a ref to the binding so it won't die when we remove it from our
616 // table.
617 nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
618 if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
619 nsRefPtr<nsXBLBinding> parentBinding = GetBinding(aContent->GetBindingParent());
620 if (parentBinding) {
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);
631 if (binding) {
632 binding->ChangeDocument(aOldDocument, aNewDocument);
633 SetBinding(aContent, nsnull);
634 if (aNewDocument)
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);
643 return NS_OK;
646 nsIAtom*
647 nsBindingManager::ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID)
649 nsXBLBinding *binding = GetBinding(aContent);
651 if (binding) {
652 nsIAtom* base = binding->GetBaseTag(aNameSpaceID);
654 if (base) {
655 return base;
659 *aNameSpaceID = aContent->GetNameSpaceID();
660 return aContent->Tag();
663 nsresult
664 nsBindingManager::GetContentListFor(nsIContent* aContent, nsIDOMNodeList** aResult)
666 // Locate the primary binding and get its node list of anonymous children.
667 *aResult = nsnull;
669 if (mContentListTable.ops) {
670 *aResult = static_cast<nsIDOMNodeList*>
671 (LookupObject(mContentListTable, aContent));
672 NS_IF_ADDREF(*aResult);
675 if (!*aResult) {
676 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
677 node->GetChildNodes(aResult);
680 return NS_OK;
683 nsresult
684 nsBindingManager::SetContentListFor(nsIContent* aContent,
685 nsInsertionPointList* aList)
687 if (mDestroyed) {
688 return NS_OK;
691 nsIDOMNodeList* contentList = nsnull;
692 if (aList) {
693 contentList = new nsAnonymousContentList(aList);
694 if (!contentList) {
695 delete aList;
696 return NS_ERROR_OUT_OF_MEMORY;
700 return SetOrRemoveObject(mContentListTable, aContent, contentList);
703 PRBool
704 nsBindingManager::HasContentListFor(nsIContent* aContent)
706 return mContentListTable.ops && LookupObject(mContentListTable, aContent);
709 nsresult
710 nsBindingManager::GetAnonymousNodesInternal(nsIContent* aContent,
711 nsIDOMNodeList** aResult,
712 PRBool* aIsAnonymousContentList)
714 // Locate the primary binding and get its node list of anonymous children.
715 *aResult = nsnull;
716 if (mAnonymousNodesTable.ops) {
717 *aResult = static_cast<nsIDOMNodeList*>
718 (LookupObject(mAnonymousNodesTable, aContent));
719 NS_IF_ADDREF(*aResult);
722 if (!*aResult) {
723 *aIsAnonymousContentList = PR_FALSE;
724 nsXBLBinding *binding = GetBinding(aContent);
725 if (binding) {
726 *aResult = binding->GetAnonymousNodes().get();
727 return NS_OK;
729 } else
730 *aIsAnonymousContentList = PR_TRUE;
732 return NS_OK;
735 nsresult
736 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
737 nsIDOMNodeList** aResult)
739 PRBool dummy;
740 return GetAnonymousNodesInternal(aContent, aResult, &dummy);
743 nsresult
744 nsBindingManager::SetAnonymousNodesFor(nsIContent* aContent,
745 nsInsertionPointList* aList)
747 if (mDestroyed) {
748 return NS_OK;
751 nsIDOMNodeList* contentList = nsnull;
752 if (aList) {
753 contentList = new nsAnonymousContentList(aList);
754 if (!contentList) {
755 delete aList;
756 return NS_ERROR_OUT_OF_MEMORY;
760 return SetOrRemoveObject(mAnonymousNodesTable, aContent, contentList);
763 nsresult
764 nsBindingManager::GetXBLChildNodesInternal(nsIContent* aContent,
765 nsIDOMNodeList** aResult,
766 PRBool* aIsAnonymousContentList)
768 *aResult = nsnull;
770 PRUint32 length;
772 // Retrieve the anonymous content that we should build.
773 nsCOMPtr<nsIDOMNodeList> result;
774 GetAnonymousNodesInternal(aContent, getter_AddRefs(result),
775 aIsAnonymousContentList);
776 if (result) {
777 result->GetLength(&length);
778 if (length == 0)
779 result = nsnull;
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
784 // insertion points.
785 if (!result) {
786 if (mContentListTable.ops) {
787 result = static_cast<nsIDOMNodeList*>
788 (LookupObject(mContentListTable, aContent));
789 *aIsAnonymousContentList = PR_TRUE;
793 result.swap(*aResult);
795 return NS_OK;
798 nsresult
799 nsBindingManager::GetXBLChildNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult)
801 PRBool dummy;
802 return GetXBLChildNodesInternal(aContent, aResult, &dummy);
805 nsIContent*
806 nsBindingManager::GetInsertionPoint(nsIContent* aParent, nsIContent* aChild,
807 PRUint32* aIndex)
809 nsXBLBinding *binding = GetBinding(aParent);
810 return binding ? binding->GetInsertionPoint(aChild, aIndex) : nsnull;
813 nsIContent*
814 nsBindingManager::GetSingleInsertionPoint(nsIContent* aParent,
815 PRUint32* aIndex,
816 PRBool* aMultipleInsertionPoints)
818 nsXBLBinding *binding = GetBinding(aParent);
819 if (binding)
820 return binding->GetSingleInsertionPoint(aIndex, aMultipleInsertionPoints);
822 *aMultipleInsertionPoints = PR_FALSE;
823 return nsnull;
826 nsresult
827 nsBindingManager::AddLayeredBinding(nsIContent* aContent, nsIURI* aURL,
828 nsIPrincipal* aOriginPrincipal)
830 // First we need to load our binding.
831 nsresult rv;
832 nsCOMPtr<nsIXBLService> xblService =
833 do_GetService("@mozilla.org/xbl;1", &rv);
834 if (!xblService)
835 return rv;
837 // Load the bindings.
838 nsRefPtr<nsXBLBinding> binding;
839 PRBool dummy;
840 xblService->LoadBindings(aContent, aURL, aOriginPrincipal, PR_TRUE,
841 getter_AddRefs(binding), &dummy);
842 if (binding) {
843 AddToAttachedQueue(binding);
844 ProcessAttachedQueue();
847 return NS_OK;
850 nsresult
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);
856 if (!binding) {
857 return NS_OK;
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();
866 PRBool equalUri;
867 nsresult rv = aURL->Equals(bindingUri, &equalUri);
868 NS_ENSURE_SUCCESS(rv, rv);
869 if (!equalUri) {
870 return NS_OK;
873 // Make sure it isn't a style binding
874 if (binding->IsStyleBinding()) {
875 return NS_OK;
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
882 // ChangeDocument?
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);;
904 nsresult
905 nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc,
906 nsIURI* aURL,
907 nsIPrincipal* aOriginPrincipal)
909 NS_PRECONDITION(aURL, "Must have a URI to load!");
911 // First we need to load our binding.
912 nsresult rv;
913 nsCOMPtr<nsIXBLService> xblService =
914 do_GetService("@mozilla.org/xbl;1", &rv);
915 if (!xblService)
916 return rv;
918 // Load the binding doc.
919 nsCOMPtr<nsIXBLDocumentInfo> info;
920 xblService->LoadBindingDocumentInfo(nsnull, aBoundDoc, aURL,
921 aOriginPrincipal, PR_TRUE,
922 getter_AddRefs(info));
923 if (!info)
924 return NS_ERROR_FAILURE;
926 return NS_OK;
929 nsresult
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();
941 return NS_OK;
945 void
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();
957 void
958 nsBindingManager::DoProcessAttachedQueue()
960 if (!mProcessingAttachedStack) {
961 ProcessAttachedQueue();
963 NS_ASSERTION(mAttachedStack.Length() == 0,
964 "Shouldn't have pending bindings!");
966 mProcessAttachedQueueEvent = nsnull;
967 } else {
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.
975 if (mDocument) {
976 // Hold a strong reference while calling UnblockOnload since that might
977 // run script.
978 nsCOMPtr<nsIDocument> doc = mDocument;
979 doc->UnblockOnload(PR_TRUE);
983 void
984 nsBindingManager::ProcessAttachedQueue(PRUint32 aSkipSize)
986 if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
987 return;
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);
996 if (binding) {
997 binding->ExecuteAttachedHandler();
1001 // If NodeWillBeDestroyed has run we don't want to clobber
1002 // mProcessingAttachedStack set there.
1003 if (mDocument) {
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,
1021 void* aClosure)
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;
1033 void
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();
1047 nsresult
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(),
1056 aDocumentInfo),
1057 NS_ERROR_OUT_OF_MEMORY);
1059 return NS_OK;
1062 void
1063 nsBindingManager::RemoveXBLDocumentInfo(nsIXBLDocumentInfo* aDocumentInfo)
1065 if (mDocumentTable.IsInitialized()) {
1066 mDocumentTable.Remove(aDocumentInfo->DocumentURI());
1070 nsIXBLDocumentInfo*
1071 nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL)
1073 if (!mDocumentTable.IsInitialized())
1074 return nsnull;
1076 return mDocumentTable.GetWeak(aURL);
1079 nsresult
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);
1090 return NS_OK;
1093 nsIStreamListener*
1094 nsBindingManager::GetLoadingDocListener(nsIURI* aURL)
1096 if (!mLoadingDocTable.IsInitialized())
1097 return nsnull;
1099 return mLoadingDocTable.GetWeak(aURL);
1102 void
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.
1116 nsCAutoString path;
1117 aBinding->PrototypeBinding()->DocURI()->GetPath(path);
1119 if (!strncmp(path.get(), "/skin", 5))
1120 aBinding->MarkForDeath();
1122 return PL_DHASH_NEXT;
1125 void
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;
1135 REFNSIID iid;
1136 AntiRecursionData* next;
1138 AntiRecursionData(nsIContent* aElement,
1139 REFNSIID aIID,
1140 AntiRecursionData* aNext)
1141 : element(aElement), iid(aIID), next(aNext) {}
1144 nsresult
1145 nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
1146 void** aResult)
1148 *aResult = nsnull;
1149 nsXBLBinding *binding = GetBinding(aContent);
1150 if (binding) {
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);
1156 if (wrappedJS) {
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)) {
1179 *aResult = nsnull;
1180 return NS_NOINTERFACE;
1184 AntiRecursionData item(aContent, aIID, list);
1185 list = &item;
1187 nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult);
1189 list = item.next;
1191 if (*aResult)
1192 return rv;
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();
1202 if (!doc)
1203 return NS_NOINTERFACE;
1205 nsIScriptGlobalObject *global = doc->GetScriptGlobalObject();
1206 if (!global)
1207 return NS_NOINTERFACE;
1209 nsIScriptContext *context = global->GetContext();
1210 if (!context)
1211 return NS_NOINTERFACE;
1213 JSContext* jscontext = (JSContext*)context->GetNativeContext();
1214 if (!jscontext)
1215 return NS_NOINTERFACE;
1217 nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
1219 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
1220 xpConnect->GetWrappedNativeOfNativeObject(jscontext,
1221 global->GetGlobalJSObject(),
1222 aContent,
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);
1234 if (NS_FAILED(rv))
1235 return rv;
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);
1244 return rv;
1248 *aResult = nsnull;
1249 return NS_NOINTERFACE;
1252 nsresult
1253 nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
1254 RuleProcessorData* aData,
1255 PRBool* aCutOffInheritance)
1257 *aCutOffInheritance = PR_FALSE;
1259 if (!aData->mContent)
1260 return NS_OK;
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;
1266 do {
1267 nsXBLBinding *binding = GetBinding(content);
1268 if (binding) {
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
1276 break;
1281 if (content->IsRootOfNativeAnonymousSubtree()) {
1282 break; // Deliberately cut off style inheritance here.
1285 content = content->GetBindingParent();
1286 } while (content);
1288 // If "content" is non-null that means we cut off inheritance at some point
1289 // in the loop.
1290 *aCutOffInheritance = (content != nsnull);
1292 // Null out the scoped root that we set repeatedly
1293 aData->mScopedRoot = nsnull;
1295 return NS_OK;
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();
1308 if (ruleProc) {
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;
1337 nsresult
1338 nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
1339 PRBool* aRulesChanged)
1341 *aRulesChanged = PR_FALSE;
1342 if (!mBindingTable.IsInitialized())
1343 return NS_OK;
1345 RuleProcessorSet set;
1346 mBindingTable.EnumerateRead(EnumRuleProcessors, &set);
1347 if (!set.IsInitialized())
1348 return NS_OK;
1350 MediumFeaturesChangedData data = { aPresContext, aRulesChanged };
1351 set.EnumerateEntries(EnumMediumFeaturesChanged, &data);
1352 return NS_OK;
1355 PRBool
1356 nsBindingManager::ShouldBuildChildFrames(nsIContent* aContent)
1358 nsXBLBinding *binding = GetBinding(aContent);
1360 return !binding || binding->ShouldBuildChildFrames();
1363 nsIContent*
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.
1371 PRUint32 index;
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);
1376 if (nestedPoint)
1377 insertionElement = nestedPoint;
1380 return insertionElement;
1383 nsIContent*
1384 nsBindingManager::GetNestedSingleInsertionPoint(nsIContent* aParent,
1385 PRBool* aMultipleInsertionPoints)
1387 *aMultipleInsertionPoints = PR_FALSE;
1389 PRUint32 index;
1390 nsIContent *insertionElement =
1391 GetSingleInsertionPoint(aParent, &index, aMultipleInsertionPoints);
1392 if (*aMultipleInsertionPoints) {
1393 return nsnull;
1395 if (insertionElement && insertionElement != aParent) {
1396 // See if we nest even further in.
1397 nsIContent* nestedPoint =
1398 GetNestedSingleInsertionPoint(insertionElement,
1399 aMultipleInsertionPoints);
1400 if (nestedPoint)
1401 insertionElement = nestedPoint;
1404 return insertionElement;
1407 void
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.
1416 PRBool multiple;
1417 nsIContent* ins = GetNestedSingleInsertionPoint(aContainer, &multiple);
1419 if (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),
1425 idx, PR_TRUE);
1428 else if (ins) {
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();
1443 if (index != -1) {
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);
1451 break;
1459 void
1460 nsBindingManager::ContentInserted(nsIDocument* aDocument,
1461 nsIContent* aContainer,
1462 nsIContent* aChild,
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);
1474 static void
1475 RemoveChildFromInsertionPoint(nsAnonymousContentList* aInsertionPointList,
1476 nsIContent* aChild,
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);
1494 void
1495 nsBindingManager::ContentRemoved(nsIDocument* aDocument,
1496 nsIContent* aContainer,
1497 nsIContent* aChild,
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);
1505 if (point) {
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*>
1515 (nodeList)),
1516 aChild,
1517 PR_FALSE);
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,
1529 aContainer)));
1530 if (insertionPointList) {
1531 RemoveChildFromInsertionPoint(insertionPointList, aChild, PR_TRUE);
1537 void
1538 nsBindingManager::DropDocumentReference()
1540 // Make sure to not run any more XBL constructors
1541 mProcessingAttachedStack = PR_TRUE;
1542 mDocument = nsnull;
1545 void
1546 nsBindingManager::Traverse(nsIContent *aContent,
1547 nsCycleCollectionTraversalCallback &cb)
1549 if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
1550 return;
1553 nsISupports *value;
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)) {
1563 return;
1566 nsXBLBinding *binding = GetBinding(aContent);
1567 if (binding) {
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);
1596 void
1597 nsBindingManager::BeginOutermostUpdate()
1599 mAttachedStackSizeOnOutermost = mAttachedStack.Length();
1602 void
1603 nsBindingManager::EndOutermostUpdate()
1605 if (!mProcessingAttachedStack) {
1606 ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
1607 mAttachedStackSizeOnOutermost = 0;
1611 void
1612 nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
1613 nsIContent* aChild,
1614 PRUint32 aIndexInContainer,
1615 PRBool aAppend)
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);
1624 if (ins) {
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;
1655 --pointIndex) {
1656 nsCOMPtr<nsIContent> currContent = point->ChildAt(pointIndex);
1657 if (currContent == currentSibling) {
1658 point->InsertChildAt(pointIndex + 1, aChild);
1659 inserted = PR_TRUE;
1660 break;
1664 if (!inserted) {
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.
1671 if (aAppend) {
1672 point->AddChild(aChild);
1673 } else {
1674 point->InsertChildAt(0, aChild);
1677 SetInsertionParent(aChild, ins);
1678 break;