/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape 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/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.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf * * * 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 NPL, 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 NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nscore.h" #include "nsIHTMLAttributes.h" #include "nsIHTMLStyleSheet.h" #include "nsIPresContext.h" #include "nsIDocument.h" #include "nsINodeInfo.h" #include "nsIStyleRule.h" #include "nsString.h" #include "nsISupportsArray.h" #include "nsCRT.h" #include "nsIArena.h" #include "nsIStyleContext.h" #include "nsHTMLAtoms.h" #include "nsIHTMLContent.h" #include "nsVoidArray.h" #include "nsISizeOfHandler.h" #include "nsCOMPtr.h" #include "nsUnicharUtils.h" #include "nsIStyleSet.h" #include "nsRuleWalker.h" MOZ_DECL_CTOR_COUNTER(HTMLAttribute) struct HTMLAttribute { HTMLAttribute(void) : mAttribute(nsnull), mValue(), mNext(nsnull) { MOZ_COUNT_CTOR(HTMLAttribute); } HTMLAttribute(nsIAtom* aAttribute, const nsAReadableString& aValue) : mAttribute(aAttribute), mValue(aValue), mNext(nsnull) { MOZ_COUNT_CTOR(HTMLAttribute); NS_IF_ADDREF(mAttribute); } HTMLAttribute(nsIAtom* aAttribute, const nsHTMLValue& aValue) : mAttribute(aAttribute), mValue(aValue), mNext(nsnull) { MOZ_COUNT_CTOR(HTMLAttribute); NS_IF_ADDREF(mAttribute); } HTMLAttribute(const HTMLAttribute& aCopy) : mAttribute(aCopy.mAttribute), mValue(aCopy.mValue), mNext(nsnull) { MOZ_COUNT_CTOR(HTMLAttribute); NS_IF_ADDREF(mAttribute); } ~HTMLAttribute(void) { MOZ_COUNT_DTOR(HTMLAttribute); NS_IF_RELEASE(mAttribute); } HTMLAttribute& operator=(const HTMLAttribute& aCopy) { NS_IF_RELEASE(mAttribute); mAttribute = aCopy.mAttribute; NS_IF_ADDREF(mAttribute); mValue = aCopy.mValue; return *this; } PRBool operator==(const HTMLAttribute& aOther) const { return PRBool((mAttribute == aOther.mAttribute) && (mValue == aOther.mValue)); } PRUint32 HashValue(void) const { return NS_PTR_TO_INT32(mAttribute) ^ mValue.HashValue(); } void Reset(void) { NS_IF_RELEASE(mAttribute); mValue.Reset(); } void Set(nsIAtom* aAttribute, const nsHTMLValue& aValue) { NS_IF_RELEASE(mAttribute); mAttribute = aAttribute; NS_IF_ADDREF(mAttribute); mValue = aValue; } void Set(nsIAtom* aAttribute, const nsAReadableString& aValue) { NS_IF_RELEASE(mAttribute); mAttribute = aAttribute; NS_IF_ADDREF(mAttribute); mValue.SetStringValue(aValue); } #ifdef DEBUG void AppendToString(nsString& aBuffer) const { if (nsnull != mAttribute) { nsAutoString temp; mAttribute->ToString(temp); aBuffer.Append(temp); if (eHTMLUnit_Null != mValue.GetUnit()) { aBuffer.Append(NS_LITERAL_STRING(" = ")); mValue.AppendToString(aBuffer); } } else { aBuffer.Append(NS_LITERAL_STRING("null")); } } void ToString(nsString& aBuffer) const { if (nsnull != mAttribute) { mAttribute->ToString(aBuffer); if (eHTMLUnit_Null != mValue.GetUnit()) { aBuffer.Append(NS_LITERAL_STRING(" = ")); mValue.AppendToString(aBuffer); } } else { aBuffer.Assign(NS_LITERAL_STRING("null")); } } #endif // DEBUG static void CopyHTMLAttributes(HTMLAttribute* aSource, HTMLAttribute** aDest) { while (aSource && aDest) { (*aDest) = new HTMLAttribute(*aSource); if (! *aDest) { break; } aDest = &((*aDest)->mNext); aSource = aSource->mNext; } } static void DeleteHTMLAttributes(HTMLAttribute* aAttr) { while (aAttr) { HTMLAttribute* deadBeef = aAttr; aAttr = aAttr->mNext; delete deadBeef; } } static HTMLAttribute* FindHTMLAttribute(nsIAtom* aAttrName, HTMLAttribute* aAttr) { while (aAttr) { if (aAttrName == aAttr->mAttribute) { return aAttr; } aAttr = aAttr->mNext; } return nsnull; } static const HTMLAttribute* FindHTMLAttribute(nsIAtom* aAttrName, const HTMLAttribute* aAttr) { while (aAttr) { if (aAttrName == aAttr->mAttribute) { return aAttr; } aAttr = aAttr->mNext; } return nsnull; } static PRBool RemoveHTMLAttribute(nsIAtom* aAttrName, HTMLAttribute** aAttr) { while (*aAttr) { if ((*aAttr)->mAttribute == aAttrName) { HTMLAttribute* attr = *aAttr; *aAttr = (*aAttr)->mNext; delete attr; return PR_TRUE; } aAttr = &((*aAttr)->mNext); } return PR_FALSE; } #ifdef DEBUG nsresult SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const { if (!aResult) { return NS_ERROR_NULL_POINTER; } *aResult = sizeof(*this); return NS_OK; } #endif nsIAtom* mAttribute; nsHTMLValue mValue; HTMLAttribute* mNext; }; // ---------------- MOZ_DECL_CTOR_COUNTER(nsClassList) struct nsClassList { nsClassList(nsIAtom* aAtom) : mAtom(aAtom), // take ref mNext(nsnull) { MOZ_COUNT_CTOR(nsClassList); } nsClassList(const nsClassList& aCopy) : mAtom(aCopy.mAtom), mNext(nsnull) { MOZ_COUNT_CTOR(nsClassList); NS_IF_ADDREF(mAtom); if (aCopy.mNext) { mNext = new nsClassList(*(aCopy.mNext)); } } ~nsClassList(void) { MOZ_COUNT_DTOR(nsClassList); Reset(); } void Reset(void) { NS_IF_RELEASE(mAtom); if (mNext) { delete mNext; mNext = nsnull; } } nsIAtom* mAtom; nsClassList* mNext; }; // ---------------- class nsHTMLMappedAttributes : public nsIHTMLMappedAttributes, public nsIStyleRule { public: nsHTMLMappedAttributes(void); nsHTMLMappedAttributes(const nsHTMLMappedAttributes& aCopy); virtual ~nsHTMLMappedAttributes(void); NS_DECL_ISUPPORTS NS_IMETHOD Init(nsIHTMLStyleSheet* aSheet, nsMapRuleToAttributesFunc aMapRuleFunc); NS_IMETHOD Clone(nsHTMLMappedAttributes** aInstancePtrResult) const; NS_IMETHOD Reset(void); NS_IMETHOD SetMappingFunction(nsMapRuleToAttributesFunc aMapRuleFunc); NS_IMETHOD SetAttribute(nsIAtom* aAttrName, const nsAReadableString& aValue); NS_IMETHOD SetAttribute(nsIAtom* aAttrName, const nsHTMLValue& aValue); NS_IMETHOD UnsetAttribute(nsIAtom* aAttrName, PRInt32& aAttrCount); NS_IMETHOD GetAttribute(nsIAtom* aAttrName, nsHTMLValue& aValue) const; NS_IMETHOD GetAttribute(nsIAtom* aAttrName, const nsHTMLValue** aValue) const; NS_IMETHOD_(PRBool) HasAttribute(nsIAtom* aAttrName) const; NS_IMETHOD GetAttributeCount(PRInt32& aCount) const; NS_IMETHOD Equals(const nsIHTMLMappedAttributes* aAttributes, PRBool& aResult) const; NS_IMETHOD HashValue(PRUint32& aValue) const; NS_IMETHOD AddUse(void); NS_IMETHOD ReleaseUse(void); NS_IMETHOD GetUseCount(PRInt32& aUseCount) const; NS_IMETHOD SetUniqued(PRBool aUniqued); NS_IMETHOD GetUniqued(PRBool& aUniqued); NS_IMETHOD DropStyleSheetReference(void); // nsIStyleRule NS_IMETHOD GetStyleSheet(nsIStyleSheet*& aSheet) const; NS_IMETHOD SetStyleSheet(nsIHTMLStyleSheet* aSheet); // Strength is an out-of-band weighting, always 0 here NS_IMETHOD GetStrength(PRInt32& aStrength) const; // The new mapping functions. NS_IMETHOD MapRuleInfoInto(nsRuleData* aRuleData); #ifdef DEBUG NS_IMETHOD List(FILE* out, PRInt32 aIndent) const; void SizeOf(nsISizeOfHandler* aSizer, PRUint32 &aResult); #endif nsIHTMLStyleSheet* mSheet; PRInt32 mUseCount; PRInt32 mAttrCount; HTMLAttribute mFirst; nsMapRuleToAttributesFunc mRuleMapper; PRBool mUniqued; }; nsHTMLMappedAttributes::nsHTMLMappedAttributes(void) : mSheet(nsnull), mUseCount(0), mAttrCount(0), mFirst(), mRuleMapper(nsnull), mUniqued(PR_FALSE) { NS_INIT_ISUPPORTS(); } nsHTMLMappedAttributes::nsHTMLMappedAttributes(const nsHTMLMappedAttributes& aCopy) : mSheet(aCopy.mSheet), mUseCount(0), mAttrCount(aCopy.mAttrCount), mFirst(aCopy.mFirst), mRuleMapper(aCopy.mRuleMapper), mUniqued(PR_FALSE) { NS_INIT_ISUPPORTS(); HTMLAttribute::CopyHTMLAttributes(aCopy.mFirst.mNext, &(mFirst.mNext)); } nsHTMLMappedAttributes::~nsHTMLMappedAttributes(void) { Reset(); } NS_IMPL_ADDREF(nsHTMLMappedAttributes); NS_IMPL_RELEASE(nsHTMLMappedAttributes); nsresult nsHTMLMappedAttributes::QueryInterface(const nsIID& aIID, void** aInstancePtrResult) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer"); if (nsnull == aInstancePtrResult) { return NS_ERROR_NULL_POINTER; } static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); if (aIID.Equals(NS_GET_IID(nsIHTMLMappedAttributes))) { *aInstancePtrResult = (void*) ((nsIHTMLMappedAttributes*)this); NS_ADDREF_THIS(); return NS_OK; } if (aIID.Equals(NS_GET_IID(nsIStyleRule))) { *aInstancePtrResult = (void*) ((nsIStyleRule*)this); NS_ADDREF_THIS(); return NS_OK; } if (aIID.Equals(kISupportsIID)) { *aInstancePtrResult = (void*) ((nsIHTMLMappedAttributes*)this); NS_ADDREF_THIS(); return NS_OK; } return NS_NOINTERFACE; } NS_IMETHODIMP nsHTMLMappedAttributes::Init(nsIHTMLStyleSheet* aSheet, nsMapRuleToAttributesFunc aMapRuleFunc) { mSheet = aSheet; mRuleMapper = aMapRuleFunc; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::Clone(nsHTMLMappedAttributes** aInstancePtrResult) const { if (aInstancePtrResult == nsnull) { return NS_ERROR_NULL_POINTER; } nsHTMLMappedAttributes* clone = new nsHTMLMappedAttributes(*this); if (! clone) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(clone); *aInstancePtrResult = clone; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::Reset(void) { mAttrCount = 0; mFirst.Reset(); HTMLAttribute::DeleteHTMLAttributes(mFirst.mNext); mUniqued = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::SetMappingFunction(nsMapRuleToAttributesFunc aMapRuleFunc) { mRuleMapper = aMapRuleFunc; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::SetAttribute(nsIAtom* aAttrName, const nsAReadableString& aValue) { if (! aAttrName) { return NS_ERROR_NULL_POINTER; } if (mFirst.mAttribute) { // do we already have any? HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, &mFirst); if (attr) { attr->mValue.SetStringValue(aValue); } else { // add new attribute // keep these arbitrarily sorted so they'll equal regardless of set order if (aAttrName < mFirst.mAttribute) { // before first, move first down one attr = new HTMLAttribute(mFirst); if (attr) { attr->mNext = mFirst.mNext; mFirst.mNext = attr; mFirst.Set(aAttrName, aValue); } else { return NS_ERROR_OUT_OF_MEMORY; } } else { attr = new HTMLAttribute(aAttrName, aValue); if (attr) { HTMLAttribute* prev = &mFirst; while (prev->mNext && (prev->mNext->mAttribute < aAttrName)) { prev = prev->mNext; } attr->mNext = prev->mNext; prev->mNext = attr; } else { return NS_ERROR_OUT_OF_MEMORY; } } mAttrCount++; } } else { mFirst.Set(aAttrName, aValue); mAttrCount++; } return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::SetAttribute(nsIAtom* aAttrName, const nsHTMLValue& aValue) { if (! aAttrName) { return NS_ERROR_NULL_POINTER; } if (mFirst.mAttribute) { // do we already have any? HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, &mFirst); if (attr) { attr->mValue = aValue; } else { // add new attribute // keep these arbitrarily sorted so they'll hash regardless of set order if (aAttrName < mFirst.mAttribute) { // before first, move first down one attr = new HTMLAttribute(mFirst); if (attr) { attr->mNext = mFirst.mNext; mFirst.mNext = attr; mFirst.Set(aAttrName, aValue); } else { return NS_ERROR_OUT_OF_MEMORY; } } else { attr = new HTMLAttribute(aAttrName, aValue); if (attr) { HTMLAttribute* prev = &mFirst; while (prev->mNext && (prev->mNext->mAttribute < aAttrName)) { prev = prev->mNext; } attr->mNext = prev->mNext; prev->mNext = attr; } else { return NS_ERROR_OUT_OF_MEMORY; } } mAttrCount++; } } else { mFirst.Set(aAttrName, aValue); mAttrCount++; } return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::UnsetAttribute(nsIAtom* aAttrName, PRInt32& aAttrCount) { if (! aAttrName) { return NS_ERROR_NULL_POINTER; } if (aAttrName == mFirst.mAttribute) { if (mFirst.mNext) { HTMLAttribute* attr = mFirst.mNext; mFirst = *attr; mFirst.mNext = attr->mNext; delete attr; } else { mFirst.Reset(); } mAttrCount--; } else { if (HTMLAttribute::RemoveHTMLAttribute(aAttrName, &(mFirst.mNext))) { mAttrCount--; } } aAttrCount = mAttrCount; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::GetAttribute(nsIAtom* aAttrName, nsHTMLValue& aValue) const { if (! aAttrName) { return NS_ERROR_NULL_POINTER; } const HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, &mFirst); if (attr) { aValue = attr->mValue; return ((attr->mValue.GetUnit() == eHTMLUnit_Null) ? NS_CONTENT_ATTR_NO_VALUE : NS_CONTENT_ATTR_HAS_VALUE); } aValue.Reset(); return NS_CONTENT_ATTR_NOT_THERE; } NS_IMETHODIMP nsHTMLMappedAttributes::GetAttribute(nsIAtom* aAttrName, const nsHTMLValue** aValue) const { if (! aAttrName) { return NS_ERROR_NULL_POINTER; } const HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, &mFirst); if (attr) { *aValue = &attr->mValue; return ((attr->mValue.GetUnit() == eHTMLUnit_Null) ? NS_CONTENT_ATTR_NO_VALUE : NS_CONTENT_ATTR_HAS_VALUE); } *aValue = nsnull; return NS_CONTENT_ATTR_NOT_THERE; } NS_IMETHODIMP_(PRBool) nsHTMLMappedAttributes::HasAttribute(nsIAtom* aAttrName) const { if (!aAttrName) return PR_FALSE; const HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, &mFirst); return attr != nsnull; } NS_IMETHODIMP nsHTMLMappedAttributes::GetAttributeCount(PRInt32& aCount) const { aCount = mAttrCount; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::Equals(const nsIHTMLMappedAttributes* aOther, PRBool& aResult) const { const nsHTMLMappedAttributes* other = (const nsHTMLMappedAttributes*)aOther; if (this == other) { aResult = PR_TRUE; } else { aResult = PR_FALSE; if ((mRuleMapper == other->mRuleMapper) && (mAttrCount == other->mAttrCount)) { const HTMLAttribute* attr = &mFirst; const HTMLAttribute* otherAttr = &(other->mFirst); aResult = PR_TRUE; while (attr) { if (! ((*attr) == (*otherAttr))) { aResult = PR_FALSE; break; } attr = attr->mNext; otherAttr = otherAttr->mNext; } } } return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::HashValue(PRUint32& aValue) const { aValue = NS_PTR_TO_INT32(mRuleMapper); const HTMLAttribute* attr = &mFirst; while (nsnull != attr) { if (nsnull != attr->mAttribute) { aValue = aValue ^ attr->HashValue(); } attr = attr->mNext; } return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::AddUse(void) { mUseCount++; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::ReleaseUse(void) { mUseCount--; if (mSheet && (0 == mUseCount) && mUniqued) { mSheet->DropMappedAttributes(this); } return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::GetUseCount(PRInt32& aUseCount) const { aUseCount = mUseCount; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::SetUniqued(PRBool aUniqued) { mUniqued = aUniqued; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::GetUniqued(PRBool& aUniqued) { aUniqued = mUniqued; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::DropStyleSheetReference(void) { mSheet = nsnull; mUniqued = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::GetStyleSheet(nsIStyleSheet*& aSheet) const { aSheet = mSheet; NS_IF_ADDREF(aSheet); return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::SetStyleSheet(nsIHTMLStyleSheet* aSheet) { if (mSheet && mUniqued) { mSheet->DropMappedAttributes(this); } mSheet = aSheet; // not ref counted mUniqued = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::GetStrength(PRInt32& aStrength) const { aStrength = 0; return NS_OK; } NS_IMETHODIMP nsHTMLMappedAttributes::MapRuleInfoInto(nsRuleData* aRuleData) { if (0 < mAttrCount) { if (mRuleMapper) { (*mRuleMapper)(this, aRuleData); } } return NS_OK; } #ifdef DEBUG NS_IMETHODIMP nsHTMLMappedAttributes::List(FILE* out, PRInt32 aIndent) const { const HTMLAttribute* attr = &mFirst; nsAutoString buffer; while (nsnull != attr) { PRInt32 index; for (index = aIndent; --index >= 0; ) fputs(" ", out); attr->ToString(buffer); fputs(NS_LossyConvertUCS2toASCII(buffer).get(), out); fputs("\n", out); attr = attr->mNext; } return NS_OK; } /****************************************************************************** * SizeOf method: * * Self (reported as nsHTMLMappedAttributes's size): * 1) sizeof(*this) * * Contained / Aggregated data (not reported as nsHTMLMappedAttributes's size): * none * * Children / siblings / parents: * none * ******************************************************************************/ void nsHTMLMappedAttributes::SizeOf(nsISizeOfHandler* aSizer, PRUint32 &aResult) { // first get the unique items collection UNIQUE_STYLE_ITEMS(uniqueItems); if(! uniqueItems->AddItem((void*)this)){ // this is already accounted for return; } nsCOMPtr tag; tag = getter_AddRefs(NS_NewAtom("HTMLMappedAttributes")); aResult = sizeof(*this); aSizer->AddSize(tag, aResult); } #endif //-------------------- const PRInt32 kNameBufferSize = 4; class HTMLAttributesImpl: public nsIHTMLAttributes { public: void* operator new(size_t size); void* operator new(size_t size, nsIArena* aArena); void operator delete(void* ptr); HTMLAttributesImpl(void); HTMLAttributesImpl(const HTMLAttributesImpl& aCopy); virtual ~HTMLAttributesImpl(void); NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); NS_IMETHOD_(nsrefcnt) AddRef(); NS_IMETHOD_(nsrefcnt) Release(); // nsIHTMLAttributes NS_IMETHOD SetAttributeFor(nsIAtom* aAttrName, const nsHTMLValue& aValue, PRBool aMappedToStyle, nsIHTMLContent* aContent, nsIHTMLStyleSheet* aSheet, PRInt32& aAttrCount); NS_IMETHOD SetAttributeFor(nsIAtom* aAttrName, const nsAReadableString& aValue, PRBool aMappedToStyle, nsIHTMLContent* aContent, nsIHTMLStyleSheet* aSheet); NS_IMETHOD UnsetAttributeFor(nsIAtom* aAttrName, nsIHTMLContent* aContent, nsIHTMLStyleSheet* aSheet, PRInt32& aAttrCount); NS_IMETHOD GetAttribute(nsIAtom* aAttrName, nsHTMLValue& aValue) const; NS_IMETHOD GetAttribute(nsIAtom* aAttribute, const nsHTMLValue** aValue) const; NS_IMETHOD_(PRBool) HasAttribute(nsIAtom* aAttrName) const; NS_IMETHOD GetAttributeNameAt(PRInt32 aIndex, nsIAtom*& aName) const; NS_IMETHOD GetAttributeCount(PRInt32& aCount) const; NS_IMETHOD GetID(nsIAtom*& aResult) const; NS_IMETHOD GetClasses(nsVoidArray& aArray) const; NS_IMETHOD HasClass(nsIAtom* aClass, PRBool aCaseSensitive) const; NS_IMETHOD Clone(nsIHTMLAttributes** aInstancePtrResult) const; NS_IMETHOD SetStyleSheet(nsIHTMLStyleSheet* aSheet); NS_IMETHOD WalkMappedAttributeStyleRules(nsRuleWalker* aRuleWalker) const; #ifdef UNIQUE_ATTR_SUPPORT NS_IMETHOD AddContentRef(void); NS_IMETHOD ReleaseContentRef(void); NS_IMETHOD GetContentRefCount(PRInt32& aCount) const; #endif NS_IMETHOD Reset(void); #ifdef DEBUG NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0) const; void SizeOf(nsISizeOfHandler* aSizer, PRUint32 &aResult); #endif protected: virtual nsresult SetAttributeName(nsIAtom* aAttrName, PRBool& aFound); virtual nsresult UnsetAttributeName(nsIAtom* aAttrName, PRBool& aFound); virtual nsresult EnsureSingleMappedFor(nsIHTMLContent* aContent, nsIHTMLStyleSheet* aSheet, PRBool aCreate); virtual nsresult UniqueMapped(nsIHTMLStyleSheet* aSheet); private: HTMLAttributesImpl& operator=(const HTMLAttributesImpl& aCopy); PRBool operator==(const HTMLAttributesImpl& aCopy) const; protected: PRUint32 mInHeap : 1; PRUint32 mRefCnt : 31; NS_DECL_OWNINGTHREAD // for thread-safety checking nsIAtom** mAttrNames; PRInt32 mAttrCount; PRInt32 mAttrSize; HTMLAttribute* mFirstUnmapped; nsHTMLMappedAttributes* mMapped; nsIAtom* mID; nsClassList mFirstClass; nsIAtom* mNameBuffer[kNameBufferSize]; }; void* HTMLAttributesImpl::operator new(size_t size) { HTMLAttributesImpl* rv = (HTMLAttributesImpl*) ::operator new(size); #ifdef NS_DEBUG if (nsnull != rv) { nsCRT::memset(rv, 0xEE, size); } #endif rv->mInHeap = 1; return (void*) rv; } void* HTMLAttributesImpl::operator new(size_t size, nsIArena* aArena) { HTMLAttributesImpl* rv = (HTMLAttributesImpl*) aArena->Alloc(PRInt32(size)); #ifdef NS_DEBUG if (nsnull != rv) { nsCRT::memset(rv, 0xEE, size); } #endif rv->mInHeap = 0; return (void*) rv; } void HTMLAttributesImpl::operator delete(void* ptr) { HTMLAttributesImpl* attr = (HTMLAttributesImpl*) ptr; if (nsnull != attr) { if (attr->mInHeap) { ::operator delete(ptr); } } } HTMLAttributesImpl::HTMLAttributesImpl(void) : mAttrNames(mNameBuffer), mAttrCount(0), mAttrSize(kNameBufferSize), mFirstUnmapped(nsnull), mMapped(nsnull), mID(nsnull), mFirstClass(nsnull) { NS_INIT_REFCNT(); } HTMLAttributesImpl::HTMLAttributesImpl(const HTMLAttributesImpl& aCopy) : mAttrNames(mNameBuffer), mAttrCount(aCopy.mAttrCount), mAttrSize(kNameBufferSize), mFirstUnmapped(nsnull), mMapped(aCopy.mMapped), mID(aCopy.mID), mFirstClass(aCopy.mFirstClass) { NS_INIT_REFCNT(); if (mAttrCount) { if (mAttrSize < mAttrCount) { mAttrNames = new nsIAtom*[mAttrCount]; if (mAttrNames) { mAttrSize = mAttrCount; } else { // new buffer failed, deal with it mAttrNames = mNameBuffer; mAttrCount = 0; } } PRInt32 index = mAttrCount; while (0 < index--) { mAttrNames[index] = aCopy.mAttrNames[index]; NS_ADDREF(mAttrNames[index]); } } HTMLAttribute::CopyHTMLAttributes(aCopy.mFirstUnmapped, &mFirstUnmapped); if (mMapped) { mMapped->AddUse(); NS_ADDREF(mMapped); } NS_IF_ADDREF(mID); } HTMLAttributesImpl::~HTMLAttributesImpl(void) { Reset(); } NS_IMPL_ISUPPORTS1(HTMLAttributesImpl, nsIHTMLAttributes) const PRUnichar kNullCh = PRUnichar('\0'); static void ParseClasses(const nsAReadableString& aClassString, nsClassList& aClassList) { nsAutoString classStr(aClassString); // copy to work buffer classStr.Append(kNullCh); // put an extra null at the end PRUnichar* start = (PRUnichar*)(const PRUnichar*)classStr.get(); PRUnichar* end = start; nsClassList* list = &aClassList; while (list && (kNullCh != *start)) { while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space start++; } end = start; while ((kNullCh != *end) && (PR_FALSE == nsCRT::IsAsciiSpace(*end))) { // look for space or end end++; } *end = kNullCh; // end string here if (start < end) { if (! list->mAtom) { list->mAtom = NS_NewAtom(start); } else { list->mNext = new nsClassList(NS_NewAtom(start)); list = list->mNext; } } start = ++end; } } nsresult HTMLAttributesImpl::SetAttributeName(nsIAtom* aAttrName, PRBool& aFound) { PRInt32 index = mAttrCount; while (0 < index--) { if (mAttrNames[index] == aAttrName) { aFound = PR_TRUE; return NS_OK; } } aFound = PR_FALSE; if (mAttrCount == mAttrSize) { // no more room, grow buffer nsIAtom** buffer = new nsIAtom*[mAttrSize + 4]; if (buffer) { nsCRT::memcpy(buffer, mAttrNames, sizeof(nsIAtom*) * mAttrCount); mAttrSize += 4; if (mAttrNames != mNameBuffer) { delete [] mAttrNames; } mAttrNames = buffer; } else { return NS_ERROR_OUT_OF_MEMORY; } } mAttrNames[mAttrCount++] = aAttrName; NS_ADDREF(aAttrName); return NS_OK; } nsresult HTMLAttributesImpl::UnsetAttributeName(nsIAtom* aAttrName, PRBool& aFound) { PRInt32 index = mAttrCount; while (0 < index--) { if (mAttrNames[index] == aAttrName) { mAttrCount--; if ((mAttrNames != mNameBuffer) && (mAttrCount <= (kNameBufferSize / 2))) { // go back to using internal buffer if (0 < index) { nsCRT::memcpy(mNameBuffer, mAttrNames, index * sizeof(nsIAtom*)); } if (index < mAttrCount) { nsCRT::memcpy(&(mNameBuffer[index]), &(mAttrNames[index + 1]), (mAttrCount - index) * sizeof(nsIAtom*)); } delete [] mAttrNames; mAttrNames = mNameBuffer; mAttrSize = kNameBufferSize; } else { if (index < mAttrCount) { nsCRT::memmove(&(mAttrNames[index]), &(mAttrNames[index + 1]), (mAttrCount - index) * sizeof(nsIAtom*)); } } NS_RELEASE(aAttrName); aFound = PR_TRUE; return NS_OK; } } aFound = PR_FALSE; return NS_OK; } nsresult HTMLAttributesImpl::EnsureSingleMappedFor(nsIHTMLContent* aContent, nsIHTMLStyleSheet* aSheet, PRBool aCreate) { nsresult result = NS_OK; if (mMapped) { nsHTMLMappedAttributes* single; result = mMapped->Clone(&single); if (NS_SUCCEEDED(result)) { mMapped->ReleaseUse(); NS_RELEASE(mMapped); mMapped = single; mMapped->AddUse(); } } else if (aCreate) { // create one mMapped = new nsHTMLMappedAttributes(); if (mMapped) { NS_ADDREF(mMapped); mMapped->AddUse(); if (aContent) { nsMapRuleToAttributesFunc mapRuleFunc; aContent->GetAttributeMappingFunction(mapRuleFunc); result = mMapped->Init(aSheet, mapRuleFunc); } } else { result = NS_ERROR_OUT_OF_MEMORY; } } return result; } nsresult HTMLAttributesImpl::UniqueMapped(nsIHTMLStyleSheet* aSheet) { nsresult result = NS_OK; if (aSheet) { nsIHTMLMappedAttributes* mapped; result = aSheet->UniqueMappedAttributes(mMapped, mapped); if (NS_SUCCEEDED(result)) { if (mapped != mMapped) { mMapped->ReleaseUse(); NS_RELEASE(mMapped); mMapped = (nsHTMLMappedAttributes*)mapped; // take ref mMapped->AddUse(); } else { NS_RELEASE(mapped); } } } return result; } NS_IMETHODIMP HTMLAttributesImpl::SetAttributeFor(nsIAtom* aAttrName, const nsAReadableString& aValue, PRBool aMappedToStyle, nsIHTMLContent* aContent, nsIHTMLStyleSheet* aSheet) { nsresult result = NS_OK; if (nsHTMLAtoms::id == aAttrName) { NS_IF_RELEASE(mID); mID = NS_NewAtom(aValue); } else if (nsHTMLAtoms::kClass == aAttrName) { mFirstClass.Reset(); ParseClasses(aValue, mFirstClass); } PRBool haveAttr; result = SetAttributeName(aAttrName, haveAttr); if (NS_SUCCEEDED(result)) { if (aMappedToStyle) { result = EnsureSingleMappedFor(aContent, aSheet, PR_TRUE); if (mMapped) { result = mMapped->SetAttribute(aAttrName, aValue); UniqueMapped(aSheet); } } else { if (haveAttr) { HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, mFirstUnmapped); NS_ASSERTION(attr, "failed to find attribute"); if (attr) { attr->mValue.SetStringValue(aValue); } } else { HTMLAttribute* attr = new HTMLAttribute(aAttrName, aValue); if (attr) { attr->mNext = mFirstUnmapped; mFirstUnmapped = attr; } else { result = NS_ERROR_OUT_OF_MEMORY; } } } } return result; } NS_IMETHODIMP HTMLAttributesImpl::SetAttributeFor(nsIAtom* aAttrName, const nsHTMLValue& aValue, PRBool aMappedToStyle, nsIHTMLContent* aContent, nsIHTMLStyleSheet* aSheet, PRInt32& aCount) { nsresult result = NS_OK; if (eHTMLUnit_Null == aValue.GetUnit()) { return UnsetAttributeFor(aAttrName, aContent, aSheet, aCount); } if (nsHTMLAtoms::id == aAttrName) { NS_IF_RELEASE(mID); if (eHTMLUnit_String == aValue.GetUnit()) { nsAutoString buffer; mID = NS_NewAtom(aValue.GetStringValue(buffer)); } } else if (nsHTMLAtoms::kClass == aAttrName) { mFirstClass.Reset(); if (eHTMLUnit_String == aValue.GetUnit()) { nsAutoString buffer; aValue.GetStringValue(buffer); ParseClasses(buffer, mFirstClass); } } PRBool haveAttr; result = SetAttributeName(aAttrName, haveAttr); if (NS_SUCCEEDED(result)) { if (aMappedToStyle) { result = EnsureSingleMappedFor(aContent, aSheet, PR_TRUE); if (mMapped) { result = mMapped->SetAttribute(aAttrName, aValue); UniqueMapped(aSheet); } } else { if (haveAttr) { HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, mFirstUnmapped); NS_ASSERTION(attr, "failed to find attribute"); if (attr) { attr->mValue = aValue; } } else { HTMLAttribute* attr = new HTMLAttribute(aAttrName, aValue); if (attr) { attr->mNext = mFirstUnmapped; mFirstUnmapped = attr; } else { result = NS_ERROR_OUT_OF_MEMORY; } } } } aCount = mAttrCount; return result; } NS_IMETHODIMP HTMLAttributesImpl::UnsetAttributeFor(nsIAtom* aAttrName, nsIHTMLContent* aContent, nsIHTMLStyleSheet* aSheet, PRInt32& aCount) { nsresult result = NS_OK; if (nsHTMLAtoms::id == aAttrName) { NS_IF_RELEASE(mID); } else if (nsHTMLAtoms::kClass == aAttrName) { mFirstClass.Reset(); } PRBool haveAttr; result = UnsetAttributeName(aAttrName, haveAttr); if (NS_SUCCEEDED(result) && haveAttr) { if (! HTMLAttribute::RemoveHTMLAttribute(aAttrName, &mFirstUnmapped)) { // must be mapped if (mMapped) { EnsureSingleMappedFor(aContent, aSheet, PR_FALSE); PRInt32 mappedCount = 0; mMapped->UnsetAttribute(aAttrName, mappedCount); if (! mappedCount) { // toss it when empty mMapped->ReleaseUse(); NS_RELEASE(mMapped); } else { UniqueMapped(aSheet); } } } } aCount = mAttrCount; return NS_OK; } NS_IMETHODIMP HTMLAttributesImpl::GetAttribute(nsIAtom* aAttrName, nsHTMLValue& aValue) const { nsresult result = NS_CONTENT_ATTR_NOT_THERE; if (mMapped) { result = mMapped->GetAttribute(aAttrName, aValue); } if (NS_CONTENT_ATTR_NOT_THERE == result) { const HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, mFirstUnmapped); if (attr) { aValue = attr->mValue; result = ((attr->mValue.GetUnit() == eHTMLUnit_Null) ? NS_CONTENT_ATTR_NO_VALUE : NS_CONTENT_ATTR_HAS_VALUE); } else { aValue.Reset(); } } return result; } NS_IMETHODIMP HTMLAttributesImpl::GetAttribute(nsIAtom* aAttrName, const nsHTMLValue** aValue) const { nsresult result = NS_CONTENT_ATTR_NOT_THERE; if (mMapped) { result = mMapped->GetAttribute(aAttrName, aValue); } if (NS_CONTENT_ATTR_NOT_THERE == result) { const HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, mFirstUnmapped); if (attr) { *aValue = &attr->mValue; result = ((attr->mValue.GetUnit() == eHTMLUnit_Null) ? NS_CONTENT_ATTR_NO_VALUE : NS_CONTENT_ATTR_HAS_VALUE); } else { *aValue = nsnull; } } return result; } NS_IMETHODIMP_(PRBool) HTMLAttributesImpl::HasAttribute(nsIAtom* aAttrName) const { if (mMapped) return mMapped->HasAttribute(aAttrName); const HTMLAttribute* attr = HTMLAttribute::FindHTMLAttribute(aAttrName, mFirstUnmapped); return attr != nsnull; } NS_IMETHODIMP HTMLAttributesImpl::GetAttributeNameAt(PRInt32 aIndex, nsIAtom*& aName) const { nsresult result = NS_ERROR_ILLEGAL_VALUE; if ((0 <= aIndex) && (aIndex < mAttrCount)) { aName = mAttrNames[aIndex]; NS_ADDREF(aName); result = NS_OK; } return result; } NS_IMETHODIMP HTMLAttributesImpl::GetAttributeCount(PRInt32& aCount) const { aCount = mAttrCount; return NS_OK; } NS_IMETHODIMP HTMLAttributesImpl::GetID(nsIAtom*& aResult) const { aResult = mID; NS_IF_ADDREF(aResult); return NS_OK; } NS_IMETHODIMP HTMLAttributesImpl::GetClasses(nsVoidArray& aArray) const { aArray.Clear(); const nsClassList* classList = &mFirstClass; while (classList && classList->mAtom) { aArray.AppendElement(classList->mAtom); // NOTE atom is not addrefed classList = classList->mNext; } return NS_OK; } NS_IMETHODIMP HTMLAttributesImpl::HasClass(nsIAtom* aClass, PRBool aCaseSensitive) const { NS_PRECONDITION(aClass, "unexpected null pointer"); if (mFirstClass.mAtom) { const nsClassList* classList = &mFirstClass; if (aCaseSensitive) { do { if (classList->mAtom == aClass) return NS_OK; classList = classList->mNext; } while (classList); } else { const PRUnichar* class1Buf; aClass->GetUnicode(&class1Buf); // This length calculation (and the |aCaseSensitive| check above) could // theoretically be pulled out of another loop by creating a separate // |HasClassCI| function. nsDependentString class1(class1Buf); do { const PRUnichar* class2Buf; classList->mAtom->GetUnicode(&class2Buf); nsDependentString class2(class2Buf); if (Compare(class1, class2, nsCaseInsensitiveStringComparator()) == 0) return NS_OK; classList = classList->mNext; } while (classList); } } return NS_COMFALSE; } #ifdef UNIQUE_ATTR_SUPPORT NS_IMETHODIMP HTMLAttributesImpl::AddContentRef(void) { ++mContentRefCount; return NS_OK; } NS_IMETHODIMP HTMLAttributesImpl::ReleaseContentRef(void) { --mContentRefCount; return NS_OK; } NS_IMETHODIMP HTMLAttributesImpl::GetContentRefCount(PRInt32& aCount) const { aCount = mContentRefCount; return NS_OK; } #endif NS_IMETHODIMP HTMLAttributesImpl::Clone(nsIHTMLAttributes** aInstancePtrResult) const { if (aInstancePtrResult == nsnull) { return NS_ERROR_NULL_POINTER; } HTMLAttributesImpl* clone = new HTMLAttributesImpl(*this); if (nsnull == clone) { return NS_ERROR_OUT_OF_MEMORY; } return clone->QueryInterface(NS_GET_IID(nsIHTMLAttributes), (void **) aInstancePtrResult); } NS_IMETHODIMP HTMLAttributesImpl::SetStyleSheet(nsIHTMLStyleSheet* aSheet) { if (mMapped && (aSheet != mMapped->mSheet)) { mMapped->SetStyleSheet(aSheet); return UniqueMapped(aSheet); } return NS_OK; } NS_IMETHODIMP HTMLAttributesImpl::WalkMappedAttributeStyleRules(nsRuleWalker* aRuleWalker) const { if (aRuleWalker && mMapped) aRuleWalker->Forward((nsIStyleRule*)mMapped); return NS_OK; } NS_IMETHODIMP HTMLAttributesImpl::Reset(void) { // Release atoms first, then the table if it was malloc'd PRInt32 i, n = mAttrCount; for (i = 0; i < n; i++) { NS_IF_RELEASE(mAttrNames[i]); } if (mAttrNames != mNameBuffer) { delete [] mAttrNames; mAttrNames = mNameBuffer; mAttrSize = kNameBufferSize; } mAttrCount = 0; if (mFirstUnmapped) { HTMLAttribute::DeleteHTMLAttributes(mFirstUnmapped); } if (mMapped) { mMapped->ReleaseUse(); NS_RELEASE(mMapped); } NS_IF_RELEASE(mID); mFirstClass.Reset(); return NS_OK; } #ifdef DEBUG NS_IMETHODIMP HTMLAttributesImpl::List(FILE* out, PRInt32 aIndent) const { PRInt32 index = 0; while (index < mAttrCount) { PRInt32 indent; for (indent = aIndent; --indent >= 0; ) fputs(" ", out); nsHTMLValue value; GetAttribute(mAttrNames[index], value); nsAutoString buffer; mAttrNames[index]->ToString(buffer); if (eHTMLUnit_Null != value.GetUnit()) { buffer.Append(NS_LITERAL_STRING(" = ")); value.AppendToString(buffer); } fputs(NS_LossyConvertUCS2toASCII(buffer).get(), out); } return NS_OK; } void HTMLAttributesImpl::SizeOf(nsISizeOfHandler* aSizer, PRUint32 &aResult) { PRUint32 sum = 0; // first get the unique items collection UNIQUE_STYLE_ITEMS(uniqueItems); if(! uniqueItems->AddItem((void*)this)){ // this is already accounted for return; } // XXX step through this again sum = sizeof(*this); if (mAttrNames != mNameBuffer) { sum += sizeof(*mAttrNames) * mAttrSize; } if (mFirstUnmapped) { HTMLAttribute* ha = mFirstUnmapped; while (ha) { PRUint32 asum = 0; ha->SizeOf(aSizer, &asum); // XXX Unique??? sum += asum; ha = ha->mNext; } } if (mMapped) { PRBool recorded; aSizer->RecordObject((void*)mMapped, &recorded); if (!recorded) { PRUint32 asum = 0; mMapped->SizeOf(aSizer, asum); // XXX Unique??? sum += asum; } } aResult = sum; nsCOMPtr tag; tag = getter_AddRefs(NS_NewAtom("HTMLAttributesImpl")); aSizer->AddSize(tag, aResult); } #endif nsresult NS_NewHTMLAttributes(nsIHTMLAttributes** aInstancePtrResult) { *aInstancePtrResult = new HTMLAttributesImpl(); NS_ENSURE_TRUE(*aInstancePtrResult, NS_ERROR_OUT_OF_MEMORY); NS_ADDREF(*aInstancePtrResult); return NS_OK; }