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
This commit is contained in:
bzbarsky@mit.edu
2006-08-15 22:41:58 +00:00
parent d8261a7a02
commit 43fc6354d1
9 changed files with 307 additions and 229 deletions

View File

@@ -825,18 +825,19 @@ nsDocument::Init()
NS_ENSURE_TRUE(bindingManager, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(bindingManager, NS_ERROR_OUT_OF_MEMORY);
mBindingManager = bindingManager; mBindingManager = bindingManager;
// The binding manager must always be the first observer of the document. if (!mObservers.PrependObserver(bindingManager)) {
mObservers.PrependObserver(bindingManager); return NS_ERROR_OUT_OF_MEMORY;
}
nsINode::nsSlots* slots = GetSlots(); nsINode::nsSlots* slots = GetSlots();
NS_ENSURE_TRUE(slots && NS_ENSURE_TRUE(slots &&
slots->mMutationObservers.PrependObserver(bindingManager), slots->mMutationObservers.PrependObserver(bindingManager),
NS_ERROR_OUT_OF_MEMORY); NS_ERROR_OUT_OF_MEMORY);
// Prepend self as mutation-observer whether we need it or not (some // Prepend self as mutation-observer whether we need it or not (some
// subclasses currently do, other don't). This is because the code in // subclasses currently do, other don't). This is because the code in
// nsNodeUtils always notifies the first observer first, even when going // nsNodeUtils always notifies the first observer first, expecting the
// backwards, expecting the first observer to be the document. // first observer to be the document.
// If we remove that hack, we can move the below registring out to the leaf
// classes.
NS_ENSURE_TRUE(slots->mMutationObservers.PrependObserver(this), NS_ENSURE_TRUE(slots->mMutationObservers.PrependObserver(this),
NS_ERROR_OUT_OF_MEMORY); NS_ERROR_OUT_OF_MEMORY);

View File

@@ -672,27 +672,9 @@ protected:
// Dispatch an event to the ScriptGlobalObject for this document // Dispatch an event to the ScriptGlobalObject for this document
void DispatchEventToWindow(nsEvent *aEvent); void DispatchEventToWindow(nsEvent *aEvent);
// NS_DOCUMENT_NOTIFY_OBSERVERS goes backwards for now for backwards compat. #define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_) \
// If you change this, update ContentAppended/Inserted/Removed accordingly. NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(mObservers, nsIDocumentObserver, \
#define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_) \ func_, params_);
do { \
nsTObserverArray<nsIDocumentObserver>::ReverseIterator \
iter_(mObservers); \
nsCOMPtr<nsIDocumentObserver> obs_; \
while ((obs_ = iter_.GetNext())) { \
obs_ -> func_ params_ ; \
} \
} while (0)
#define NS_DOCUMENT_FORWARD_NOTIFY_OBSERVERS(func_, params_) \
do { \
nsTObserverArray<nsIDocumentObserver>::ForwardIterator \
iter_(mObservers); \
nsCOMPtr<nsIDocumentObserver> obs_; \
while ((obs_ = iter_.GetNext())) { \
obs_ -> func_ params_ ; \
} \
} while (0)
#ifdef DEBUG #ifdef DEBUG
void VerifyRootContentState(); void VerifyRootContentState();

View File

@@ -403,7 +403,6 @@ nsGenericDOMDataNode::AppendData(const nsAString& aData)
// Apparently this is called often enough that we don't want to just simply // 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 // call SetText like ReplaceData does. See bug 77585 and comment in
// ReplaceData. // ReplaceData.
nsIDocument *document = GetCurrentDoc();
// FIXME, but 330872: We can't call BeginUpdate here because it confuses the // FIXME, but 330872: We can't call BeginUpdate here because it confuses the
// poor little nsHTMLContentSink. // poor little nsHTMLContentSink.

View File

@@ -42,7 +42,8 @@
#include "nsIMutationObserver.h" #include "nsIMutationObserver.h"
#include "nsIDocument.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* node = content_; \
nsINode* prev; \ nsINode* prev; \
do { \ do { \
@@ -50,12 +51,9 @@
if (slots && !slots->mMutationObservers.IsEmpty()) { \ if (slots && !slots->mMutationObservers.IsEmpty()) { \
/* No need to explicitly notify the first observer first \ /* No need to explicitly notify the first observer first \
since that'll happen anyway. */ \ since that'll happen anyway. */ \
nsTObserverArray<nsIMutationObserver>::ForwardIterator \ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS( \
iter_(slots->mMutationObservers); \ slots->mMutationObservers, nsIMutationObserver, \
nsCOMPtr<nsIMutationObserver> obs_; \ func_, params_); \
while ((obs_ = iter_.GetNext())) { \
obs_-> func_ params_; \
} \
} \ } \
prev = node; \ prev = node; \
node = node->GetNodeParent(); \ node = node->GetNodeParent(); \
@@ -66,51 +64,16 @@
need to notify the document */ \ need to notify the document */ \
node = NS_STATIC_CAST(nsIContent*, prev)->GetCurrentDoc(); \ node = NS_STATIC_CAST(nsIContent*, prev)->GetCurrentDoc(); \
} \ } \
} while (node); } while (node); \
PR_END_MACRO
#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<nsIMutationObserver>::ReverseIterator \
iter_(slots->mMutationObservers); \
nsCOMPtr<nsIMutationObserver> 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);
void void
nsNodeUtils::CharacterDataChanged(nsIContent* aContent, PRBool aAppend) nsNodeUtils::CharacterDataChanged(nsIContent* aContent, PRBool aAppend)
{ {
nsIDocument* doc = aContent->GetOwnerDoc(); nsIDocument* doc = aContent->GetOwnerDoc();
IMPL_MUTATION_NOTIFICATION_BW(CharacterDataChanged, aContent, IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent,
(doc, aContent, aAppend)); (doc, aContent, aAppend));
} }
void void
@@ -120,9 +83,9 @@ nsNodeUtils::AttributeChanged(nsIContent* aContent,
PRInt32 aModType) PRInt32 aModType)
{ {
nsIDocument* doc = aContent->GetOwnerDoc(); nsIDocument* doc = aContent->GetOwnerDoc();
IMPL_MUTATION_NOTIFICATION_BW(AttributeChanged, aContent, IMPL_MUTATION_NOTIFICATION(AttributeChanged, aContent,
(doc, aContent, aNameSpaceID, aAttribute, (doc, aContent, aNameSpaceID, aAttribute,
aModType)); aModType));
} }
void void
@@ -131,14 +94,8 @@ nsNodeUtils::ContentAppended(nsIContent* aContainer,
{ {
nsIDocument* document = aContainer->GetOwnerDoc(); nsIDocument* document = aContainer->GetOwnerDoc();
// XXXdwh There is a hacky ordering dependency between the binding IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer,
// manager and the frame constructor that forces us to walk the (document, aContainer, aNewIndexInContainer));
// 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));
} }
void void
@@ -160,14 +117,8 @@ nsNodeUtils::ContentInserted(nsINode* aContainer,
document = NS_STATIC_CAST(nsIDocument*, aContainer); document = NS_STATIC_CAST(nsIDocument*, aContainer);
} }
// XXXdwh There is a hacky ordering dependency between the binding manager IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer,
// and the frame constructor that forces us to walk the observer list (document, container, aChild, aIndexInContainer));
// 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));
} }
void void
@@ -189,15 +140,8 @@ nsNodeUtils::ContentRemoved(nsINode* aContainer,
document = NS_STATIC_CAST(nsIDocument*, aContainer); document = NS_STATIC_CAST(nsIDocument*, aContainer);
} }
// XXXdwh There is a hacky ordering dependency between the binding IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer,
// manager and the frame constructor that forces us to walk the (document, container, aChild, aIndexInContainer));
// 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));
} }
void void
@@ -206,12 +150,9 @@ nsNodeUtils::NodeWillBeDestroyed(nsINode* aNode)
nsINode::nsSlots* slots = aNode->GetExistingSlots(); nsINode::nsSlots* slots = aNode->GetExistingSlots();
if (slots) { if (slots) {
if (!slots->mMutationObservers.IsEmpty()) { if (!slots->mMutationObservers.IsEmpty()) {
nsTObserverArray<nsIMutationObserver>::ForwardIterator NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers,
iter(slots->mMutationObservers); nsIMutationObserver,
nsCOMPtr<nsIMutationObserver> obs; NodeWillBeDestroyed, (aNode));
while ((obs = iter.GetNext())) {
obs->NodeWillBeDestroyed(aNode);
}
} }
PtrBits flags = slots->mFlags | NODE_DOESNT_HAVE_SLOTS; PtrBits flags = slots->mFlags | NODE_DOESNT_HAVE_SLOTS;

View File

@@ -196,29 +196,17 @@ class nsTObserverArray : public nsTObserverArray_base {
return NS_STATIC_CAST(T*, GetSafeElementAt(mPosition++)); 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<T>& 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));
}
};
}; };
// XXXbz I wish I didn't have to pass in the observer type, but I
// don't see a way to get it out of array_.
#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
PR_BEGIN_MACRO \
nsTObserverArray<obstype_>::ForwardIterator iter_(array_); \
nsCOMPtr<obstype_> obs_; \
while ((obs_ = iter_.GetNext())) { \
obs_ -> func_ params_ ; \
} \
PR_END_MACRO
#endif // nsTObserverArray_h___ #endif // nsTObserverArray_h___

View File

@@ -57,9 +57,11 @@ class nsIURI;
class nsIXPConnectWrappedJS; class nsIXPConnectWrappedJS;
class nsIDOMNodeList; class nsIDOMNodeList;
class nsVoidArray; class nsVoidArray;
class nsIDocumentObserver;
#define NS_IBINDING_MANAGER_IID \ #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 class nsIBindingManager : public nsISupports
{ {
@@ -176,6 +178,22 @@ public:
NS_IMETHOD GetBindingImplementation(nsIContent* aContent, REFNSIID aIID, void** aResult)=0; NS_IMETHOD GetBindingImplementation(nsIContent* aContent, REFNSIID aIID, void** aResult)=0;
NS_IMETHOD ShouldBuildChildFrames(nsIContent* aContent, PRBool* 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) NS_DEFINE_STATIC_IID_ACCESSOR(nsIBindingManager, NS_IBINDING_MANAGER_IID)

View File

@@ -1138,51 +1138,127 @@ nsBindingManager::GetNestedInsertionPoint(nsIContent* aParent, nsIContent* aChil
return NS_OK; 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 void
nsBindingManager::ContentAppended(nsIDocument* aDocument, nsBindingManager::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer, nsIContent* aContainer,
PRInt32 aNewIndexInContainer) PRInt32 aNewIndexInContainer)
{ {
// XXX This is hacked and not quite correct. See below. // XXX This is hacked and not quite correct. See below.
if (aNewIndexInContainer == -1 || !mContentListTable.ops) if (aNewIndexInContainer != -1 && mContentListTable.ops) {
// It's anonymous. // It's not anonymous.
return; PRInt32 childCount = aContainer->GetChildCount();
PRInt32 childCount = aContainer->GetChildCount(); nsIContent *child = aContainer->GetChildAt(aNewIndexInContainer);
nsIContent *child = aContainer->GetChildAt(aNewIndexInContainer); nsCOMPtr<nsIContent> ins;
GetNestedInsertionPoint(aContainer, child, getter_AddRefs(ins));
nsCOMPtr<nsIContent> ins; if (ins) {
GetNestedInsertionPoint(aContainer, child, getter_AddRefs(ins)); nsCOMPtr<nsIDOMNodeList> nodeList;
PRBool isAnonymousContentList;
GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList),
&isAnonymousContentList);
if (ins) { if (nodeList && isAnonymousContentList) {
nsCOMPtr<nsIDOMNodeList> nodeList; // Find a non-pseudo-insertion point and just jam ourselves in.
PRBool isAnonymousContentList; // This is not 100% correct. Hack city, baby.
GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList), nsAnonymousContentList* contentList =
&isAnonymousContentList); NS_STATIC_CAST(nsAnonymousContentList*,
NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get()));
if (nodeList && isAnonymousContentList) { PRInt32 count = contentList->GetInsertionPointCount();
// Find a non-pseudo-insertion point and just jam ourselves in. for (PRInt32 i = 0; i < count; i++) {
// This is not 100% correct. Hack city, baby. nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i);
nsAnonymousContentList* contentList = NS_STATIC_CAST(nsAnonymousContentList*, NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get())); PRInt32 index = point->GetInsertionIndex();
if (index != -1) {
PRInt32 count = contentList->GetInsertionPointCount(); // We're real. Jam all the kids in.
for (PRInt32 i = 0; i < count; i++) { // XXX Check the filters to find the correct points.
nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i); for (PRInt32 j = aNewIndexInContainer; j < childCount; j++) {
PRInt32 index = point->GetInsertionIndex(); child = aContainer->GetChildAt(j);
if (index != -1) { point->AddChild(child);
// We're real. Jam all the kids in. SetInsertionParent(child, ins);
// XXX Check the filters to find the correct points. }
for (PRInt32 j = aNewIndexInContainer; j < childCount; j++) { break;
child = aContainer->GetChildAt(j);
point->AddChild(child);
SetInsertionParent(child, ins);
} }
break;
} }
} }
} }
} }
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentAppended,
(aDocument, aContainer,
aNewIndexInContainer));
} }
void void
@@ -1191,66 +1267,71 @@ nsBindingManager::ContentInserted(nsIDocument* aDocument,
nsIContent* aChild, nsIContent* aChild,
PRInt32 aIndexInContainer) PRInt32 aIndexInContainer)
{ {
// XXX This is hacked just to make menus work again. // XXX This is hacked just to make menus work again.
if (aIndexInContainer == -1 || !mContentListTable.ops) if (aIndexInContainer != -1 && mContentListTable.ops) {
// It's anonymous. // It's not anonymous.
return; nsCOMPtr<nsIContent> ins;
GetNestedInsertionPoint(aContainer, aChild, getter_AddRefs(ins));
nsCOMPtr<nsIContent> ins; if (ins) {
GetNestedInsertionPoint(aContainer, aChild, getter_AddRefs(ins)); nsCOMPtr<nsIDOMNodeList> nodeList;
PRBool isAnonymousContentList;
GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList),
&isAnonymousContentList);
if (ins) { if (nodeList && isAnonymousContentList) {
nsCOMPtr<nsIDOMNodeList> nodeList; // Find a non-pseudo-insertion point and just jam ourselves in.
PRBool isAnonymousContentList; // This is not 100% correct. Hack city, baby.
GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList), nsAnonymousContentList* contentList =
&isAnonymousContentList); NS_STATIC_CAST(nsAnonymousContentList*,
NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get()));
if (nodeList && isAnonymousContentList) { PRInt32 count = contentList->GetInsertionPointCount();
// Find a non-pseudo-insertion point and just jam ourselves in. for (PRInt32 i = 0; i < count; i++) {
// This is not 100% correct. Hack city, baby. nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i);
nsAnonymousContentList* contentList = NS_STATIC_CAST(nsAnonymousContentList*, NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get())); if (point->GetInsertionIndex() != -1) {
// We're real. Jam the kid in.
// XXX Check the filters to find the correct points.
PRInt32 count = contentList->GetInsertionPointCount(); // Find the right insertion spot. Can't just insert in the insertion
for (PRInt32 i = 0; i < count; i++) { // point at aIndexInContainer since the point may contain anonymous
nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i); // content, not all of aContainer's kids, etc. So find the last
if (point->GetInsertionIndex() != -1) { // child of aContainer that comes before aIndexInContainer and is in
// We're real. Jam the kid in. // the insertion point and insert right after it.
// XXX Check the filters to find the correct points. PRInt32 pointSize = point->ChildCount();
PRBool inserted = PR_FALSE;
// Find the right insertion spot. Can't just insert in the insertion for (PRInt32 parentIndex = aIndexInContainer - 1;
// point at aIndexInContainer since the point may contain anonymous parentIndex >= 0 && !inserted; --parentIndex) {
// content, not all of aContainer's kids, etc. So find the last nsIContent* currentSibling = aContainer->GetChildAt(parentIndex);
// child of aContainer that comes before aIndexInContainer and is in for (PRInt32 pointIndex = pointSize - 1; pointIndex >= 0;
// the insertion point and insert right after it. --pointIndex) {
PRInt32 pointSize = point->ChildCount(); nsCOMPtr<nsIContent> currContent = point->ChildAt(pointIndex);
PRBool inserted = PR_FALSE; if (currContent == currentSibling) {
for (PRInt32 parentIndex = aIndexInContainer - 1; point->InsertChildAt(pointIndex + 1, aChild);
parentIndex >= 0 && !inserted; --parentIndex) { inserted = PR_TRUE;
nsIContent* currentSibling = aContainer->GetChildAt(parentIndex); break;
for (PRInt32 pointIndex = pointSize - 1; pointIndex >= 0; }
--pointIndex) {
nsCOMPtr<nsIContent> 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 void
@@ -1259,6 +1340,10 @@ nsBindingManager::ContentRemoved(nsIDocument* aDocument,
nsIContent* aChild, nsIContent* aChild,
PRInt32 aIndexInContainer) PRInt32 aIndexInContainer)
{ {
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentRemoved,
(aDocument, aContainer, aChild,
aIndexInContainer));
if (aIndexInContainer == -1 || !mContentListTable.ops) if (aIndexInContainer == -1 || !mContentListTable.ops)
// It's anonymous. // It's anonymous.
return; 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));
}

View File

@@ -61,11 +61,12 @@ class nsStyleSet;
class nsBindingManager : public nsIBindingManager, class nsBindingManager : public nsIBindingManager,
public nsIStyleRuleSupplier, public nsIStyleRuleSupplier,
public nsStubDocumentObserver public nsIDocumentObserver
{ {
NS_DECL_ISUPPORTS
public: public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOCUMENTOBSERVER
nsBindingManager(); nsBindingManager();
~nsBindingManager(); ~nsBindingManager();
@@ -123,25 +124,16 @@ public:
NS_IMETHOD ShouldBuildChildFrames(nsIContent* aContent, PRBool* aResult); NS_IMETHOD ShouldBuildChildFrames(nsIContent* aContent, PRBool* aResult);
virtual NS_HIDDEN_(void) AddObserver(nsIDocumentObserver* aObserver);
virtual NS_HIDDEN_(PRBool) RemoveObserver(nsIDocumentObserver* aObserver);
// nsIStyleRuleSupplier // nsIStyleRuleSupplier
NS_IMETHOD WalkRules(nsStyleSet* aStyleSet, NS_IMETHOD WalkRules(nsStyleSet* aStyleSet,
nsIStyleRuleProcessor::EnumFunc aFunc, nsIStyleRuleProcessor::EnumFunc aFunc,
RuleProcessorData* aData, RuleProcessorData* aData,
PRBool* aCutOffInheritance); 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: protected:
nsresult GetXBLChildNodesInternal(nsIContent* aContent, nsresult GetXBLChildNodesInternal(nsIContent* aContent,
nsIDOMNodeList** aResult, nsIDOMNodeList** aResult,
@@ -156,6 +148,10 @@ protected:
nsresult GetNestedInsertionPoint(nsIContent* aParent, nsIContent* aChild, nsIContent** aResult); 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 // MEMBER VARIABLES
protected: protected:
// A mapping from nsIContent* to the nsXBLBinding* that is // A mapping from nsIContent* to the nsXBLBinding* that is
@@ -201,6 +197,11 @@ protected:
// table, they have not yet finished loading. // table, they have not yet finished loading.
nsInterfaceHashtable<nsURIHashKey,nsIStreamListener> mLoadingDocTable; nsInterfaceHashtable<nsURIHashKey,nsIStreamListener> 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<nsIDocumentObserver> mObservers;
// A queue of binding attached event handlers that are awaiting execution. // A queue of binding attached event handlers that are awaiting execution.
nsVoidArray mAttachedStack; nsVoidArray mAttachedStack;
PRBool mProcessingAttachedStack; PRBool mProcessingAttachedStack;

View File

@@ -2700,7 +2700,7 @@ NS_IMETHODIMP
PresShell::BeginObservingDocument() PresShell::BeginObservingDocument()
{ {
if (mDocument) { if (mDocument) {
mDocument->AddObserver(this); mDocument->BindingManager()->AddObserver(this);
if (mIsDocumentGone) { if (mIsDocumentGone) {
NS_WARNING("Adding a presshell that was disconnected from the document " NS_WARNING("Adding a presshell that was disconnected from the document "
"as a document observer? Sounds wrong..."); "as a document observer? Sounds wrong...");
@@ -2718,7 +2718,7 @@ PresShell::EndObservingDocument()
// is gone, perhaps? Except for printing it's NOT gone, sometimes. // is gone, perhaps? Except for printing it's NOT gone, sometimes.
mIsDocumentGone = PR_TRUE; mIsDocumentGone = PR_TRUE;
if (mDocument) { if (mDocument) {
mDocument->RemoveObserver(this); mDocument->BindingManager()->RemoveObserver(this);
} }
return NS_OK; return NS_OK;
} }