Bug 450717 dep file for pixman-mmx.c isn't generated with GCC, r=ted.mielczarek
[wine-gecko.git] / content / xbl / src / nsBindingManager.cpp
blobdb64237c30ecec1cf6b9e60eaf4d29aaf5c65bfd
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"
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"
75 #include "jsapi.h"
76 #include "nsIXPConnect.h"
77 #include "nsDOMCID.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,
96 public nsINodeList
98 public:
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)
115 private:
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);
134 delete mElements;
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);
147 else
148 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeList)
149 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(NodeList)
150 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsAnonymousContentList)
151 NS_INTERFACE_MAP_END
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
164 NS_IMETHODIMP
165 nsAnonymousContentList::GetLength(PRUint32* aLength)
167 NS_ASSERTION(aLength != nsnull, "null ptr");
168 if (! aLength)
169 return NS_ERROR_NULL_POINTER;
171 PRInt32 cnt = mElements->Length();
173 *aLength = 0;
174 for (PRInt32 i = 0; i < cnt; i++)
175 *aLength += static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i))->ChildCount();
177 return NS_OK;
180 NS_IMETHODIMP
181 nsAnonymousContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
183 nsINode* item = GetNodeAt(aIndex);
184 if (!item)
185 return NS_ERROR_FAILURE;
187 return CallQueryInterface(item, aReturn);
190 nsINode*
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);
207 return nsnull;
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
217 public:
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; }
229 private:
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;
245 return PR_TRUE;
250 static PLDHashTableOps ObjectTableOps = {
251 PL_DHashAllocTable,
252 PL_DHashFreeTable,
253 PL_DHashVoidPtrKeyStub,
254 PL_DHashMatchEntryStub,
255 PL_DHashMoveEntryStub,
256 ClearObjectEntry,
257 PL_DHashFinalizeStub,
258 InitObjectEntry
261 // helper routine for adding a new entry
262 static nsresult
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;
268 ObjectEntry *entry =
269 static_cast<ObjectEntry*>
270 (PL_DHashTableOperate(&table, aKey, PL_DHASH_ADD));
272 if (!entry)
273 return NS_ERROR_OUT_OF_MEMORY;
275 // only add the key if the entry is new
276 if (!entry->GetKey())
277 entry->SetKey(aKey);
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);
283 return NS_OK;
286 // helper routine for looking up an existing entry. Note that the
287 // return result is NOT addreffed
288 static nsISupports*
289 LookupObject(PLDHashTable& table, nsIContent* aKey)
291 if (aKey && aKey->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
292 ObjectEntry *entry =
293 static_cast<ObjectEntry*>
294 (PL_DHashTableOperate(&table, aKey, PL_DHASH_LOOKUP));
296 if (PL_DHASH_ENTRY_IS_BUSY(entry))
297 return entry->GetValue();
300 return nsnull;
303 inline void
304 RemoveObjectEntry(PLDHashTable& table, nsISupports* aKey)
306 PL_DHashTableOperate(&table, aKey, PL_DHASH_REMOVE);
309 static nsresult
310 SetOrRemoveObject(PLDHashTable& table, nsIContent* aKey, nsISupports* aValue)
312 if (aValue) {
313 // lazily create the table, but only when adding elements
314 if (!table.ops &&
315 !PL_DHashTableInit(&table, &ObjectTableOps, nsnull,
316 sizeof(ObjectEntry), 16)) {
317 table.ops = nsnull;
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
325 if (table.ops) {
326 ObjectEntry* entry =
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);
336 return NS_OK;
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,
380 void* userArg)
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,
391 void* userArg)
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,
406 nsXBLBinding)
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)
416 NS_INTERFACE_MAP_END
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),
426 mDocument(aDocument)
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);
450 PLDHashOperator
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;
458 static void
459 RemoveInsertionParentForNodeList(nsIDOMNodeList* aList, nsIContent* aParent)
461 nsAnonymousContentList* list = nsnull;
462 if (aList) {
463 CallQueryInterface(aList, &list);
465 if (list) {
466 PRInt32 count = list->GetInsertionPointCount();
467 for (PRInt32 i = 0; i < count; ++i) {
468 nsRefPtr<nsXBLInsertionPoint> currPoint = list->GetInsertionPointAt(i);
469 currPoint->UnbindDefaultContent();
470 #ifdef DEBUG
471 nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
472 NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
473 #endif
474 currPoint->ClearInsertionParent();
476 NS_RELEASE(list);
480 void
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));
497 nsXBLBinding*
498 nsBindingManager::GetBinding(nsIContent* aContent)
500 if (aContent && aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
501 mBindingTable.IsInitialized()) {
502 return mBindingTable.GetWeak(aContent);
505 return nsnull;
508 nsresult
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);
523 if (oldBinding) {
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;
544 if (aBinding) {
545 aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
546 result = mBindingTable.Put(aContent, aBinding);
547 } else {
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
552 // lists.
553 SetWrappedJS(aContent, nsnull);
554 SetContentListFor(aContent, nsnull);
555 SetAnonymousNodesFor(aContent, nsnull);
558 return result ? NS_OK : NS_ERROR_FAILURE;
561 nsIContent*
562 nsBindingManager::GetInsertionParent(nsIContent* aContent)
564 if (mInsertionParentTable.ops) {
565 return static_cast<nsIContent*>
566 (LookupObject(mInsertionParentTable, aContent));
569 return nsnull;
572 nsresult
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!");
578 if (mDestroyed) {
579 return NS_OK;
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));
592 return nsnull;
595 nsresult
596 nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS)
598 if (mDestroyed) {
599 return NS_OK;
602 return SetOrRemoveObject(mWrapperTable, aContent, aWrappedJS);
605 nsresult
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");
614 if (! aOldDocument)
615 return NS_ERROR_NULL_POINTER;
617 // Hold a ref to the binding so it won't die when we remove it from our
618 // table.
619 nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
620 if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
621 nsRefPtr<nsXBLBinding> parentBinding = GetBinding(aContent->GetBindingParent());
622 if (parentBinding) {
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);
633 if (binding) {
634 binding->ChangeDocument(aOldDocument, aNewDocument);
635 SetBinding(aContent, nsnull);
636 if (aNewDocument)
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);
645 return NS_OK;
648 nsIAtom*
649 nsBindingManager::ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID)
651 nsXBLBinding *binding = GetBinding(aContent);
653 if (binding) {
654 nsIAtom* base = binding->GetBaseTag(aNameSpaceID);
656 if (base) {
657 return base;
661 *aNameSpaceID = aContent->GetNameSpaceID();
662 return aContent->Tag();
665 nsresult
666 nsBindingManager::GetContentListFor(nsIContent* aContent, nsIDOMNodeList** aResult)
668 // Locate the primary binding and get its node list of anonymous children.
669 *aResult = nsnull;
671 if (mContentListTable.ops) {
672 *aResult = static_cast<nsIDOMNodeList*>
673 (LookupObject(mContentListTable, aContent));
674 NS_IF_ADDREF(*aResult);
677 if (!*aResult) {
678 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
679 node->GetChildNodes(aResult);
682 return NS_OK;
685 nsresult
686 nsBindingManager::SetContentListFor(nsIContent* aContent,
687 nsInsertionPointList* aList)
689 if (mDestroyed) {
690 return NS_OK;
693 nsIDOMNodeList* contentList = nsnull;
694 if (aList) {
695 contentList = new nsAnonymousContentList(aList);
696 if (!contentList) {
697 delete aList;
698 return NS_ERROR_OUT_OF_MEMORY;
702 return SetOrRemoveObject(mContentListTable, aContent, contentList);
705 PRBool
706 nsBindingManager::HasContentListFor(nsIContent* aContent)
708 return mContentListTable.ops && LookupObject(mContentListTable, aContent);
711 nsresult
712 nsBindingManager::GetAnonymousNodesInternal(nsIContent* aContent,
713 nsIDOMNodeList** aResult,
714 PRBool* aIsAnonymousContentList)
716 // Locate the primary binding and get its node list of anonymous children.
717 *aResult = nsnull;
718 if (mAnonymousNodesTable.ops) {
719 *aResult = static_cast<nsIDOMNodeList*>
720 (LookupObject(mAnonymousNodesTable, aContent));
721 NS_IF_ADDREF(*aResult);
724 if (!*aResult) {
725 *aIsAnonymousContentList = PR_FALSE;
726 nsXBLBinding *binding = GetBinding(aContent);
727 if (binding) {
728 *aResult = binding->GetAnonymousNodes().get();
729 return NS_OK;
731 } else
732 *aIsAnonymousContentList = PR_TRUE;
734 return NS_OK;
737 nsresult
738 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
739 nsIDOMNodeList** aResult)
741 PRBool dummy;
742 return GetAnonymousNodesInternal(aContent, aResult, &dummy);
745 nsresult
746 nsBindingManager::SetAnonymousNodesFor(nsIContent* aContent,
747 nsInsertionPointList* aList)
749 if (mDestroyed) {
750 return NS_OK;
753 nsIDOMNodeList* contentList = nsnull;
754 if (aList) {
755 contentList = new nsAnonymousContentList(aList);
756 if (!contentList) {
757 delete aList;
758 return NS_ERROR_OUT_OF_MEMORY;
762 return SetOrRemoveObject(mAnonymousNodesTable, aContent, contentList);
765 nsresult
766 nsBindingManager::GetXBLChildNodesInternal(nsIContent* aContent,
767 nsIDOMNodeList** aResult,
768 PRBool* aIsAnonymousContentList)
770 *aResult = nsnull;
772 PRUint32 length;
774 // Retrieve the anonymous content that we should build.
775 nsCOMPtr<nsIDOMNodeList> result;
776 GetAnonymousNodesInternal(aContent, getter_AddRefs(result),
777 aIsAnonymousContentList);
778 if (result) {
779 result->GetLength(&length);
780 if (length == 0)
781 result = nsnull;
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
786 // insertion points.
787 if (!result) {
788 if (mContentListTable.ops) {
789 result = static_cast<nsIDOMNodeList*>
790 (LookupObject(mContentListTable, aContent));
791 *aIsAnonymousContentList = PR_TRUE;
795 result.swap(*aResult);
797 return NS_OK;
800 nsresult
801 nsBindingManager::GetXBLChildNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult)
803 PRBool dummy;
804 return GetXBLChildNodesInternal(aContent, aResult, &dummy);
807 nsIContent*
808 nsBindingManager::GetInsertionPoint(nsIContent* aParent, nsIContent* aChild,
809 PRUint32* aIndex)
811 nsXBLBinding *binding = GetBinding(aParent);
812 return binding ? binding->GetInsertionPoint(aChild, aIndex) : nsnull;
815 nsIContent*
816 nsBindingManager::GetSingleInsertionPoint(nsIContent* aParent,
817 PRUint32* aIndex,
818 PRBool* aMultipleInsertionPoints)
820 nsXBLBinding *binding = GetBinding(aParent);
821 if (binding)
822 return binding->GetSingleInsertionPoint(aIndex, aMultipleInsertionPoints);
824 *aMultipleInsertionPoints = PR_FALSE;
825 return nsnull;
828 nsresult
829 nsBindingManager::AddLayeredBinding(nsIContent* aContent, nsIURI* aURL,
830 nsIPrincipal* aOriginPrincipal)
832 // First we need to load our binding.
833 nsresult rv;
834 nsCOMPtr<nsIXBLService> xblService =
835 do_GetService("@mozilla.org/xbl;1", &rv);
836 if (!xblService)
837 return rv;
839 // Load the bindings.
840 nsRefPtr<nsXBLBinding> binding;
841 PRBool dummy;
842 xblService->LoadBindings(aContent, aURL, aOriginPrincipal, PR_TRUE,
843 getter_AddRefs(binding), &dummy);
844 if (binding) {
845 AddToAttachedQueue(binding);
846 ProcessAttachedQueue();
849 return NS_OK;
852 nsresult
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);
858 if (!binding) {
859 return NS_OK;
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();
868 PRBool equalUri;
869 nsresult rv = aURL->Equals(bindingUri, &equalUri);
870 NS_ENSURE_SUCCESS(rv, rv);
871 if (!equalUri) {
872 return NS_OK;
875 // Make sure it isn't a style binding
876 if (binding->IsStyleBinding()) {
877 return NS_OK;
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
884 // ChangeDocument?
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);;
906 nsresult
907 nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc,
908 nsIURI* aURL,
909 nsIPrincipal* aOriginPrincipal)
911 NS_PRECONDITION(aURL, "Must have a URI to load!");
913 // First we need to load our binding.
914 nsresult rv;
915 nsCOMPtr<nsIXBLService> xblService =
916 do_GetService("@mozilla.org/xbl;1", &rv);
917 if (!xblService)
918 return rv;
920 // Load the binding doc.
921 nsCOMPtr<nsIXBLDocumentInfo> info;
922 xblService->LoadBindingDocumentInfo(nsnull, aBoundDoc, aURL,
923 aOriginPrincipal, PR_TRUE,
924 getter_AddRefs(info));
925 if (!info)
926 return NS_ERROR_FAILURE;
928 return NS_OK;
931 nsresult
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();
943 return NS_OK;
947 void
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();
959 void
960 nsBindingManager::DoProcessAttachedQueue()
962 if (!mProcessingAttachedStack) {
963 ProcessAttachedQueue();
965 NS_ASSERTION(mAttachedStack.Length() == 0,
966 "Shouldn't have pending bindings!");
968 mProcessAttachedQueueEvent = nsnull;
969 } else {
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.
977 if (mDocument) {
978 // Hold a strong reference while calling UnblockOnload since that might
979 // run script.
980 nsCOMPtr<nsIDocument> doc = mDocument;
981 doc->UnblockOnload(PR_TRUE);
985 void
986 nsBindingManager::ProcessAttachedQueue(PRUint32 aSkipSize)
988 if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
989 return;
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);
998 if (binding) {
999 binding->ExecuteAttachedHandler();
1003 // If NodeWillBeDestroyed has run we don't want to clobber
1004 // mProcessingAttachedStack set there.
1005 if (mDocument) {
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,
1023 void* aClosure)
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;
1035 void
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();
1049 nsresult
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(),
1058 aDocumentInfo),
1059 NS_ERROR_OUT_OF_MEMORY);
1061 return NS_OK;
1064 void
1065 nsBindingManager::RemoveXBLDocumentInfo(nsIXBLDocumentInfo* aDocumentInfo)
1067 if (mDocumentTable.IsInitialized()) {
1068 mDocumentTable.Remove(aDocumentInfo->DocumentURI());
1072 nsIXBLDocumentInfo*
1073 nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL)
1075 if (!mDocumentTable.IsInitialized())
1076 return nsnull;
1078 return mDocumentTable.GetWeak(aURL);
1081 nsresult
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);
1092 return NS_OK;
1095 nsIStreamListener*
1096 nsBindingManager::GetLoadingDocListener(nsIURI* aURL)
1098 if (!mLoadingDocTable.IsInitialized())
1099 return nsnull;
1101 return mLoadingDocTable.GetWeak(aURL);
1104 void
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.
1118 nsCAutoString path;
1119 aBinding->PrototypeBinding()->DocURI()->GetPath(path);
1121 if (!strncmp(path.get(), "/skin", 5))
1122 aBinding->MarkForDeath();
1124 return PL_DHASH_NEXT;
1127 void
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;
1137 REFNSIID iid;
1138 AntiRecursionData* next;
1140 AntiRecursionData(nsIContent* aElement,
1141 REFNSIID aIID,
1142 AntiRecursionData* aNext)
1143 : element(aElement), iid(aIID), next(aNext) {}
1146 nsresult
1147 nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
1148 void** aResult)
1150 *aResult = nsnull;
1151 nsXBLBinding *binding = GetBinding(aContent);
1152 if (binding) {
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);
1158 if (wrappedJS) {
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)) {
1181 *aResult = nsnull;
1182 return NS_NOINTERFACE;
1186 AntiRecursionData item(aContent, aIID, list);
1187 list = &item;
1189 nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult);
1191 list = item.next;
1193 if (*aResult)
1194 return rv;
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();
1204 if (!doc)
1205 return NS_NOINTERFACE;
1207 nsIScriptGlobalObject *global = doc->GetScriptGlobalObject();
1208 if (!global)
1209 return NS_NOINTERFACE;
1211 nsIScriptContext *context = global->GetContext();
1212 if (!context)
1213 return NS_NOINTERFACE;
1215 JSContext* jscontext = (JSContext*)context->GetNativeContext();
1216 if (!jscontext)
1217 return NS_NOINTERFACE;
1219 nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
1221 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
1222 xpConnect->GetWrappedNativeOfNativeObject(jscontext,
1223 global->GetGlobalJSObject(),
1224 aContent,
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);
1236 if (NS_FAILED(rv))
1237 return rv;
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);
1246 return rv;
1250 *aResult = nsnull;
1251 return NS_NOINTERFACE;
1254 nsresult
1255 nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
1256 RuleProcessorData* aData,
1257 PRBool* aCutOffInheritance)
1259 *aCutOffInheritance = PR_FALSE;
1261 if (!aData->mContent)
1262 return NS_OK;
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;
1268 do {
1269 nsXBLBinding *binding = GetBinding(content);
1270 if (binding) {
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
1278 break;
1283 if (content->IsRootOfNativeAnonymousSubtree()) {
1284 break; // Deliberately cut off style inheritance here.
1287 content = content->GetBindingParent();
1288 } while (content);
1290 // If "content" is non-null that means we cut off inheritance at some point
1291 // in the loop.
1292 *aCutOffInheritance = (content != nsnull);
1294 // Null out the scoped root that we set repeatedly
1295 aData->mScopedRoot = nsnull;
1297 return NS_OK;
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();
1310 if (ruleProc) {
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;
1339 nsresult
1340 nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
1341 PRBool* aRulesChanged)
1343 *aRulesChanged = PR_FALSE;
1344 if (!mBindingTable.IsInitialized())
1345 return NS_OK;
1347 RuleProcessorSet set;
1348 mBindingTable.EnumerateRead(EnumRuleProcessors, &set);
1349 if (!set.IsInitialized())
1350 return NS_OK;
1352 MediumFeaturesChangedData data = { aPresContext, aRulesChanged };
1353 set.EnumerateEntries(EnumMediumFeaturesChanged, &data);
1354 return NS_OK;
1357 PRBool
1358 nsBindingManager::ShouldBuildChildFrames(nsIContent* aContent)
1360 nsXBLBinding *binding = GetBinding(aContent);
1362 return !binding || binding->ShouldBuildChildFrames();
1365 nsIContent*
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.
1373 PRUint32 index;
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);
1378 if (nestedPoint)
1379 insertionElement = nestedPoint;
1382 return insertionElement;
1385 nsIContent*
1386 nsBindingManager::GetNestedSingleInsertionPoint(nsIContent* aParent,
1387 PRBool* aMultipleInsertionPoints)
1389 *aMultipleInsertionPoints = PR_FALSE;
1391 PRUint32 index;
1392 nsIContent *insertionElement =
1393 GetSingleInsertionPoint(aParent, &index, aMultipleInsertionPoints);
1394 if (*aMultipleInsertionPoints) {
1395 return nsnull;
1397 if (insertionElement && insertionElement != aParent) {
1398 // See if we nest even further in.
1399 nsIContent* nestedPoint =
1400 GetNestedSingleInsertionPoint(insertionElement,
1401 aMultipleInsertionPoints);
1402 if (nestedPoint)
1403 insertionElement = nestedPoint;
1406 return insertionElement;
1409 void
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.
1418 PRBool multiple;
1419 nsIContent* ins = GetNestedSingleInsertionPoint(aContainer, &multiple);
1421 if (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),
1427 idx, PR_TRUE);
1430 else if (ins) {
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();
1445 if (index != -1) {
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);
1453 break;
1461 void
1462 nsBindingManager::ContentInserted(nsIDocument* aDocument,
1463 nsIContent* aContainer,
1464 nsIContent* aChild,
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);
1476 static void
1477 RemoveChildFromInsertionPoint(nsAnonymousContentList* aInsertionPointList,
1478 nsIContent* aChild,
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);
1496 void
1497 nsBindingManager::ContentRemoved(nsIDocument* aDocument,
1498 nsIContent* aContainer,
1499 nsIContent* aChild,
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);
1507 if (point) {
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*>
1517 (nodeList)),
1518 aChild,
1519 PR_FALSE);
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,
1531 aContainer)));
1532 if (insertionPointList) {
1533 RemoveChildFromInsertionPoint(insertionPointList, aChild, PR_TRUE);
1539 void
1540 nsBindingManager::DropDocumentReference()
1542 // Make sure to not run any more XBL constructors
1543 mProcessingAttachedStack = PR_TRUE;
1544 mDocument = nsnull;
1547 void
1548 nsBindingManager::Traverse(nsIContent *aContent,
1549 nsCycleCollectionTraversalCallback &cb)
1551 if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
1552 return;
1555 nsISupports *value;
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)) {
1565 return;
1568 nsXBLBinding *binding = GetBinding(aContent);
1569 if (binding) {
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);
1598 void
1599 nsBindingManager::BeginOutermostUpdate()
1601 mAttachedStackSizeOnOutermost = mAttachedStack.Length();
1604 void
1605 nsBindingManager::EndOutermostUpdate()
1607 if (!mProcessingAttachedStack) {
1608 ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
1609 mAttachedStackSizeOnOutermost = 0;
1613 void
1614 nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
1615 nsIContent* aChild,
1616 PRUint32 aIndexInContainer,
1617 PRBool aAppend)
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);
1626 if (ins) {
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;
1657 --pointIndex) {
1658 nsCOMPtr<nsIContent> currContent = point->ChildAt(pointIndex);
1659 if (currContent == currentSibling) {
1660 point->InsertChildAt(pointIndex + 1, aChild);
1661 inserted = PR_TRUE;
1662 break;
1666 if (!inserted) {
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.
1673 if (aAppend) {
1674 point->AddChild(aChild);
1675 } else {
1676 point->InsertChildAt(0, aChild);
1679 SetInsertionParent(aChild, ins);
1680 break;