From cb1e3536d6ada446a8869f59aac5793fd2d8db2d Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Tue, 15 Aug 2006 22:41:58 +0000 Subject: [PATCH] Make it possible for document observers who need to worry about XBL to observe the binding manager and make presshell do that. Bug 348573, r+sr=sicking --- content/base/src/nsDocument.cpp | 13 +- content/base/src/nsDocument.h | 26 +- content/base/src/nsGenericDOMDataNode.cpp | 1 - content/base/src/nsNodeUtils.cpp | 101 ++----- content/base/src/nsTObserverArray.h | 224 ---------------- content/xbl/public/nsIBindingManager.h | 20 +- content/xbl/src/nsBindingManager.cpp | 304 ++++++++++++++++------ content/xbl/src/nsBindingManager.h | 33 +-- layout/base/nsPresShell.cpp | 4 +- 9 files changed, 296 insertions(+), 430 deletions(-) diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 3da6566c1aa..831bc7e56f7 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -825,18 +825,19 @@ nsDocument::Init() NS_ENSURE_TRUE(bindingManager, NS_ERROR_OUT_OF_MEMORY); mBindingManager = bindingManager; - // The binding manager must always be the first observer of the document. - mObservers.PrependObserver(bindingManager); + if (!mObservers.PrependObserver(bindingManager)) { + return NS_ERROR_OUT_OF_MEMORY; + } + nsINode::nsSlots* slots = GetSlots(); NS_ENSURE_TRUE(slots && slots->mMutationObservers.PrependObserver(bindingManager), NS_ERROR_OUT_OF_MEMORY); + // Prepend self as mutation-observer whether we need it or not (some // subclasses currently do, other don't). This is because the code in - // nsNodeUtils always notifies the first observer first, even when going - // backwards, expecting the first observer to be the document. - // If we remove that hack, we can move the below registring out to the leaf - // classes. + // nsNodeUtils always notifies the first observer first, expecting the + // first observer to be the document. NS_ENSURE_TRUE(slots->mMutationObservers.PrependObserver(this), NS_ERROR_OUT_OF_MEMORY); diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 6b1e749fabc..d341d7594d0 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -672,28 +672,10 @@ protected: // Dispatch an event to the ScriptGlobalObject for this document void DispatchEventToWindow(nsEvent *aEvent); - // NS_DOCUMENT_NOTIFY_OBSERVERS goes backwards for now for backwards compat. - // If you change this, update ContentAppended/Inserted/Removed accordingly. -#define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_) \ - do { \ - nsTObserverArray::ReverseIterator \ - iter_(mObservers); \ - nsCOMPtr obs_; \ - while ((obs_ = iter_.GetNext())) { \ - obs_ -> func_ params_ ; \ - } \ - } while (0) - -#define NS_DOCUMENT_FORWARD_NOTIFY_OBSERVERS(func_, params_) \ - do { \ - nsTObserverArray::ForwardIterator \ - iter_(mObservers); \ - nsCOMPtr obs_; \ - while ((obs_ = iter_.GetNext())) { \ - obs_ -> func_ params_ ; \ - } \ - } while (0) - +#define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_) \ + NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(mObservers, nsIDocumentObserver, \ + func_, params_); + #ifdef DEBUG void VerifyRootContentState(); #endif diff --git a/content/base/src/nsGenericDOMDataNode.cpp b/content/base/src/nsGenericDOMDataNode.cpp index b8657b9aea6..4197e125982 100644 --- a/content/base/src/nsGenericDOMDataNode.cpp +++ b/content/base/src/nsGenericDOMDataNode.cpp @@ -403,7 +403,6 @@ nsGenericDOMDataNode::AppendData(const nsAString& aData) // Apparently this is called often enough that we don't want to just simply // call SetText like ReplaceData does. See bug 77585 and comment in // ReplaceData. - nsIDocument *document = GetCurrentDoc(); // FIXME, but 330872: We can't call BeginUpdate here because it confuses the // poor little nsHTMLContentSink. diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index 8d0e326eb56..56616ea987f 100755 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -42,7 +42,8 @@ #include "nsIMutationObserver.h" #include "nsIDocument.h" -#define IMPL_MUTATION_NOTIFICATION_FW(func_, content_, params_) \ +#define IMPL_MUTATION_NOTIFICATION(func_, content_, params_) \ + PR_BEGIN_MACRO \ nsINode* node = content_; \ nsINode* prev; \ do { \ @@ -50,12 +51,9 @@ if (slots && !slots->mMutationObservers.IsEmpty()) { \ /* No need to explicitly notify the first observer first \ since that'll happen anyway. */ \ - nsTObserverArray::ForwardIterator \ - iter_(slots->mMutationObservers); \ - nsCOMPtr obs_; \ - while ((obs_ = iter_.GetNext())) { \ - obs_-> func_ params_; \ - } \ + NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS( \ + slots->mMutationObservers, nsIMutationObserver, \ + func_, params_); \ } \ prev = node; \ node = node->GetNodeParent(); \ @@ -66,51 +64,16 @@ need to notify the document */ \ node = NS_STATIC_CAST(nsIContent*, prev)->GetCurrentDoc(); \ } \ - } while (node); - - - -#define IMPL_MUTATION_NOTIFICATION_BW(func_, content_, params_) \ - nsINode* node = content_; \ - nsINode* prev; \ - do { \ - nsINode::nsSlots* slots = node->GetExistingSlots(); \ - if (slots && !slots->mMutationObservers.IsEmpty()) { \ - /* \ - * Notify the first observer first even if we're doing a \ - * reverse walk. This is since we want to notify the \ - * document first when |node| is a document. \ - * This may not actually be needed, but it's safer for now. \ - * This can be removed once we always walk forward \ - */ \ - nsIMutationObserver* first = \ - slots->mMutationObservers.SafeObserverAt(0); \ - first-> func_ params_; \ - nsTObserverArray::ReverseIterator \ - iter_(slots->mMutationObservers); \ - nsCOMPtr obs_; \ - while ((obs_ = iter_.GetNext()) != first) { \ - obs_-> func_ params_; \ - } \ - } \ - prev = node; \ - node = node->GetNodeParent(); \ - \ - if (!node && prev->IsNodeOfType(nsINode::eXUL)) { \ - /* XUL elements can have the in-document flag set, but \ - still be in an orphaned subtree. In this case we \ - need to notify the document */ \ - node = NS_STATIC_CAST(nsIContent*, prev)->GetCurrentDoc(); \ - } \ - } while (node); + } while (node); \ + PR_END_MACRO void nsNodeUtils::CharacterDataChanged(nsIContent* aContent, PRBool aAppend) { nsIDocument* doc = aContent->GetOwnerDoc(); - IMPL_MUTATION_NOTIFICATION_BW(CharacterDataChanged, aContent, - (doc, aContent, aAppend)); + IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent, + (doc, aContent, aAppend)); } void @@ -120,9 +83,9 @@ nsNodeUtils::AttributeChanged(nsIContent* aContent, PRInt32 aModType) { nsIDocument* doc = aContent->GetOwnerDoc(); - IMPL_MUTATION_NOTIFICATION_BW(AttributeChanged, aContent, - (doc, aContent, aNameSpaceID, aAttribute, - aModType)); + IMPL_MUTATION_NOTIFICATION(AttributeChanged, aContent, + (doc, aContent, aNameSpaceID, aAttribute, + aModType)); } void @@ -131,14 +94,8 @@ nsNodeUtils::ContentAppended(nsIContent* aContainer, { nsIDocument* document = aContainer->GetOwnerDoc(); - // XXXdwh There is a hacky ordering dependency between the binding - // manager and the frame constructor that forces us to walk the - // observer list in a forward order - // XXXldb So one should notify the other rather than both being - // registered. - - IMPL_MUTATION_NOTIFICATION_FW(ContentAppended, aContainer, - (document, aContainer, aNewIndexInContainer)); + IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer, + (document, aContainer, aNewIndexInContainer)); } void @@ -160,14 +117,8 @@ nsNodeUtils::ContentInserted(nsINode* aContainer, document = NS_STATIC_CAST(nsIDocument*, aContainer); } - // XXXdwh There is a hacky ordering dependency between the binding manager - // and the frame constructor that forces us to walk the observer list - // in a forward order - // XXXldb So one should notify the other rather than both being - // registered. - - IMPL_MUTATION_NOTIFICATION_FW(ContentInserted, aContainer, - (document, container, aChild, aIndexInContainer)); + IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer, + (document, container, aChild, aIndexInContainer)); } void @@ -189,15 +140,8 @@ nsNodeUtils::ContentRemoved(nsINode* aContainer, document = NS_STATIC_CAST(nsIDocument*, aContainer); } - // XXXdwh There is a hacky ordering dependency between the binding - // manager and the frame constructor that forces us to walk the - // observer list in a reverse order - // XXXldb So one should notify the other rather than both being - // registered. - - - IMPL_MUTATION_NOTIFICATION_BW(ContentRemoved, aContainer, - (document, container, aChild, aIndexInContainer)); + IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer, + (document, container, aChild, aIndexInContainer)); } void @@ -206,12 +150,9 @@ nsNodeUtils::NodeWillBeDestroyed(nsINode* aNode) nsINode::nsSlots* slots = aNode->GetExistingSlots(); if (slots) { if (!slots->mMutationObservers.IsEmpty()) { - nsTObserverArray::ForwardIterator - iter(slots->mMutationObservers); - nsCOMPtr obs; - while ((obs = iter.GetNext())) { - obs->NodeWillBeDestroyed(aNode); - } + NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers, + nsIMutationObserver, + NodeWillBeDestroyed, (aNode)); } PtrBits flags = slots->mFlags | NODE_DOESNT_HAVE_SLOTS; diff --git a/content/base/src/nsTObserverArray.h b/content/base/src/nsTObserverArray.h index d70fe6a6a65..e69de29bb2d 100755 --- a/content/base/src/nsTObserverArray.h +++ b/content/base/src/nsTObserverArray.h @@ -1,224 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla.org code. - * - * The Initial Developer of the Original Code is Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Jonas Sicking (Original Author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nsTObserverArray_h___ -#define nsTObserverArray_h___ - -#include "nsVoidArray.h" - -class nsTObserverArray_base { - public: - class Iterator_base; - friend class Iterator_base; - - class Iterator_base { - protected: - friend class nsTObserverArray_base; - - Iterator_base(PRInt32 aPosition, nsTObserverArray_base& aArray) - : mPosition(aPosition), - mNext(aArray.mIterators), - mArray(aArray) { - aArray.mIterators = this; - } - - ~Iterator_base() { - NS_ASSERTION(mArray.mIterators == this, - "Iterators must currently be destroyed in opposite order " - "from the construction order. It is suggested that you " - "simply put them on the stack"); - mArray.mIterators = mNext; - } - - // This function exists solely to avoid having to make the subclasses - // into friends of nsTObserverArray_base - void* GetSafeElementAt(PRInt32 aIndex) { - return mArray.mObservers.SafeElementAt(aIndex); - } - - // The current position of the iterator. It's exact meaning differs - // depending on if the array is iterated forwards or backwards. See - // nsTObserverArray::ForwardIterator and - // nsTObserverArray::ReverseIterator - PRInt32 mPosition; - - // The next iterator currently iterating the same array - Iterator_base* mNext; - - // The array we're iterating - nsTObserverArray_base& mArray; - }; - - /** - * Removes all observers and collapses all iterators to the beginning of - * the array. The result is that forward iterators will see all elements - * in the array, and backward iterators will not see any more elements. - */ - void Clear(); - - protected: - nsTObserverArray_base() - : mIterators(nsnull) { - } - - /** - * Adjusts iterators after an element has been inserted or removed - * from the array. - * @param modPos Position where elements were added or removed. - * @param adjustment -1 if an element was removed, 1 if an element was - * added. - */ - void AdjustIterators(PRInt32 aModPos, PRInt32 aAdjustment); - - Iterator_base* mIterators; - nsVoidArray mObservers; -}; - -/** - * An array of observers. Like a normal array, but supports iterators that are - * stable even if the array is modified during iteration. - * The template parameter is the type of observer the array will hold pointers - * to. - */ - -template -class nsTObserverArray : public nsTObserverArray_base { - public: - - /** - * Adds an observer to the beginning of the array - * @param aObserver Observer to add - */ - PRBool PrependObserver(T* aObserver) { - NS_PRECONDITION(!Contains(aObserver), - "Don't prepend if the observer is already in the list"); - - PRBool res = mObservers.InsertElementAt(aObserver, 0); - if (res) { - AdjustIterators(0, 1); - } - return res; - } - - /** - * Adds an observer to the end of the array unless it already exists in - * the array. - * @param aObserver Observer to add - * @return True on success, false otherwise - */ - PRBool AppendObserver(T* aObserver) { - return Contains(aObserver) || mObservers.AppendElement(aObserver); - } - - /** - * Removes an observer from the array - * @param aObserver Observer to remove - * @return True if observer was found and removed, false otherwise - */ - PRBool RemoveObserver(T* aObserver) { - PRInt32 index = mObservers.IndexOf(aObserver); - if (index < 0) { - return PR_FALSE; - } - - mObservers.RemoveElementAt(index); - AdjustIterators(index, -1); - - return PR_TRUE; - } - - PRBool Contains(T* aObserver) const { - return mObservers.IndexOf(aObserver) >= 0; - } - - PRBool IsEmpty() const { - return mObservers.Count() == 0; - } - - T* SafeObserverAt(PRInt32 aIndex) { - return NS_STATIC_CAST(T*, mObservers.SafeElementAt(aIndex)); - } - - /** - * Iterators - */ - - // Iterates the array forward from beginning to end. - // mPosition points to the element that will be returned on next call - // to GetNext - class ForwardIterator : public nsTObserverArray_base::Iterator_base { - public: - ForwardIterator(nsTObserverArray& aArray) - : Iterator_base(0, aArray) { - } - - /** - * Returns the next element and steps one step. - * Returns null if there are no more observers. Once null is returned - * the iterator becomes invalid and GetNext must not be called any more. - * @return The next observer. - */ - T* GetNext() { - return NS_STATIC_CAST(T*, GetSafeElementAt(mPosition++)); - } - }; - - class ReverseIterator; - friend class ReverseIterator; - - // Iterates the array backwards from end to beginning - // mPosition points to the element that was returned from last call to - // GetNext - class ReverseIterator : public nsTObserverArray_base::Iterator_base { - public: - ReverseIterator(nsTObserverArray& aArray) - : Iterator_base(aArray.mObservers.Count(), aArray) { - } - - /** - * Returns the next element and steps one step. - * Returns null if there are no more observers. Once null is returned - * the iterator becomes invalid and GetNext must not be called any more. - * @return The next observer. - */ - T* GetNext() { - return NS_STATIC_CAST(T*, GetSafeElementAt(--mPosition)); - } - }; -}; - -#endif // nsTObserverArray_h___ diff --git a/content/xbl/public/nsIBindingManager.h b/content/xbl/public/nsIBindingManager.h index 0027199fb0e..1fdc3b6b1a9 100644 --- a/content/xbl/public/nsIBindingManager.h +++ b/content/xbl/public/nsIBindingManager.h @@ -57,9 +57,11 @@ class nsIURI; class nsIXPConnectWrappedJS; class nsIDOMNodeList; class nsVoidArray; +class nsIDocumentObserver; #define NS_IBINDING_MANAGER_IID \ -{ 0x92281eaa, 0x89c4, 0x4457, { 0x8f, 0x8d, 0xca, 0x92, 0xbf, 0xbe, 0x0f, 0x50 } } +{ 0x8186980b, 0x35b8, 0x469f, \ + { 0x8b, 0xc5, 0x33, 0xad, 0x3c, 0x35, 0x90, 0x98 } } class nsIBindingManager : public nsISupports { @@ -176,6 +178,22 @@ public: NS_IMETHOD GetBindingImplementation(nsIContent* aContent, REFNSIID aIID, void** aResult)=0; NS_IMETHOD ShouldBuildChildFrames(nsIContent* aContent, PRBool* aResult) = 0; + + /** + * Add a new observer of document change notifications. Whenever content is + * changed, appended, inserted or removed the observers are informed. This + * is like nsIDocument::AddObserver, but these observers will be notified + * after the XBL data structures are updated for + * ContentInserted/ContentAppended and before they're updated for + * ContentRemoved. + */ + virtual void AddObserver(nsIDocumentObserver* aObserver) = 0; + + /** + * Remove an observer of document change notifications. This will + * return false if the observer cannot be found. + */ + virtual PRBool RemoveObserver(nsIDocumentObserver* aObserver) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIBindingManager, NS_IBINDING_MANAGER_IID) diff --git a/content/xbl/src/nsBindingManager.cpp b/content/xbl/src/nsBindingManager.cpp index 4b93f588a06..f1ade97dfbc 100644 --- a/content/xbl/src/nsBindingManager.cpp +++ b/content/xbl/src/nsBindingManager.cpp @@ -1138,51 +1138,127 @@ nsBindingManager::GetNestedInsertionPoint(nsIContent* aParent, nsIContent* aChil return NS_OK; } +// Note: We don't hold a reference to the document observer; we assume +// that it has a live reference to the document. +void +nsBindingManager::AddObserver(nsIDocumentObserver* aObserver) +{ + // The array makes sure the observer isn't already in the list + mObservers.AppendObserver(aObserver); +} + +PRBool +nsBindingManager::RemoveObserver(nsIDocumentObserver* aObserver) +{ + return mObservers.RemoveObserver(aObserver); +} + +void +nsBindingManager::BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(BeginUpdate, (aDocument, aUpdateType)); +} + +void +nsBindingManager::EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(EndUpdate, (aDocument, aUpdateType)); +} + +void +nsBindingManager::BeginLoad(nsIDocument* aDocument) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(BeginLoad, (aDocument)); +} + +void +nsBindingManager::EndLoad(nsIDocument* aDocument) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(EndLoad, (aDocument)); +} + +void +nsBindingManager::CharacterDataChanged(nsIDocument* aDocument, + nsIContent* aContent, + PRBool aAppend) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(CharacterDataChanged, + (aDocument, aContent, aAppend)); +} + +void +nsBindingManager::ContentStatesChanged(nsIDocument* aDocument, + nsIContent* aContent1, + nsIContent* aContent2, + PRInt32 aStateMask) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentStatesChanged, + (aDocument, aContent1, aContent2, + aStateMask)); +} + +void +nsBindingManager::AttributeChanged(nsIDocument* aDocument, + nsIContent* aContent, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute, + PRInt32 aModType) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(AttributeChanged, + (aDocument, aContent, aNameSpaceID, + aAttribute, aModType)); +} + void nsBindingManager::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer, PRInt32 aNewIndexInContainer) { // XXX This is hacked and not quite correct. See below. - if (aNewIndexInContainer == -1 || !mContentListTable.ops) - // It's anonymous. - return; + if (aNewIndexInContainer != -1 && mContentListTable.ops) { + // It's not anonymous. + PRInt32 childCount = aContainer->GetChildCount(); - PRInt32 childCount = aContainer->GetChildCount(); + nsIContent *child = aContainer->GetChildAt(aNewIndexInContainer); - nsIContent *child = aContainer->GetChildAt(aNewIndexInContainer); + nsCOMPtr ins; + GetNestedInsertionPoint(aContainer, child, getter_AddRefs(ins)); - nsCOMPtr ins; - GetNestedInsertionPoint(aContainer, child, getter_AddRefs(ins)); + if (ins) { + nsCOMPtr nodeList; + PRBool isAnonymousContentList; + GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList), + &isAnonymousContentList); - if (ins) { - nsCOMPtr nodeList; - PRBool isAnonymousContentList; - GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList), - &isAnonymousContentList); + if (nodeList && isAnonymousContentList) { + // Find a non-pseudo-insertion point and just jam ourselves in. + // This is not 100% correct. Hack city, baby. + nsAnonymousContentList* contentList = + NS_STATIC_CAST(nsAnonymousContentList*, + NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get())); - if (nodeList && isAnonymousContentList) { - // Find a non-pseudo-insertion point and just jam ourselves in. - // This is not 100% correct. Hack city, baby. - nsAnonymousContentList* contentList = NS_STATIC_CAST(nsAnonymousContentList*, NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get())); - - PRInt32 count = contentList->GetInsertionPointCount(); - for (PRInt32 i = 0; i < count; i++) { - nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i); - PRInt32 index = point->GetInsertionIndex(); - if (index != -1) { - // We're real. Jam all the kids in. - // XXX Check the filters to find the correct points. - for (PRInt32 j = aNewIndexInContainer; j < childCount; j++) { - child = aContainer->GetChildAt(j); - point->AddChild(child); - SetInsertionParent(child, ins); + PRInt32 count = contentList->GetInsertionPointCount(); + for (PRInt32 i = 0; i < count; i++) { + nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i); + PRInt32 index = point->GetInsertionIndex(); + if (index != -1) { + // We're real. Jam all the kids in. + // XXX Check the filters to find the correct points. + for (PRInt32 j = aNewIndexInContainer; j < childCount; j++) { + child = aContainer->GetChildAt(j); + point->AddChild(child); + SetInsertionParent(child, ins); + } + break; } - break; } } } } + + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentAppended, + (aDocument, aContainer, + aNewIndexInContainer)); } void @@ -1191,66 +1267,71 @@ nsBindingManager::ContentInserted(nsIDocument* aDocument, nsIContent* aChild, PRInt32 aIndexInContainer) { -// XXX This is hacked just to make menus work again. - if (aIndexInContainer == -1 || !mContentListTable.ops) - // It's anonymous. - return; + // XXX This is hacked just to make menus work again. + if (aIndexInContainer != -1 && mContentListTable.ops) { + // It's not anonymous. + nsCOMPtr ins; + GetNestedInsertionPoint(aContainer, aChild, getter_AddRefs(ins)); - nsCOMPtr ins; - GetNestedInsertionPoint(aContainer, aChild, getter_AddRefs(ins)); + if (ins) { + nsCOMPtr nodeList; + PRBool isAnonymousContentList; + GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList), + &isAnonymousContentList); - if (ins) { - nsCOMPtr nodeList; - PRBool isAnonymousContentList; - GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList), - &isAnonymousContentList); + if (nodeList && isAnonymousContentList) { + // Find a non-pseudo-insertion point and just jam ourselves in. + // This is not 100% correct. Hack city, baby. + nsAnonymousContentList* contentList = + NS_STATIC_CAST(nsAnonymousContentList*, + NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get())); - if (nodeList && isAnonymousContentList) { - // Find a non-pseudo-insertion point and just jam ourselves in. - // This is not 100% correct. Hack city, baby. - nsAnonymousContentList* contentList = NS_STATIC_CAST(nsAnonymousContentList*, NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get())); + PRInt32 count = contentList->GetInsertionPointCount(); + for (PRInt32 i = 0; i < count; i++) { + nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i); + if (point->GetInsertionIndex() != -1) { + // We're real. Jam the kid in. + // XXX Check the filters to find the correct points. - PRInt32 count = contentList->GetInsertionPointCount(); - for (PRInt32 i = 0; i < count; i++) { - nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i); - if (point->GetInsertionIndex() != -1) { - // We're real. Jam the kid in. - // XXX Check the filters to find the correct points. - - // Find the right insertion spot. Can't just insert in the insertion - // point at aIndexInContainer since the point may contain anonymous - // content, not all of aContainer's kids, etc. So find the last - // child of aContainer that comes before aIndexInContainer and is in - // the insertion point and insert right after it. - PRInt32 pointSize = point->ChildCount(); - PRBool inserted = PR_FALSE; - for (PRInt32 parentIndex = aIndexInContainer - 1; - parentIndex >= 0 && !inserted; --parentIndex) { - nsIContent* currentSibling = aContainer->GetChildAt(parentIndex); - for (PRInt32 pointIndex = pointSize - 1; pointIndex >= 0; - --pointIndex) { - nsCOMPtr currContent = point->ChildAt(pointIndex); - if (currContent == currentSibling) { - point->InsertChildAt(pointIndex + 1, aChild); - inserted = PR_TRUE; - break; + // Find the right insertion spot. Can't just insert in the insertion + // point at aIndexInContainer since the point may contain anonymous + // content, not all of aContainer's kids, etc. So find the last + // child of aContainer that comes before aIndexInContainer and is in + // the insertion point and insert right after it. + PRInt32 pointSize = point->ChildCount(); + PRBool inserted = PR_FALSE; + for (PRInt32 parentIndex = aIndexInContainer - 1; + parentIndex >= 0 && !inserted; --parentIndex) { + nsIContent* currentSibling = aContainer->GetChildAt(parentIndex); + for (PRInt32 pointIndex = pointSize - 1; pointIndex >= 0; + --pointIndex) { + nsCOMPtr currContent = point->ChildAt(pointIndex); + if (currContent == currentSibling) { + point->InsertChildAt(pointIndex + 1, aChild); + inserted = PR_TRUE; + break; + } } } + if (!inserted) { + // None of our previous siblings are in here... just stick + // ourselves in at the beginning of the insertion point. + // XXXbz if we ever start doing the filter thing right, this may be + // no good, since we may _still_ have anonymous kids in there and + // may need to get the ordering with those right. + point->InsertChildAt(0, aChild); + } + SetInsertionParent(aChild, ins); + break; } - if (!inserted) { - // None of our previous siblings are in here... just stick - // ourselves in at the beginning of the insertion point. - // XXXbz if we ever start doing the filter thing right, this may be - // no good, since we may _still_ have anonymous kids in there and - // may need to get the ordering with those right. - point->InsertChildAt(0, aChild); - } - SetInsertionParent(aChild, ins); - break; } } } } + + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentInserted, + (aDocument, aContainer, aChild, + aIndexInContainer)); } void @@ -1259,6 +1340,10 @@ nsBindingManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aChild, PRInt32 aIndexInContainer) { + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentRemoved, + (aDocument, aContainer, aChild, + aIndexInContainer)); + if (aIndexInContainer == -1 || !mContentListTable.ops) // It's anonymous. return; @@ -1285,3 +1370,66 @@ nsBindingManager::ContentRemoved(nsIDocument* aDocument, } } } + +void +nsBindingManager::NodeWillBeDestroyed(const nsINode *aNode) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(NodeWillBeDestroyed, (aNode)); +} + +void +nsBindingManager::StyleSheetAdded(nsIDocument* aDocument, + nsIStyleSheet* aStyleSheet, + PRBool aDocumentSheet) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(StyleSheetAdded, + (aDocument, aStyleSheet, aDocumentSheet)); +} + +void +nsBindingManager::StyleSheetRemoved(nsIDocument* aDocument, + nsIStyleSheet* aStyleSheet, + PRBool aDocumentSheet) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(StyleSheetRemoved, + (aDocument, aStyleSheet, aDocumentSheet)); +} + +void +nsBindingManager::StyleSheetApplicableStateChanged(nsIDocument* aDocument, + nsIStyleSheet* aStyleSheet, + PRBool aApplicable) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged, + (aDocument, aStyleSheet, aApplicable)); +} + +void +nsBindingManager::StyleRuleChanged(nsIDocument* aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aOldStyleRule, + nsIStyleRule* aNewStyleRule) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(StyleRuleChanged, + (aDocument, aStyleSheet, aOldStyleRule, + aNewStyleRule)); +} + +void +nsBindingManager::StyleRuleAdded(nsIDocument* aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(StyleRuleAdded, + (aDocument, aStyleSheet, aStyleRule)); +} + +void +nsBindingManager::StyleRuleRemoved(nsIDocument* aDocument, + nsIStyleSheet* aStyleSheet, + nsIStyleRule* aStyleRule) +{ + NS_BINDINGMANAGER_NOTIFY_OBSERVERS(StyleRuleRemoved, + (aDocument, aStyleSheet, aStyleRule)); +} + diff --git a/content/xbl/src/nsBindingManager.h b/content/xbl/src/nsBindingManager.h index fa7ad8cda10..9c237ed3fab 100755 --- a/content/xbl/src/nsBindingManager.h +++ b/content/xbl/src/nsBindingManager.h @@ -61,11 +61,12 @@ class nsStyleSet; class nsBindingManager : public nsIBindingManager, public nsIStyleRuleSupplier, - public nsStubDocumentObserver + public nsIDocumentObserver { - NS_DECL_ISUPPORTS - public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOCUMENTOBSERVER + nsBindingManager(); ~nsBindingManager(); @@ -123,25 +124,16 @@ public: NS_IMETHOD ShouldBuildChildFrames(nsIContent* aContent, PRBool* aResult); + virtual NS_HIDDEN_(void) AddObserver(nsIDocumentObserver* aObserver); + + virtual NS_HIDDEN_(PRBool) RemoveObserver(nsIDocumentObserver* aObserver); + // nsIStyleRuleSupplier NS_IMETHOD WalkRules(nsStyleSet* aStyleSet, nsIStyleRuleProcessor::EnumFunc aFunc, RuleProcessorData* aData, PRBool* aCutOffInheritance); - // nsIDocumentObserver - virtual void ContentAppended(nsIDocument* aDocument, - nsIContent* aContainer, - PRInt32 aNewIndexInContainer); - virtual void ContentInserted(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - PRInt32 aIndexInContainer); - virtual void ContentRemoved(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - PRInt32 aIndexInContainer); - protected: nsresult GetXBLChildNodesInternal(nsIContent* aContent, nsIDOMNodeList** aResult, @@ -156,6 +148,10 @@ protected: nsresult GetNestedInsertionPoint(nsIContent* aParent, nsIContent* aChild, nsIContent** aResult); +#define NS_BINDINGMANAGER_NOTIFY_OBSERVERS(func_, params_) \ + NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(mObservers, nsIDocumentObserver, \ + func_, params_); + // MEMBER VARIABLES protected: // A mapping from nsIContent* to the nsXBLBinding* that is @@ -201,6 +197,11 @@ protected: // table, they have not yet finished loading. nsInterfaceHashtable mLoadingDocTable; + // Array of document observers who would like to be notified of content + // appends/inserts after we update our data structures and of content removes + // before we do so. + nsTObserverArray mObservers; + // A queue of binding attached event handlers that are awaiting execution. nsVoidArray mAttachedStack; PRBool mProcessingAttachedStack; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index eeb3255094b..8b97a20afe4 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -2700,7 +2700,7 @@ NS_IMETHODIMP PresShell::BeginObservingDocument() { if (mDocument) { - mDocument->AddObserver(this); + mDocument->BindingManager()->AddObserver(this); if (mIsDocumentGone) { NS_WARNING("Adding a presshell that was disconnected from the document " "as a document observer? Sounds wrong..."); @@ -2718,7 +2718,7 @@ PresShell::EndObservingDocument() // is gone, perhaps? Except for printing it's NOT gone, sometimes. mIsDocumentGone = PR_TRUE; if (mDocument) { - mDocument->RemoveObserver(this); + mDocument->BindingManager()->RemoveObserver(this); } return NS_OK; }