/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (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/NPL/ * * 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 Communicator client code. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are Copyright (C) 1998 * Netscape Communications Corporation. All Rights Reserved. */ #include "nsDOMAttributeMap.h" #include "nsDOMAttribute.h" #include "nsGenericElement.h" #include "nsIContent.h" #include "nsIDOMScriptObjectFactory.h" #include "nsINameSpaceManager.h" static NS_DEFINE_IID(kIDOMNamedNodeMapIID, NS_IDOMNAMEDNODEMAP_IID); static NS_DEFINE_IID(kIDOMAttrIID, NS_IDOMATTR_IID); static NS_DEFINE_IID(kIDOMAttributePrivateIID, NS_IDOMATTRIBUTEPRIVATE_IID); //---------------------------------------------------------------------- nsDOMAttributeMap::nsDOMAttributeMap(nsIContent* aContent) : mContent(aContent) { NS_INIT_REFCNT(); mScriptObject = nsnull; mAttributes = nsnull; // We don't add a reference to our content. If it goes away, // we'll be told to drop our reference } PR_CALLBACK PRIntn RemoveAttributes(PLHashEntry* he, PRIntn i, void* arg) { nsIDOMAttr* attr = (nsIDOMAttr*)he->value; char* str = (char*)he->key; if (nsnull != attr) { nsIDOMAttributePrivate* attrPrivate; attr->QueryInterface(kIDOMAttributePrivateIID, (void**)&attrPrivate); attrPrivate->DropReference(); NS_RELEASE(attrPrivate); NS_RELEASE(attr); } delete [] str; return HT_ENUMERATE_REMOVE; } PR_CALLBACK PRIntn DropReferencesInAttributes(PLHashEntry* he, PRIntn i, void* arg) { nsDOMAttribute* attr = (nsDOMAttribute*)he->value; if (nsnull != attr) { nsIDOMAttributePrivate* attrPrivate; attr->QueryInterface(kIDOMAttributePrivateIID, (void**)&attrPrivate); attrPrivate->DropReference(); NS_RELEASE(attrPrivate); } return HT_ENUMERATE_NEXT; } nsDOMAttributeMap::~nsDOMAttributeMap() { if (nsnull != mAttributes) { PL_HashTableEnumerateEntries(mAttributes, RemoveAttributes, nsnull); } } void nsDOMAttributeMap::DropReference() { mContent = nsnull; if (nsnull != mAttributes) { PL_HashTableEnumerateEntries(mAttributes, DropReferencesInAttributes, nsnull); } } nsresult nsDOMAttributeMap::QueryInterface(REFNSIID aIID, void** aInstancePtr) { if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } if (aIID.Equals(kIDOMNamedNodeMapIID)) { nsIDOMNamedNodeMap* tmp = this; *aInstancePtr = (void*)tmp; NS_ADDREF_THIS(); return NS_OK; } if (aIID.Equals(kIScriptObjectOwnerIID)) { nsIScriptObjectOwner* tmp = this; *aInstancePtr = (void*)tmp; NS_ADDREF_THIS(); return NS_OK; } if (aIID.Equals(kISupportsIID)) { nsIDOMNamedNodeMap* tmp1 = this; nsISupports* tmp2 = tmp1; *aInstancePtr = (void*)tmp2; NS_ADDREF_THIS(); return NS_OK; } return NS_NOINTERFACE; } NS_IMPL_ADDREF(nsDOMAttributeMap) NS_IMPL_RELEASE(nsDOMAttributeMap) nsresult nsDOMAttributeMap::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject) { nsresult res = NS_OK; if (nsnull == mScriptObject) { nsIDOMScriptObjectFactory *factory; res = nsGenericElement::GetScriptObjectFactory(&factory); if (NS_OK != res) { return res; } res = factory->NewScriptNamedNodeMap(aContext, (nsISupports *)(nsIDOMNamedNodeMap *)this, (nsISupports *)mContent, (void**)&mScriptObject); NS_RELEASE(factory); } *aScriptObject = mScriptObject; return res; } nsresult nsDOMAttributeMap::SetScriptObject(void *aScriptObject) { mScriptObject = aScriptObject; return NS_OK; } PLHashTable* nsDOMAttributeMap::GetAttributeTable() { if ((nsnull == mAttributes) && (nsnull != mContent)) { PRInt32 count; mContent->GetAttributeCount(count); mAttributes = PL_NewHashTable(count, PL_HashString, PL_CompareStrings, PL_CompareValues, nsnull, nsnull); } return mAttributes; } nsresult nsDOMAttributeMap::GetNamedItemCommon(const nsString& aAttrName, PRInt32 aNameSpaceID, nsIAtom* aNameAtom, nsIDOMNode** aAttribute) { nsIDOMAttr* attribute; char buf[128]; nsresult result = NS_OK; PLHashTable* attrHash = GetAttributeTable(); aAttrName.ToCString(buf, sizeof(buf)); if (nsnull != attrHash) { attribute = (nsIDOMAttr*)PL_HashTableLookup(attrHash, buf); if (nsnull == attribute) { nsresult attrResult; nsAutoString value; attrResult = mContent->GetAttribute(aNameSpaceID, aNameAtom, value); if (NS_CONTENT_ATTR_NOT_THERE != attrResult) { nsDOMAttribute* domAttribute; domAttribute = new nsDOMAttribute(mContent, aAttrName, value); if (nsnull == domAttribute) { result = NS_ERROR_OUT_OF_MEMORY; } else { result = domAttribute->QueryInterface(kIDOMAttrIID, (void **)&attribute); char* hashKey = aAttrName.ToNewCString(); PL_HashTableAdd(attrHash, hashKey, attribute); } } } } if (nsnull != attribute) { result = attribute->QueryInterface(kIDOMNodeIID, (void**)aAttribute); } else { *aAttribute = nsnull; } return result; } void nsDOMAttributeMap::GetNormalizedName(PRInt32 aNameSpaceID, nsIAtom* aNameAtom, nsString& aAttrName) { nsIAtom* prefix; aAttrName.Truncate(); mContent->GetNameSpacePrefix(aNameSpaceID, prefix); if (nsnull != prefix) { prefix->ToString(aAttrName); aAttrName.Append(":"); NS_RELEASE(prefix); } if (nsnull != aNameAtom) { nsAutoString tmp; aNameAtom->ToString(tmp); aAttrName.Append(tmp); } } nsresult nsDOMAttributeMap::GetNamedItem(const nsString &aAttrName, nsIDOMNode** aAttribute) { nsresult result = NS_OK; if (nsnull != mContent) { nsIAtom* nameAtom; PRInt32 nameSpaceID; nsAutoString normalizedName; mContent->ParseAttributeString(aAttrName, nameAtom, nameSpaceID); GetNormalizedName(nameSpaceID, nameAtom, normalizedName); result = GetNamedItemCommon(normalizedName, nameSpaceID, nameAtom, aAttribute); NS_IF_RELEASE(nameAtom); } else { *aAttribute = nsnull; } return result; } nsresult nsDOMAttributeMap::SetNamedItem(nsIDOMNode *aNode, nsIDOMNode **aReturn) { nsresult result = NS_OK; nsIDOMAttr* attribute; if ((nsnull != mContent) && (nsnull != aNode)) { result = aNode->QueryInterface(kIDOMAttrIID, (void**)&attribute); if (NS_OK == result) { PLHashTable* attrHash; attrHash = GetAttributeTable(); if (nsnull != attrHash) { nsIDOMNode* oldAttribute; nsIDOMAttributePrivate* attrPrivate; nsAutoString name, value; char buf[128]; char* key; nsIAtom* nameAtom; PRInt32 nameSpaceID; // Get normalized attribute name attribute->GetName(name); mContent->ParseAttributeString(name, nameAtom, nameSpaceID); GetNormalizedName(nameSpaceID, nameAtom, name); name.ToCString(buf, sizeof(buf)); result = GetNamedItemCommon(name, nameSpaceID, nameAtom, &oldAttribute); if (nsnull != oldAttribute) { nsIDOMAttributePrivate* oldAttributePrivate; PLHashEntry** he; // Remove the attribute from the hash table, cleaning // the hash table entry as we go about it. he = PL_HashTableRawLookup(attrHash, PL_HashString(buf), buf); key = (char*)(*he)->key; PL_HashTableRemove(attrHash, buf); if (nsnull != key) { delete [] key; } result = oldAttribute->QueryInterface(kIDOMAttributePrivateIID, (void **)&oldAttributePrivate); if (NS_OK == result) { oldAttributePrivate->DropReference(); NS_RELEASE(oldAttributePrivate); } *aReturn = oldAttribute; // Drop the reference held in the hash table NS_RELEASE(oldAttribute); } else { *aReturn = nsnull; } attribute->GetValue(value); // Associate the new attribute with the content // XXX Need to fail if it's already associated with other // content key = name.ToNewCString(); result = attribute->QueryInterface(kIDOMAttributePrivateIID, (void **)&attrPrivate); if (NS_OK == result) { attrPrivate->SetContent(mContent); attrPrivate->SetName(name); NS_RELEASE(attrPrivate); } // Add the new attribute node to the hash table (maintaining // a reference to it) PL_HashTableAdd(attrHash, key, attribute); // Set the attribute on the content result = mContent->SetAttribute(nameSpaceID, nameAtom, value, PR_TRUE); NS_IF_RELEASE(nameAtom); } } } else { *aReturn = nsnull; } return result; } NS_IMETHODIMP nsDOMAttributeMap::RemoveNamedItem(const nsString& aName, nsIDOMNode** aReturn) { nsresult result = NS_OK; if (nsnull != mContent) { PLHashTable* attrHash; attrHash = GetAttributeTable(); if (nsnull != attrHash) { nsIDOMNode* attribute; nsIDOMAttributePrivate* attrPrivate; char buf[128]; char* key; nsIAtom* nameAtom; PRInt32 nameSpaceID; nsAutoString name; mContent->ParseAttributeString(aName, nameAtom, nameSpaceID); GetNormalizedName(nameSpaceID, nameAtom, name); name.ToCString(buf, sizeof(buf)); result = GetNamedItemCommon(name, nameSpaceID, nameAtom, &attribute); if (nsnull != attribute) { PLHashEntry** he; // Remove the attribute from the hash table, cleaning // the hash table entry as we go about it. he = PL_HashTableRawLookup(attrHash, PL_HashString(buf), buf); key = (char*)(*he)->key; PL_HashTableRemove(attrHash, buf); if (nsnull != key) { delete [] key; } result = attribute->QueryInterface(kIDOMAttributePrivateIID, (void **)&attrPrivate); if (NS_OK == result) { attrPrivate->DropReference(); NS_RELEASE(attrPrivate); } *aReturn = attribute; // Drop the reference held in the hash table NS_RELEASE(attribute); } else { *aReturn = nsnull; } // Unset the attribute in the content result = mContent->UnsetAttribute(nameSpaceID, nameAtom, PR_TRUE); NS_IF_RELEASE(nameAtom); } } return result; } nsresult nsDOMAttributeMap::Item(PRUint32 aIndex, nsIDOMNode** aReturn) { PRInt32 nameSpaceID; nsIAtom* nameAtom = nsnull; nsresult result = NS_OK; if ((nsnull != mContent) && NS_SUCCEEDED(mContent->GetAttributeNameAt(aIndex, nameSpaceID, nameAtom))) { nsAutoString attrName; GetNormalizedName(nameSpaceID, nameAtom, attrName); result = GetNamedItemCommon(attrName, nameSpaceID, nameAtom, aReturn); NS_IF_RELEASE(nameAtom); } else { *aReturn = nsnull; } return result; } nsresult nsDOMAttributeMap::GetLength(PRUint32 *aLength) { PRInt32 n; nsresult rv = NS_OK; if (nsnull != mContent) { rv = mContent->GetAttributeCount(n); *aLength = PRUint32(n); } else { *aLength = 0; } return rv; }