Mozilla/mozilla/content/html/style/src/nsHTMLAttributes.cpp
troy%netscape.com eb4d44f03f b=28635, a=rickg@netscape.com, r=vidur@netscape.com
Reduced the number of nsString objects allocated


git-svn-id: svn://10.0.0.236/trunk@61491 18797224-902f-48f8-a5cc-f745e15eee43
2000-02-23 15:45:43 +00:00

1557 lines
38 KiB
C++

/* -*- 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.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 Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*/
#include "nsIHTMLAttributes.h"
#include "nsIHTMLStyleSheet.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"
static NS_DEFINE_IID(kIHTMLAttributesIID, NS_IHTML_ATTRIBUTES_IID);
MOZ_DECL_CTOR_COUNTER(HTMLAttribute);
struct HTMLAttribute {
HTMLAttribute(void)
: mAttribute(nsnull),
mValue(),
mNext(nsnull)
{
MOZ_COUNT_CTOR(HTMLAttribute);
}
HTMLAttribute(nsIAtom* aAttribute, const nsString& 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 PRUint32(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 nsString& aValue)
{
NS_IF_RELEASE(mAttribute);
mAttribute = aAttribute;
NS_IF_ADDREF(mAttribute);
mValue.SetStringValue(aValue);
}
void AppendToString(nsString& aBuffer) const
{
if (nsnull != mAttribute) {
nsAutoString temp;
mAttribute->ToString(temp);
aBuffer.Append(temp);
if (eHTMLUnit_Null != mValue.GetUnit()) {
aBuffer.Append(" = ");
mValue.AppendToString(aBuffer);
}
}
else {
aBuffer.Append("null");
}
}
void ToString(nsString& aBuffer) const
{
if (nsnull != mAttribute) {
mAttribute->ToString(aBuffer);
if (eHTMLUnit_Null != mValue.GetUnit()) {
aBuffer.Append(" = ");
mValue.AppendToString(aBuffer);
}
}
else {
aBuffer.Truncate();
aBuffer.Append("null");
}
}
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,
nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc);
NS_IMETHOD Clone(nsHTMLMappedAttributes** aInstancePtrResult) const;
NS_IMETHOD Reset(void);
NS_IMETHOD SetMappingFunctions(nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc);
NS_IMETHOD SetAttribute(nsIAtom* aAttrName, const nsString& 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 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;
NS_IMETHOD MapFontStyleInto(nsIMutableStyleContext* aContext, nsIPresContext* aPresContext);
NS_IMETHOD MapStyleInto(nsIMutableStyleContext* aContext, nsIPresContext* aPresContext);
NS_IMETHOD List(FILE* out, PRInt32 aIndent) const;
#ifdef DEBUG
nsresult SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const {
if (!aResult) {
return NS_ERROR_NULL_POINTER;
}
*aResult = sizeof(*this);
return NS_OK;
}
#endif
nsIHTMLStyleSheet* mSheet;
PRInt32 mUseCount;
PRInt32 mAttrCount;
HTMLAttribute mFirst;
nsMapAttributesFunc mFontMapper;
nsMapAttributesFunc mMapper;
PRBool mUniqued;
};
nsHTMLMappedAttributes::nsHTMLMappedAttributes(void)
: mSheet(nsnull),
mUseCount(0),
mAttrCount(0),
mFirst(),
mFontMapper(nsnull),
mMapper(nsnull),
mUniqued(PR_FALSE)
{
NS_INIT_ISUPPORTS();
}
nsHTMLMappedAttributes::nsHTMLMappedAttributes(const nsHTMLMappedAttributes& aCopy)
: mSheet(aCopy.mSheet),
mUseCount(0),
mAttrCount(aCopy.mAttrCount),
mFirst(aCopy.mFirst),
mFontMapper(aCopy.mFontMapper),
mMapper(aCopy.mMapper),
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,
nsMapAttributesFunc aFontMapFunc,
nsMapAttributesFunc aMapFunc)
{
mSheet = aSheet;
mFontMapper = aFontMapFunc;
mMapper = aMapFunc;
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::SetMappingFunctions(nsMapAttributesFunc aFontMapFunc,
nsMapAttributesFunc aMapFunc)
{
mFontMapper = aFontMapFunc;
mMapper = aMapFunc;
return NS_OK;
}
NS_IMETHODIMP
nsHTMLMappedAttributes::SetAttribute(nsIAtom* aAttrName, const nsString& 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
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 ((mFontMapper == other->mFontMapper) && (mMapper == other->mMapper) &&
(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 = PRUint32(mFontMapper);
aValue = aValue ^ PRUint32(mMapper);
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::MapFontStyleInto(nsIMutableStyleContext* aContext, nsIPresContext* aPresContext)
{
if (0 < mAttrCount) {
if (mFontMapper) {
(*mFontMapper)(this, aContext, aPresContext);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLMappedAttributes::MapStyleInto(nsIMutableStyleContext* aContext, nsIPresContext* aPresContext)
{
if (0 < mAttrCount) {
NS_ASSERTION(mMapper || mFontMapper, "no mapping function");
if (mMapper) {
(*mMapper)(this, aContext, aPresContext);
}
}
return NS_OK;
}
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(buffer, out);
fputs("\n", out);
attr = attr->mNext;
}
return NS_OK;
}
//--------------------
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 nsString& 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 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) const;
NS_IMETHOD Clone(nsIHTMLAttributes** aInstancePtrResult) const;
NS_IMETHOD SetStyleSheet(nsIHTMLStyleSheet* aSheet);
NS_IMETHOD GetMappedAttributeStyleRules(nsISupportsArray* aArray) const;
#ifdef UNIQUE_ATTR_SUPPORT
NS_IMETHOD AddContentRef(void);
NS_IMETHOD ReleaseContentRef(void);
NS_IMETHOD GetContentRefCount(PRInt32& aCount) const;
#endif
NS_IMETHOD Reset(void);
NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0) const;
NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const;
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;
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_ADDREF(HTMLAttributesImpl)
NS_IMPL_RELEASE(HTMLAttributesImpl)
NS_IMPL_QUERY_INTERFACE(HTMLAttributesImpl, kIHTMLAttributesIID);
const PRUnichar kNullCh = PRUnichar('\0');
static void ParseClasses(const nsString& 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.GetUnicode();
PRUnichar* end = start;
nsClassList* list = &aClassList;
while (list && (kNullCh != *start)) {
while ((kNullCh != *start) && nsString::IsSpace(*start)) { // skip leading space
start++;
}
end = start;
while ((kNullCh != *end) && (PR_FALSE == nsString::IsSpace(*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) {
PRInt32 useCount;
mMapped->GetUseCount(useCount);
if (1 < useCount) { // shared, clone it
nsHTMLMappedAttributes* single;
result = mMapped->Clone(&single);
if (NS_SUCCEEDED(result)) {
mMapped->ReleaseUse();
NS_RELEASE(mMapped);
mMapped = single;
mMapped->AddUse();
}
}
else { // single use, remove it from unique table before modifying
if (aSheet) {
aSheet->DropMappedAttributes(mMapped);
}
}
}
else if (aCreate) { // create one
mMapped = new nsHTMLMappedAttributes();
if (mMapped) {
NS_ADDREF(mMapped);
mMapped->AddUse();
if (aContent) {
nsMapAttributesFunc fontMapFunc;
nsMapAttributesFunc mapFunc;
aContent->GetAttributeMappingFunctions(fontMapFunc, mapFunc);
result = mMapped->Init(aSheet, fontMapFunc, mapFunc);
}
}
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 nsString& 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
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) const
{
const nsClassList* classList = &mFirstClass;
while (classList) {
if (classList->mAtom == aClass) {
return NS_OK;
}
classList = classList->mNext;
}
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::GetMappedAttributeStyleRules(nsISupportsArray* aArray) const
{
if (aArray && mMapped) {
aArray->AppendElement((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;
}
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(" = ");
value.AppendToString(buffer);
}
fputs(buffer, out);
}
return NS_OK;
}
NS_IMETHODIMP
HTMLAttributesImpl::SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const
{
if (!aResult) {
return NS_ERROR_NULL_POINTER;
}
PRUint32 sum = 0;
#ifdef DEBUG
sum = sizeof(*this);
if (mAttrNames != mNameBuffer) {
sum += sizeof(*mAttrNames) * mAttrSize;
}
if (mFirstUnmapped) {
HTMLAttribute* ha = mFirstUnmapped;
while (ha) {
PRUint32 asum = 0;
ha->SizeOf(aSizer, &asum);
sum += asum;
ha = ha->mNext;
}
}
if (mMapped) {
PRBool recorded;
aSizer->RecordObject((void*)mMapped, &recorded);
if (!recorded) {
PRUint32 asum = 0;
mMapped->SizeOf(aSizer, &asum);
sum += asum;
}
}
#endif
*aResult = sum;
return NS_OK;
}
extern NS_HTML nsresult
NS_NewHTMLAttributes(nsIHTMLAttributes** aInstancePtrResult)
{
if (aInstancePtrResult == nsnull) {
return NS_ERROR_NULL_POINTER;
}
HTMLAttributesImpl *it = new HTMLAttributesImpl();
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
return it->QueryInterface(NS_GET_IID(nsIHTMLAttributes), (void **) aInstancePtrResult);
}