Update the binding manager before doing any other notifications, and don't notify the document for nodes not in the document. Bug 398492, r+sr=sicking.

This commit is contained in:
bzbarsky@mit.edu 2007-12-04 10:37:54 -08:00
parent 52b94fea43
commit e055db8c48
7 changed files with 116 additions and 130 deletions

View File

@ -882,7 +882,10 @@ nsDocument::~nsDocument()
mStyleAttrStyleSheet->SetOwningDocument(nsnull);
}
NS_IF_RELEASE(mBindingManager);
if (mBindingManager) {
mBindingManager->DropDocumentReference();
NS_RELEASE(mBindingManager);
}
delete mHeaderData;
delete mBoxObjectTable;
@ -1103,12 +1106,8 @@ nsDocument::Init()
NS_ENSURE_TRUE(bindingManager, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(mBindingManager = bindingManager);
// The binding manager needs to come before everything but us in our
// mutation observer list.
nsINode::nsSlots* slots = GetSlots();
NS_ENSURE_TRUE(slots &&
slots->mMutationObservers.PrependObserver(bindingManager),
NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(slots,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
@ -2678,18 +2677,6 @@ nsDocument::ScriptLoader()
return mScriptLoader;
}
void
nsDocument::AddMutationObserver(nsIMutationObserver* aMutationObserver)
{
mBindingManager->AddObserver(aMutationObserver);
}
void
nsDocument::RemoveMutationObserver(nsIMutationObserver* aMutationObserver)
{
mBindingManager->RemoveObserver(aMutationObserver);
}
// Note: We don't hold a reference to the document observer; we assume
// that it has a live reference to the document.
void

View File

@ -456,9 +456,6 @@ public:
*/
virtual nsScriptLoader* ScriptLoader();
virtual void AddMutationObserver(nsIMutationObserver* aObserver);
virtual void RemoveMutationObserver(nsIMutationObserver* aMutationObserver);
/**
* Add a new observer of document change notifications. Whenever
* content is changed, appended, inserted or removed the observers are

View File

@ -54,10 +54,16 @@
#include "nsXULElement.h"
#endif
// This macro expects the ownerDocument of content_ to be in scope as
// |nsIDocument* doc|
#define IMPL_MUTATION_NOTIFICATION(func_, content_, params_) \
PR_BEGIN_MACRO \
nsINode* node = content_; \
nsINode* prev; \
NS_ASSERTION(node->GetOwnerDoc() == doc, "Bogus document"); \
if (doc) { \
static_cast<nsIMutationObserver*>(doc->BindingManager())-> \
func_ params_; \
} \
do { \
nsINode::nsSlots* slots = node->GetExistingSlots(); \
if (slots && !slots->mMutationObservers.IsEmpty()) { \
@ -67,14 +73,7 @@
slots->mMutationObservers, nsIMutationObserver, \
func_, params_); \
} \
prev = node; \
node = node->GetNodeParent(); \
\
if (!node && prev->HasFlag(NODE_FORCE_XBL_BINDINGS)) { \
/* For elements that have the NODE_FORCE_XBL_BINDINGS flag \
set we need to notify the document */ \
node = prev->GetOwnerDoc(); \
} \
} while (node); \
PR_END_MACRO
@ -114,10 +113,10 @@ void
nsNodeUtils::ContentAppended(nsIContent* aContainer,
PRInt32 aNewIndexInContainer)
{
nsIDocument* document = aContainer->GetOwnerDoc();
nsIDocument* doc = aContainer->GetOwnerDoc();
IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer,
(document, aContainer, aNewIndexInContainer));
(doc, aContainer, aNewIndexInContainer));
}
void
@ -129,10 +128,11 @@ nsNodeUtils::ContentInserted(nsINode* aContainer,
aContainer->IsNodeOfType(nsINode::eDOCUMENT),
"container must be an nsIContent or an nsIDocument");
nsIContent* container;
nsIDocument* doc = aContainer->GetOwnerDoc();
nsIDocument* document;
if (aContainer->IsNodeOfType(nsINode::eCONTENT)) {
container = static_cast<nsIContent*>(aContainer);
document = aContainer->GetOwnerDoc();
document = doc;
}
else {
container = nsnull;
@ -152,10 +152,11 @@ nsNodeUtils::ContentRemoved(nsINode* aContainer,
aContainer->IsNodeOfType(nsINode::eDOCUMENT),
"container must be an nsIContent or an nsIDocument");
nsIContent* container;
nsIDocument* doc = aContainer->GetOwnerDoc();
nsIDocument* document;
if (aContainer->IsNodeOfType(nsINode::eCONTENT)) {
container = static_cast<nsIContent*>(aContainer);
document = aContainer->GetOwnerDoc();
document = doc;
}
else {
container = nsnull;

View File

@ -1334,52 +1334,6 @@ nsBindingManager::GetNestedSingleInsertionPoint(nsIContent* aParent,
return insertionElement;
}
// 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(nsIMutationObserver* aObserver)
{
// The array makes sure the observer isn't already in the list
mObservers.AppendObserver(aObserver);
}
PRBool
nsBindingManager::RemoveObserver(nsIMutationObserver* aObserver)
{
return mObservers.RemoveObserver(aObserver);
}
void
nsBindingManager::CharacterDataWillChange(nsIDocument* aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(CharacterDataWillChange,
(aDocument, aContent, aInfo));
}
void
nsBindingManager::CharacterDataChanged(nsIDocument* aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(CharacterDataChanged,
(aDocument, aContent, aInfo));
}
void
nsBindingManager::AttributeChanged(nsIDocument* aDocument,
nsIContent* aContent,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType,
PRUint32 aStateMask)
{
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(AttributeChanged,
(aDocument, aContent, aNameSpaceID,
aAttribute, aModType, aStateMask));
}
void
nsBindingManager::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer,
@ -1431,10 +1385,6 @@ nsBindingManager::ContentAppended(nsIDocument* aDocument,
}
}
}
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentAppended,
(aDocument, aContainer,
aNewIndexInContainer));
}
void
@ -1450,10 +1400,6 @@ nsBindingManager::ContentInserted(nsIDocument* aDocument,
NS_ASSERTION(aIndexInContainer >= 0, "Bogus index");
HandleChildInsertion(aContainer, aChild, aIndexInContainer, PR_FALSE);
}
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentInserted,
(aDocument, aContainer, aChild,
aIndexInContainer));
}
void
@ -1483,31 +1429,18 @@ nsBindingManager::ContentRemoved(nsIDocument* aDocument,
point->RemoveChild(aChild);
}
}
SetInsertionParent(aChild, nsnull);
}
}
}
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ContentRemoved,
(aDocument, aContainer, aChild,
aIndexInContainer));
}
void
nsBindingManager::NodeWillBeDestroyed(const nsINode *aNode)
nsBindingManager::DropDocumentReference()
{
// Make sure to not run any more XBL constructors
mProcessingAttachedStack = PR_TRUE;
mDocument = nsnull;
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(NodeWillBeDestroyed, (aNode));
}
void
nsBindingManager::ParentChainChanged(nsIContent *aContent)
{
NS_BINDINGMANAGER_NOTIFY_OBSERVERS(ParentChainChanged, (aContent));
}
void

View File

@ -40,7 +40,7 @@
#ifndef nsBindingManager_h_
#define nsBindingManager_h_
#include "nsIMutationObserver.h"
#include "nsStubMutationObserver.h"
#include "pldhash.h"
#include "nsInterfaceHashtable.h"
#include "nsRefPtrHashtable.h"
@ -64,11 +64,14 @@ typedef nsTArray<nsRefPtr<nsXBLBinding> > nsBindingList;
template<class T> class nsRunnableMethod;
class nsIPrincipal;
class nsBindingManager : public nsIMutationObserver
class nsBindingManager : public nsStubMutationObserver
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIMUTATIONOBSERVER
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
nsBindingManager(nsIDocument* aDocument);
~nsBindingManager();
@ -180,22 +183,6 @@ public:
PRBool ShouldBuildChildFrames(nsIContent* aContent);
/**
* 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.
*/
void AddObserver(nsIMutationObserver* aObserver);
/**
* Remove an observer of document change notifications. This will
* return false if the observer cannot be found.
*/
PRBool RemoveObserver(nsIMutationObserver* aObserver);
// Style rule methods
nsresult WalkRules(nsStyleSet* aStyleSet,
nsIStyleRuleProcessor::EnumFunc aFunc,
@ -212,6 +199,9 @@ public:
void BeginOutermostUpdate();
void EndOutermostUpdate();
// Called when the document is going away
void DropDocumentReference();
protected:
nsIXPConnectWrappedJS* GetWrappedJS(nsIContent* aContent);
nsresult SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aResult);
@ -234,10 +224,6 @@ protected:
void HandleChildInsertion(nsIContent* aContainer, nsIContent* aChild,
PRUint32 aIndexInContainer, PRBool aAppend);
#define NS_BINDINGMANAGER_NOTIFY_OBSERVERS(func_, params_) \
NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(mObservers, nsIMutationObserver, \
func_, params_);
// Same as ProcessAttachedQueue, but also nulls out
// mProcessAttachedQueueEvent
void DoProcessAttachedQueue();
@ -291,11 +277,6 @@ protected:
// table, they have not yet finished loading.
nsInterfaceHashtable<nsURIHashKey,nsIStreamListener> mLoadingDocTable;
// Array of mutation 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<nsIMutationObserver> mObservers;
// A queue of binding attached event handlers that are awaiting execution.
nsBindingList mAttachedStack;
PRPackedBool mProcessingAttachedStack;

View File

@ -55,6 +55,7 @@ _TEST_FILES = \
test_bug378866.xhtml \
test_bug397934.xhtml \
test_bug398135.xhtml \
test_bug398492.xul \
test_bug400705.xhtml \
test_bug401907.xhtml \
test_bug403162.xhtml \

View File

@ -0,0 +1,86 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=398492
-->
<window title="Mozilla Bug 398492"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="/MochiKit/packed.js" />
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
<bindings xmlns="http://www.mozilla.org/xbl">
<binding id="test">
<content>
<xul:hbox id="xxx"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<children/>
</xul:hbox>
</content>
</binding>
</bindings>
<!-- test resuls are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=398492"
target="_blank">Mozilla Bug 398492</a>
</body>
<hbox id="testbox" style="-moz-binding: url(#test)">Text</hbox>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
/** Test for Bug 398492 **/
SimpleTest.waitForExplicitFinish();
function getXBLParent(node) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var utils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
.getService(Components.interfaces.inIDOMUtils);
return utils.getParentForNode(node, true);
}
addLoadEvent(function() {
var n = $("testbox");
var kid = n.firstChild;
var anonKid = document.getAnonymousNodes(n)[0];
is(anonKid instanceof XULElement, true, "Must be a XUL element");
is(anonKid, getXBLParent(kid), "Unexpected anonymous nodes");
var n2 = n.cloneNode(true);
var kid2 = n2.firstChild;
var anonKid2 = document.getAnonymousNodes(n2)[0];
is(anonKid2 instanceof XULElement, true,
"Must be a XUL element after clone");
is(anonKid2, getXBLParent(kid2),
"Unexpected anonymous nodes after clone");
var n3 = document.createElement("hbox");
var kid3 = document.createTextNode("Text");
n3.appendChild(kid3);
document.addBinding(n3, document.location.href + "#test");
var anonKid3 = document.getAnonymousNodes(n3)[0];
is(anonKid3 instanceof XULElement, true,
"Must be a XUL element after addBinding");
is(anonKid3, getXBLParent(kid3),
"Unexpected anonymous nodes after addBinding");
n.removeChild(kid);
isnot(anonKid, getXBLParent(kid),
"Should have removed kid from insertion point");
n2.removeChild(kid2);
isnot(anonKid2, getXBLParent(kid2),
"Should have removed kid2 from insertion point");
n3.removeChild(kid3);
isnot(anonKid3, getXBLParent(kid3),
"Should have removed kid3 from insertion point");
SimpleTest.finish();
});
]]></script>
</window>