/* -*- 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 "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "nsINameSpaceManager.h" #include "nsIHTMLStyleSheet.h" #include "nsIArena.h" #include "nsCRT.h" #include "nsIAtom.h" #include "nsIURL.h" #include "nsISupportsArray.h" #include "nsHashtable.h" #include "nsIHTMLContent.h" #include "nsIHTMLAttributes.h" #include "nsIStyleRule.h" #include "nsIFrame.h" #include "nsIStyleContext.h" #include "nsHTMLAtoms.h" #include "nsIPresContext.h" #include "nsILinkHandler.h" #include "nsIEventStateManager.h" #include "nsIDocument.h" #include "nsHTMLIIDs.h" #include "nsCSSFrameConstructor.h" #include "nsHTMLParts.h" #include "nsIPresShell.h" #include "nsStyleConsts.h" #include "nsHTMLContainerFrame.h" #include "nsINameSpaceManager.h" #include "nsLayoutAtoms.h" static NS_DEFINE_IID(kIHTMLStyleSheetIID, NS_IHTML_STYLE_SHEET_IID); static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID); static NS_DEFINE_IID(kIStyleRuleIID, NS_ISTYLE_RULE_IID); static NS_DEFINE_IID(kIStyleFrameConstructionIID, NS_ISTYLE_FRAME_CONSTRUCTION_IID); class HTMLAnchorRule : public nsIStyleRule { public: HTMLAnchorRule(nsIHTMLStyleSheet* aSheet); ~HTMLAnchorRule(); NS_DECL_ISUPPORTS NS_IMETHOD Equals(const nsIStyleRule* aRule, PRBool& aValue) const; NS_IMETHOD HashValue(PRUint32& aValue) const; NS_IMETHOD GetStyleSheet(nsIStyleSheet*& aSheet) const; // Strength is an out-of-band weighting, always 0 here NS_IMETHOD GetStrength(PRInt32& aStrength) const; NS_IMETHOD MapFontStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext); NS_IMETHOD MapStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext); NS_IMETHOD List(FILE* out = stdout, PRInt32 aIndent = 0) const; nscolor mColor; nsIHTMLStyleSheet* mSheet; }; HTMLAnchorRule::HTMLAnchorRule(nsIHTMLStyleSheet* aSheet) : mSheet(aSheet) { NS_INIT_REFCNT(); } HTMLAnchorRule::~HTMLAnchorRule() { } NS_IMPL_ISUPPORTS(HTMLAnchorRule, kIStyleRuleIID); NS_IMETHODIMP HTMLAnchorRule::Equals(const nsIStyleRule* aRule, PRBool& aResult) const { aResult = PRBool(this == aRule); return NS_OK; } NS_IMETHODIMP HTMLAnchorRule::HashValue(PRUint32& aValue) const { aValue = (PRUint32)(mColor); return NS_OK; } NS_IMETHODIMP HTMLAnchorRule::GetStyleSheet(nsIStyleSheet*& aSheet) const { NS_IF_ADDREF(mSheet); aSheet = mSheet; return NS_OK; } // Strength is an out-of-band weighting, always 0 here NS_IMETHODIMP HTMLAnchorRule::GetStrength(PRInt32& aStrength) const { aStrength = 0; return NS_OK; } NS_IMETHODIMP HTMLAnchorRule::MapFontStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext) { return NS_OK; } NS_IMETHODIMP HTMLAnchorRule::MapStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext) { nsStyleColor* styleColor = (nsStyleColor*)(aContext->GetMutableStyleData(eStyleStruct_Color)); if (nsnull != styleColor) { styleColor->mColor = mColor; } return NS_OK; } NS_IMETHODIMP HTMLAnchorRule::List(FILE* out, PRInt32 aIndent) const { return NS_OK; } // ----------------------------------------------------------- class AttributeKey: public nsHashKey { public: AttributeKey(nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc, nsIHTMLAttributes* aAttributes); virtual ~AttributeKey(void); PRBool Equals(const nsHashKey* aOther) const; PRUint32 HashValue(void) const; nsHashKey* Clone(void) const; private: AttributeKey(void); AttributeKey(const AttributeKey& aCopy); AttributeKey& operator=(const AttributeKey& aCopy); public: nsMapAttributesFunc mFontMapFunc; nsMapAttributesFunc mMapFunc; nsIHTMLAttributes* mAttributes; union { struct { PRUint32 mHashSet: 1; PRUint32 mHashCode: 31; } mBits; PRUint32 mInitializer; } mHash; }; AttributeKey::AttributeKey(nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc, nsIHTMLAttributes* aAttributes) : mFontMapFunc(aFontMapFunc), mMapFunc(aMapFunc), mAttributes(aAttributes) { NS_ADDREF(mAttributes); mHash.mInitializer = 0; } AttributeKey::~AttributeKey(void) { NS_RELEASE(mAttributes); } PRBool AttributeKey::Equals(const nsHashKey* aOther) const { const AttributeKey* other = (const AttributeKey*)aOther; if ((mMapFunc == other->mMapFunc) && (mFontMapFunc == other->mFontMapFunc)) { PRBool equals; mAttributes->Equals(other->mAttributes, equals); return equals; } return PR_FALSE; } PRUint32 AttributeKey::HashValue(void) const { if (0 == mHash.mBits.mHashSet) { AttributeKey* self = (AttributeKey*)this; // break const PRUint32 hash; mAttributes->HashValue(hash); self->mHash.mBits.mHashCode = (0x7FFFFFFF & hash); self->mHash.mBits.mHashCode |= (0x7FFFFFFF & PRUint32(mFontMapFunc)); self->mHash.mBits.mHashCode |= (0x7FFFFFFF & PRUint32(mMapFunc)); self->mHash.mBits.mHashSet = 1; } return mHash.mBits.mHashCode; } nsHashKey* AttributeKey::Clone(void) const { AttributeKey* clown = new AttributeKey(mFontMapFunc, mMapFunc, mAttributes); if (nsnull != clown) { clown->mHash.mInitializer = mHash.mInitializer; } return clown; } // ----------------------------------------------------------- class HTMLStyleSheetImpl : public nsIHTMLStyleSheet { public: void* operator new(size_t size); void* operator new(size_t size, nsIArena* aArena); void operator delete(void* ptr); HTMLStyleSheetImpl(void); NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); NS_IMETHOD_(nsrefcnt) AddRef(); NS_IMETHOD_(nsrefcnt) Release(); // nsIStyleSheet api NS_IMETHOD GetURL(nsIURL*& aURL) const; NS_IMETHOD GetTitle(nsString& aTitle) const; NS_IMETHOD GetType(nsString& aType) const; NS_IMETHOD GetMediumCount(PRInt32& aCount) const; NS_IMETHOD GetMediumAt(PRInt32 aIndex, nsIAtom*& aMedium) const; NS_IMETHOD GetEnabled(PRBool& aEnabled) const; NS_IMETHOD SetEnabled(PRBool aEnabled); // style sheet owner info NS_IMETHOD GetParentSheet(nsIStyleSheet*& aParent) const; // will be null NS_IMETHOD GetOwningDocument(nsIDocument*& aDocument) const; NS_IMETHOD SetOwningDocument(nsIDocument* aDocumemt); virtual PRInt32 RulesMatching(nsIPresContext* aPresContext, nsIContent* aContent, nsIStyleContext* aParentContext, nsISupportsArray* aResults); virtual PRInt32 RulesMatching(nsIPresContext* aPresContext, nsIContent* aParentContent, nsIAtom* aPseudoTag, nsIStyleContext* aParentContext, nsISupportsArray* aResults); NS_IMETHOD HasStateDependentStyle(nsIPresContext* aPresContext, nsIContent* aContent); // nsIHTMLStyleSheet api NS_IMETHOD Init(nsIURL* aURL, nsIDocument* aDocument); NS_IMETHOD Reset(nsIURL* aURL); NS_IMETHOD SetLinkColor(nscolor aColor); NS_IMETHOD SetActiveLinkColor(nscolor aColor); NS_IMETHOD SetVisitedLinkColor(nscolor aColor); // Attribute management methods, aAttributes is an in/out param NS_IMETHOD SetAttributesFor(nsIHTMLContent* aContent, nsIHTMLAttributes*& aAttributes); NS_IMETHOD SetAttributeFor(nsIAtom* aAttribute, const nsString& aValue, nsIHTMLContent* aContent, nsIHTMLAttributes*& aAttributes); NS_IMETHOD SetAttributeFor(nsIAtom* aAttribute, const nsHTMLValue& aValue, nsIHTMLContent* aContent, nsIHTMLAttributes*& aAttributes); NS_IMETHOD UnsetAttributeFor(nsIAtom* aAttribute, nsIHTMLContent* aContent, nsIHTMLAttributes*& aAttributes); virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const; private: // These are not supported and are not implemented! HTMLStyleSheetImpl(const HTMLStyleSheetImpl& aCopy); HTMLStyleSheetImpl& operator=(const HTMLStyleSheetImpl& aCopy); protected: virtual ~HTMLStyleSheetImpl(); NS_IMETHOD EnsureSingleAttributes(nsIHTMLAttributes*& aAttributes, nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc, PRBool aCreate, nsIHTMLAttributes*& aSingleAttrs); NS_IMETHOD UniqueAttributes(nsIHTMLAttributes*& aSingleAttrs, nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc, PRInt32 aAttrCount, nsIHTMLAttributes*& aAttributes); protected: PRUint32 mInHeap : 1; PRUint32 mRefCnt : 31; nsIURL* mURL; nsIDocument* mDocument; HTMLAnchorRule* mLinkRule; HTMLAnchorRule* mVisitedRule; HTMLAnchorRule* mActiveRule; nsHashtable mAttrTable; nsIHTMLAttributes* mRecycledAttrs; }; void* HTMLStyleSheetImpl::operator new(size_t size) { HTMLStyleSheetImpl* rv = (HTMLStyleSheetImpl*) ::operator new(size); #ifdef NS_DEBUG if (nsnull != rv) { nsCRT::memset(rv, 0xEE, size); } #endif rv->mInHeap = 1; return (void*) rv; } void* HTMLStyleSheetImpl::operator new(size_t size, nsIArena* aArena) { HTMLStyleSheetImpl* rv = (HTMLStyleSheetImpl*) aArena->Alloc(PRInt32(size)); #ifdef NS_DEBUG if (nsnull != rv) { nsCRT::memset(rv, 0xEE, size); } #endif rv->mInHeap = 0; return (void*) rv; } void HTMLStyleSheetImpl::operator delete(void* ptr) { HTMLStyleSheetImpl* sheet = (HTMLStyleSheetImpl*) ptr; if (nsnull != sheet) { if (sheet->mInHeap) { ::operator delete(ptr); } } } HTMLStyleSheetImpl::HTMLStyleSheetImpl(void) : nsIHTMLStyleSheet(), mURL(nsnull), mDocument(nsnull), mLinkRule(nsnull), mVisitedRule(nsnull), mActiveRule(nsnull), mRecycledAttrs(nsnull) { NS_INIT_REFCNT(); } HTMLStyleSheetImpl::~HTMLStyleSheetImpl() { NS_RELEASE(mURL); if (nsnull != mLinkRule) { mLinkRule->mSheet = nsnull; NS_RELEASE(mLinkRule); } if (nsnull != mVisitedRule) { mVisitedRule->mSheet = nsnull; NS_RELEASE(mVisitedRule); } if (nsnull != mActiveRule) { mActiveRule->mSheet = nsnull; NS_RELEASE(mActiveRule); } NS_IF_RELEASE(mRecycledAttrs); } NS_IMPL_ADDREF(HTMLStyleSheetImpl) NS_IMPL_RELEASE(HTMLStyleSheetImpl) nsresult HTMLStyleSheetImpl::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(kIHTMLStyleSheetIID)) { *aInstancePtrResult = (void*) ((nsIHTMLStyleSheet*)this); NS_ADDREF_THIS(); return NS_OK; } if (aIID.Equals(kIStyleSheetIID)) { *aInstancePtrResult = (void*) ((nsIStyleSheet*)this); NS_ADDREF_THIS(); return NS_OK; } if (aIID.Equals(kIStyleFrameConstructionIID)) { // XXX this breaks XPCOM rules since it isn't a proper delegate // This is a temporary method of connecting the constructor for now nsCSSFrameConstructor* constructor = new nsCSSFrameConstructor(); if (nsnull != constructor) { constructor->Init(mDocument); nsresult result = constructor->QueryInterface(aIID, aInstancePtrResult); if (NS_FAILED(result)) { delete constructor; } return result; } return NS_ERROR_OUT_OF_MEMORY; } if (aIID.Equals(kISupportsIID)) { *aInstancePtrResult = (void*) this; NS_ADDREF_THIS(); return NS_OK; } return NS_NOINTERFACE; } PRInt32 HTMLStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext, nsIContent* aContent, nsIStyleContext* aParentContext, nsISupportsArray* aResults) { NS_PRECONDITION(nsnull != aPresContext, "null arg"); NS_PRECONDITION(nsnull != aContent, "null arg"); NS_PRECONDITION(nsnull != aResults, "null arg"); PRInt32 matchCount = 0; nsIStyledContent* styledContent; if (NS_SUCCEEDED(aContent->QueryInterface(nsIStyledContent::GetIID(), (void**)&styledContent))) { PRInt32 nameSpace; styledContent->GetNameSpaceID(nameSpace); if (kNameSpaceID_HTML == nameSpace) { nsIAtom* tag; styledContent->GetTag(tag); // if we have anchor colors, check if this is an anchor with an href if (tag == nsHTMLAtoms::a) { if ((nsnull != mLinkRule) || (nsnull != mVisitedRule)) { // test link state nsILinkHandler* linkHandler; if ((NS_OK == aPresContext->GetLinkHandler(&linkHandler)) && (nsnull != linkHandler)) { nsAutoString base, href; nsresult attrState = styledContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::href, href); if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { nsIURL* docURL = nsnull; nsIHTMLContent* htmlContent; if (NS_SUCCEEDED(styledContent->QueryInterface(kIHTMLContentIID, (void**)&htmlContent))) { htmlContent->GetBaseURL(docURL); nsAutoString absURLSpec; nsresult rv = NS_MakeAbsoluteURL(docURL, base, href, absURLSpec); NS_IF_RELEASE(docURL); nsLinkState state; if (NS_OK == linkHandler->GetLinkState(absURLSpec.GetUnicode(), state)) { switch (state) { case eLinkState_Unvisited: if (nsnull != mLinkRule) { aResults->AppendElement(mLinkRule); matchCount++; } break; case eLinkState_Visited: if (nsnull != mVisitedRule) { aResults->AppendElement(mVisitedRule); matchCount++; } break; } } NS_RELEASE(htmlContent); } } } NS_IF_RELEASE(linkHandler); } // end link/visited rules if (nsnull != mActiveRule) { // test active state of link nsIEventStateManager* eventStateManager; if ((NS_OK == aPresContext->GetEventStateManager(&eventStateManager)) && (nsnull != eventStateManager)) { PRInt32 state; if (NS_OK == eventStateManager->GetContentState(aContent, state)) { if (0 != (state & NS_EVENT_STATE_ACTIVE)) { aResults->AppendElement(mActiveRule); matchCount++; } } NS_RELEASE(eventStateManager); } } // end active rule } // end A tag NS_IF_RELEASE(tag); } // end html namespace // just get the one and only style rule from the content nsIStyleRule* rule; styledContent->GetContentStyleRule(rule); if (nsnull != rule) { aResults->AppendElement(rule); NS_RELEASE(rule); matchCount++; } NS_RELEASE(styledContent); } return matchCount; } // Test if style is dependent on content state NS_IMETHODIMP HTMLStyleSheetImpl::HasStateDependentStyle(nsIPresContext* aPresContext, nsIContent* aContent) { nsresult result = NS_COMFALSE; if (mActiveRule || mLinkRule || mVisitedRule) { // do we have any rules dependent on state? nsIStyledContent* styledContent; if (NS_SUCCEEDED(aContent->QueryInterface(nsIStyledContent::GetIID(), (void**)&styledContent))) { PRInt32 nameSpace; styledContent->GetNameSpaceID(nameSpace); if (kNameSpaceID_HTML == nameSpace) { nsIAtom* tag; styledContent->GetTag(tag); // if we have anchor colors, check if this is an anchor with an href if (tag == nsHTMLAtoms::a) { nsAutoString href; nsresult attrState = styledContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::href, href); if (NS_CONTENT_ATTR_HAS_VALUE == attrState) { result = NS_OK; // yes, style will depend on link state } } NS_IF_RELEASE(tag); } NS_RELEASE(styledContent); } } return result; } PRInt32 HTMLStyleSheetImpl::RulesMatching(nsIPresContext* aPresContext, nsIContent* aParentContent, nsIAtom* aPseudoTag, nsIStyleContext* aParentContext, nsISupportsArray* aResults) { // no pseudo frame style return 0; } // nsIStyleSheet api NS_IMETHODIMP HTMLStyleSheetImpl::GetURL(nsIURL*& aURL) const { NS_IF_ADDREF(mURL); aURL = mURL; return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::GetTitle(nsString& aTitle) const { aTitle.Truncate(); return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::GetType(nsString& aType) const { aType.Truncate(); aType.Append("text/html"); return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::GetMediumCount(PRInt32& aCount) const { aCount = 0; return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::GetMediumAt(PRInt32 aIndex, nsIAtom*& aMedium) const { aMedium = nsnull; return NS_ERROR_INVALID_ARG; } NS_IMETHODIMP HTMLStyleSheetImpl::GetEnabled(PRBool& aEnabled) const { aEnabled = PR_TRUE; return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::SetEnabled(PRBool aEnabled) { // these can't be disabled return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::GetParentSheet(nsIStyleSheet*& aParent) const { aParent = nsnull; return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::GetOwningDocument(nsIDocument*& aDocument) const { NS_IF_ADDREF(mDocument); aDocument = mDocument; return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::SetOwningDocument(nsIDocument* aDocument) { mDocument = aDocument; // not refcounted return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::Init(nsIURL* aURL, nsIDocument* aDocument) { NS_PRECONDITION(aURL && aDocument, "null ptr"); if (! aURL || ! aDocument) return NS_ERROR_NULL_POINTER; if (mURL || mDocument) return NS_ERROR_ALREADY_INITIALIZED; mDocument = aDocument; // not refcounted! mURL = aURL; NS_ADDREF(mURL); return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::Reset(nsIURL* aURL) { NS_IF_RELEASE(mURL); mURL = aURL; NS_ADDREF(mURL); if (nsnull != mLinkRule) { mLinkRule->mSheet = nsnull; NS_RELEASE(mLinkRule); } if (nsnull != mVisitedRule) { mVisitedRule->mSheet = nsnull; NS_RELEASE(mVisitedRule); } if (nsnull != mActiveRule) { mActiveRule->mSheet = nsnull; NS_RELEASE(mActiveRule); } mAttrTable.Reset(); return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::SetLinkColor(nscolor aColor) { if (nsnull == mLinkRule) { mLinkRule = new HTMLAnchorRule(this); if (nsnull == mLinkRule) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(mLinkRule); } mLinkRule->mColor = aColor; return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::SetActiveLinkColor(nscolor aColor) { if (nsnull == mActiveRule) { mActiveRule = new HTMLAnchorRule(this); if (nsnull == mActiveRule) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(mActiveRule); } mActiveRule->mColor = aColor; return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::SetVisitedLinkColor(nscolor aColor) { if (nsnull == mVisitedRule) { mVisitedRule = new HTMLAnchorRule(this); if (nsnull == mVisitedRule) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(mVisitedRule); } mVisitedRule->mColor = aColor; return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::SetAttributesFor(nsIHTMLContent* aContent, nsIHTMLAttributes*& aAttributes) { nsIHTMLAttributes* attrs = aAttributes; if (nsnull != attrs) { nsMapAttributesFunc fontMapFunc; nsMapAttributesFunc mapFunc; aContent->GetAttributeMappingFunctions(fontMapFunc, mapFunc); AttributeKey key(fontMapFunc, mapFunc, attrs); nsIHTMLAttributes* sharedAttrs = (nsIHTMLAttributes*)mAttrTable.Get(&key); if (nsnull == sharedAttrs) { // we have a new unique set mAttrTable.Put(&key, attrs); } else { // found existing set if (sharedAttrs != aAttributes) { aAttributes->ReleaseContentRef(); NS_RELEASE(aAttributes); // release content's ref aAttributes = sharedAttrs; aAttributes->AddContentRef(); NS_ADDREF(aAttributes); // add ref for content } } } return NS_OK; } NS_IMETHODIMP HTMLStyleSheetImpl::SetAttributeFor(nsIAtom* aAttribute, const nsString& aValue, nsIHTMLContent* aContent, nsIHTMLAttributes*& aAttributes) { nsresult result = NS_OK; nsIHTMLAttributes* attrs; nsMapAttributesFunc fontMapFunc; nsMapAttributesFunc mapFunc; aContent->GetAttributeMappingFunctions(fontMapFunc, mapFunc); result = EnsureSingleAttributes(aAttributes, fontMapFunc, mapFunc, PR_TRUE, attrs); if ((NS_OK == result) && (nsnull != attrs)) { PRInt32 count; attrs->SetAttribute(aAttribute, aValue, count); result = UniqueAttributes(attrs, fontMapFunc, mapFunc, count, aAttributes); } return result; } #ifdef NS_DEBUG #define NS_ASSERT_REFCOUNT(ptr,cnt,msg) { \ nsrefcnt count = ptr->AddRef(); \ ptr->Release(); \ NS_ASSERTION(--count == cnt, msg); \ } #else #define NS_ASSERT_REFCOUNT(ptr,cnt,msg) {} #endif NS_IMETHODIMP HTMLStyleSheetImpl::EnsureSingleAttributes(nsIHTMLAttributes*& aAttributes, nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc, PRBool aCreate, nsIHTMLAttributes*& aSingleAttrs) { nsresult result = NS_OK; PRInt32 contentRefCount; if (nsnull == aAttributes) { if (PR_TRUE == aCreate) { if (nsnull != mRecycledAttrs) { NS_ASSERT_REFCOUNT(mRecycledAttrs, 1, "attributes used elsewhere"); aSingleAttrs = mRecycledAttrs; mRecycledAttrs = nsnull; aSingleAttrs->SetMappingFunctions(aFontMapFunc, aMapFunc); } else { result = NS_NewHTMLAttributes(&aSingleAttrs, this, aFontMapFunc, aMapFunc); } } else { aSingleAttrs = nsnull; } contentRefCount = 0; } else { aSingleAttrs = aAttributes; aSingleAttrs->GetContentRefCount(contentRefCount); NS_ASSERTION(0 < contentRefCount, "bad content ref count"); } if (NS_OK == result) { if (1 < contentRefCount) { // already shared, copy it result = aSingleAttrs->Clone(&aSingleAttrs); if (NS_OK != result) { aSingleAttrs = nsnull; return result; } contentRefCount = 0; aAttributes->ReleaseContentRef(); NS_RELEASE(aAttributes); } else { // one content ref, ok to use, remove from table because hash may change if (1 == contentRefCount) { AttributeKey key(aFontMapFunc, aMapFunc, aSingleAttrs); mAttrTable.Remove(&key); NS_ADDREF(aSingleAttrs); // add a local ref so we match up } } // at this point, content ref count is 0 or 1, and we hold a local ref // attrs is also unique and not in the table NS_ASSERTION(((0 == contentRefCount) && (nsnull == aAttributes)) || ((1 == contentRefCount) && (aSingleAttrs == aAttributes)), "this is broken"); } return result; } NS_IMETHODIMP HTMLStyleSheetImpl::UniqueAttributes(nsIHTMLAttributes*& aSingleAttrs, nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc, PRInt32 aAttrCount, nsIHTMLAttributes*& aAttributes) { nsresult result = NS_OK; if (0 < aAttrCount) { AttributeKey key(aFontMapFunc, aMapFunc, aSingleAttrs); nsIHTMLAttributes* sharedAttrs = (nsIHTMLAttributes*)mAttrTable.Get(&key); if (nsnull == sharedAttrs) { // we have a new unique set mAttrTable.Put(&key, aSingleAttrs); if (aSingleAttrs != aAttributes) { NS_ASSERTION(nsnull == aAttributes, "this is broken"); aAttributes = aSingleAttrs; aAttributes->AddContentRef(); NS_ADDREF(aAttributes); // add ref for content } } else { // found existing set NS_ASSERTION (sharedAttrs != aAttributes, "should never happen"); if (nsnull != aAttributes) { aAttributes->ReleaseContentRef(); NS_RELEASE(aAttributes); // release content's ref } aAttributes = sharedAttrs; aAttributes->AddContentRef(); NS_ADDREF(aAttributes); // add ref for content if (nsnull == mRecycledAttrs) { mRecycledAttrs = aSingleAttrs; NS_ADDREF(mRecycledAttrs); mRecycledAttrs->Reset(); } } } else { // no attributes to store if (nsnull != aAttributes) { aAttributes->ReleaseContentRef(); NS_RELEASE(aAttributes); } if ((nsnull != aSingleAttrs) && (nsnull == mRecycledAttrs)) { mRecycledAttrs = aSingleAttrs; NS_ADDREF(mRecycledAttrs); mRecycledAttrs->Reset(); } } NS_IF_RELEASE(aSingleAttrs); return result; } NS_IMETHODIMP HTMLStyleSheetImpl::SetAttributeFor(nsIAtom* aAttribute, const nsHTMLValue& aValue, nsIHTMLContent* aContent, nsIHTMLAttributes*& aAttributes) { nsresult result = NS_OK; nsIHTMLAttributes* attrs; PRBool hasValue = PRBool(eHTMLUnit_Null != aValue.GetUnit()); nsMapAttributesFunc fontMapFunc; nsMapAttributesFunc mapFunc; aContent->GetAttributeMappingFunctions(fontMapFunc, mapFunc); result = EnsureSingleAttributes(aAttributes, fontMapFunc, mapFunc, hasValue, attrs); if ((NS_OK == result) && (nsnull != attrs)) { PRInt32 count; attrs->SetAttribute(aAttribute, aValue, count); result = UniqueAttributes(attrs, fontMapFunc, mapFunc, count, aAttributes); } return result; } NS_IMETHODIMP HTMLStyleSheetImpl::UnsetAttributeFor(nsIAtom* aAttribute, nsIHTMLContent* aContent, nsIHTMLAttributes*& aAttributes) { nsresult result = NS_OK; nsIHTMLAttributes* attrs; nsMapAttributesFunc fontMapFunc; nsMapAttributesFunc mapFunc; aContent->GetAttributeMappingFunctions(fontMapFunc, mapFunc); result = EnsureSingleAttributes(aAttributes, fontMapFunc, mapFunc, PR_FALSE, attrs); if ((NS_OK == result) && (nsnull != attrs)) { PRInt32 count; attrs->UnsetAttribute(aAttribute, count); result = UniqueAttributes(attrs, fontMapFunc, mapFunc, count, aAttributes); } return result; } void HTMLStyleSheetImpl::List(FILE* out, PRInt32 aIndent) const { PRUnichar* buffer; // Indent for (PRInt32 index = aIndent; --index >= 0; ) fputs(" ", out); fputs("HTML Style Sheet: ", out); mURL->ToString(&buffer); nsAutoString as(buffer,0); fputs(as, out); fputs("\n", out); delete buffer; } // XXX For convenience and backwards compatibility NS_HTML nsresult NS_NewHTMLStyleSheet(nsIHTMLStyleSheet** aInstancePtrResult, nsIURL* aURL, nsIDocument* aDocument) { nsresult rv; nsIHTMLStyleSheet* sheet; if (NS_FAILED(rv = NS_NewHTMLStyleSheet(&sheet))) return rv; if (NS_FAILED(rv = sheet->Init(aURL, aDocument))) { NS_RELEASE(sheet); return rv; } *aInstancePtrResult = sheet; return NS_OK; } NS_HTML nsresult NS_NewHTMLStyleSheet(nsIHTMLStyleSheet** aInstancePtrResult) { if (aInstancePtrResult == nsnull) { return NS_ERROR_NULL_POINTER; } HTMLStyleSheetImpl *it = new HTMLStyleSheetImpl(); if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(it); *aInstancePtrResult = it; return NS_OK; }