Bug 566416. Make attribute nodes set their mFirstChild correctly. r=sicking

This commit is contained in:
Boris Zbarsky 2010-05-27 15:10:33 -04:00
parent aadbb25e72
commit 07c9425ef6
2 changed files with 73 additions and 58 deletions

View File

@ -73,6 +73,13 @@ nsDOMAttribute::nsDOMAttribute(nsDOMAttributeMap *aAttrMap,
// We don't add a reference to our content. It will tell us
// to drop our reference when it goes away.
EnsureChildState();
nsIContent* content = GetContentInternal();
if (content) {
content->AddMutationObserver(this);
}
}
nsDOMAttribute::~nsDOMAttribute()
@ -80,6 +87,12 @@ nsDOMAttribute::~nsDOMAttribute()
if (mChild) {
static_cast<nsTextNode*>(mChild)->UnbindFromAttribute();
NS_RELEASE(mChild);
mFirstChild = nsnull;
}
nsIContent* content = GetContentInternal();
if (content) {
content->RemoveMutationObserver(this);
}
}
@ -105,6 +118,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttribute)
if (tmp->mChild) {
static_cast<nsTextNode*>(tmp->mChild)->UnbindFromAttribute();
NS_RELEASE(tmp->mChild);
tmp->mFirstChild = nsnull;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_LISTENERMANAGER
NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
@ -115,8 +129,8 @@ DOMCI_DATA(Attr, nsDOMAttribute)
// QueryInterface implementation for nsDOMAttribute
NS_INTERFACE_TABLE_HEAD(nsDOMAttribute)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_NODE_INTERFACE_TABLE5(nsDOMAttribute, nsIDOMAttr, nsIAttribute, nsIDOMNode,
nsIDOM3Attr, nsPIDOMEventTarget)
NS_NODE_INTERFACE_TABLE6(nsDOMAttribute, nsIDOMAttr, nsIAttribute, nsIDOMNode,
nsIDOM3Attr, nsPIDOMEventTarget, nsIMutationObserver)
NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMAttribute)
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
new nsNodeSupportsWeakRefTearoff(this))
@ -144,6 +158,11 @@ nsDOMAttribute::SetMap(nsDOMAttributeMap *aMap)
// need to locally cache value. GetValue() does that.
GetValue(mValue);
}
nsIContent* content = GetContentInternal();
if (content) {
content->RemoveMutationObserver(this);
}
mAttrMap = aMap;
}
@ -306,11 +325,7 @@ nsDOMAttribute::GetChildNodes(nsIDOMNodeList** aChildNodes)
NS_IMETHODIMP
nsDOMAttribute::HasChildNodes(PRBool* aHasChildNodes)
{
PRBool hasChild;
nsresult rv = EnsureChildState(PR_FALSE, hasChild);
NS_ENSURE_SUCCESS(rv, rv);
*aHasChildNodes = hasChild;
*aHasChildNodes = mFirstChild != nsnull;
return NS_OK;
}
@ -330,12 +345,8 @@ nsDOMAttribute::GetFirstChild(nsIDOMNode** aFirstChild)
{
*aFirstChild = nsnull;
PRBool hasChild;
nsresult rv = EnsureChildState(PR_TRUE, hasChild);
NS_ENSURE_SUCCESS(rv, rv);
if (mChild) {
CallQueryInterface(mChild, aFirstChild);
if (mFirstChild) {
CallQueryInterface(mFirstChild, aFirstChild);
}
return NS_OK;
@ -584,38 +595,30 @@ nsDOMAttribute::IsNodeOfType(PRUint32 aFlags) const
PRUint32
nsDOMAttribute::GetChildCount() const
{
return GetChildCount(PR_FALSE);
return mFirstChild ? 1 : 0;
}
nsIContent *
nsDOMAttribute::GetChildAt(PRUint32 aIndex) const
{
// Don't need to check result of EnsureChildState since mChild will be null.
PRBool hasChild;
EnsureChildState(PR_TRUE, hasChild);
return aIndex == 0 && hasChild ? mChild : nsnull;
return aIndex == 0 ? mFirstChild : nsnull;
}
nsIContent * const *
nsDOMAttribute::GetChildArray(PRUint32* aChildCount) const
{
*aChildCount = GetChildCount(PR_TRUE);
return &mChild;
*aChildCount = GetChildCount();
return &mFirstChild;
}
PRInt32
nsDOMAttribute::IndexOf(nsINode* aPossibleChild) const
{
// No need to call EnsureChildState here. If we don't already have a child
// then aPossibleChild can't possibly be our child.
if (!aPossibleChild || aPossibleChild != mChild) {
if (!aPossibleChild || aPossibleChild != mFirstChild) {
return -1;
}
PRBool hasChild;
EnsureChildState(PR_FALSE, hasChild);
return hasChild ? 0 : -1;
return 0;
}
nsresult
@ -659,8 +662,6 @@ nsDOMAttribute::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationE
if (guard.Mutated(0) && mChild != child) {
return NS_OK;
}
NS_RELEASE(mChild);
static_cast<nsTextNode*>(child.get())->UnbindFromAttribute();
nsString nullString;
SetDOMStringToNull(nullString);
@ -724,34 +725,52 @@ nsDOMAttribute::GetSystemEventGroup(nsIDOMEventGroup** aGroup)
return elm->GetSystemEventGroupLM(aGroup);
}
nsresult
nsDOMAttribute::EnsureChildState(PRBool aSetText, PRBool &aHasChild) const
void
nsDOMAttribute::EnsureChildState()
{
aHasChild = PR_FALSE;
nsDOMAttribute* mutableThis = const_cast<nsDOMAttribute*>(this);
NS_PRECONDITION(!mChild, "Someone screwed up");
nsAutoString value;
mutableThis->GetValue(value);
GetValue(value);
if (!mChild && !value.IsEmpty()) {
nsresult rv = NS_NewTextNode(&mutableThis->mChild,
mNodeInfo->NodeInfoManager());
NS_ENSURE_SUCCESS(rv, rv);
if (!value.IsEmpty()) {
NS_NewTextNode(&mChild, mNodeInfo->NodeInfoManager());
static_cast<nsTextNode*>(mChild)->BindToAttribute(mutableThis);
}
static_cast<nsTextNode*>(mChild)->BindToAttribute(this);
mFirstChild = mChild;
aHasChild = !value.IsEmpty();
if (aSetText && aHasChild) {
// aNotify should probably be PR_TRUE sometimes, but it's unlikely that
// anyone cares. And we aren't updating the node when the attribute changes
// anyway so any notifications are way late.
mChild->SetText(value, PR_FALSE);
}
}
return NS_OK;
void
nsDOMAttribute::AttributeChanged(nsIDocument* aDocument,
nsIContent* aContent,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType)
{
nsIContent* content = GetContentInternal();
if (aContent != content) {
return;
}
if (aNameSpaceID != mNodeInfo->NamespaceID()) {
return;
}
nsCOMPtr<nsIAtom> nameAtom = GetNameAtom(content);
if (nameAtom != aAttribute) {
return;
}
// Just blow away our mChild and recreate it if needed
if (mChild) {
static_cast<nsTextNode*>(mChild)->UnbindFromAttribute();
NS_RELEASE(mChild);
mFirstChild = nsnull;
}
EnsureChildState();
}
void

View File

@ -53,12 +53,14 @@
#include "nsDOMAttributeMap.h"
#include "nsCycleCollectionParticipant.h"
#include "nsContentUtils.h"
#include "nsStubMutationObserver.h"
// Attribute helper class used to wrap up an attribute with a dom
// object that implements nsIDOMAttr, nsIDOM3Attr, nsIDOMNode, nsIDOM3Node
class nsDOMAttribute : public nsIAttribute,
public nsIDOMAttr,
public nsIDOM3Attr
public nsIDOM3Attr,
public nsStubMutationObserver
{
public:
nsDOMAttribute(nsDOMAttributeMap* aAttrMap, nsINodeInfo *aNodeInfo,
@ -118,6 +120,8 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDOMAttribute,
nsIAttribute)
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
protected:
virtual mozilla::dom::Element* GetNameSpaceElement()
{
@ -129,15 +133,7 @@ protected:
private:
already_AddRefed<nsIAtom> GetNameAtom(nsIContent* aContent);
nsresult EnsureChildState(PRBool aSetText, PRBool &aHasChild) const;
PRUint32 GetChildCount(PRBool aSetText) const
{
PRBool hasChild;
EnsureChildState(aSetText, hasChild);
return hasChild ? 1 : 0;
}
void EnsureChildState();
nsString mValue;
// XXX For now, there's only a single child - a text element