diff --git a/mozilla/content/base/public/nsIStyleRuleProcessor.h b/mozilla/content/base/public/nsIStyleRuleProcessor.h index b392d76f84a..aca01834244 100644 --- a/mozilla/content/base/public/nsIStyleRuleProcessor.h +++ b/mozilla/content/base/public/nsIStyleRuleProcessor.h @@ -145,6 +145,21 @@ struct StateRuleProcessorData : public RuleProcessorData { // Constants defined in nsIEventStateManager.h . }; +struct AttributeRuleProcessorData : public RuleProcessorData { + AttributeRuleProcessorData(nsIPresContext* aPresContext, + nsIContent* aContent, + nsIAtom* aAttribute, + PRInt32 aModType) + : RuleProcessorData(aPresContext, aContent, nsnull), + mAttribute(aAttribute), + mModType(aModType) + { + NS_PRECONDITION(aContent, "null pointer"); + } + nsIAtom* mAttribute; // |HasAttributeDependentStyle| for which attribute? + PRInt32 mModType; // The type of modification (see nsIDOMMutationEvent). +}; + // IID for the nsIStyleRuleProcessor interface {015575fe-7b6c-11d3-ba05-001083023c2b} #define NS_ISTYLE_RULE_PROCESSOR_IID \ @@ -172,6 +187,10 @@ public: NS_IMETHOD HasStateDependentStyle(StateRuleProcessorData* aData, nsIAtom* aMedium, PRBool* aResult) = 0; + // Test if style is dependent on attribute + NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult) = 0; }; #endif /* nsIStyleRuleProcessor_h___ */ diff --git a/mozilla/content/base/public/nsIStyleRuleSupplier.h b/mozilla/content/base/public/nsIStyleRuleSupplier.h index 7d3de0cbe89..e2e75a9d68a 100644 --- a/mozilla/content/base/public/nsIStyleRuleSupplier.h +++ b/mozilla/content/base/public/nsIStyleRuleSupplier.h @@ -20,11 +20,6 @@ class nsIStyleRuleSupplier : public nsISupports { NS_IMETHOD WalkRules(nsIStyleSet* aStyleSet, nsISupportsArrayEnumFunc aFunc, RuleProcessorData* aData)=0; - - NS_IMETHOD AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, - void* aData, - nsIContent* aContent, - PRBool* aAffects)=0; }; #endif /* _nsIStyleRuleSupplier_h */ diff --git a/mozilla/content/base/public/nsIStyleSheet.h b/mozilla/content/base/public/nsIStyleSheet.h index 6752ca94e84..193b565428e 100644 --- a/mozilla/content/base/public/nsIStyleSheet.h +++ b/mozilla/content/base/public/nsIStyleSheet.h @@ -97,17 +97,9 @@ public: NS_IMETHOD GetStyleRuleProcessor(nsIStyleRuleProcessor*& aProcessor, nsIStyleRuleProcessor* aPrevProcessor) = 0; - // XXX style rule enumerations - - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects) = 0; - #ifdef DEBUG virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const = 0; #endif - }; #endif /* nsIStyleSheet_h___ */ diff --git a/mozilla/content/base/public/nsIStyledContent.h b/mozilla/content/base/public/nsIStyledContent.h index 852c99d74b6..9ecae87837a 100644 --- a/mozilla/content/base/public/nsIStyledContent.h +++ b/mozilla/content/base/public/nsIStyledContent.h @@ -56,6 +56,10 @@ class nsIStyledContent : public nsIContent { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISTYLEDCONTENT_IID) + // XXX Currently callers (e.g., CSSStyleSheetImpl) assume that the ID + // corresponds to the attribute nsHTMLAtoms::id and that the Class + // corresponds to the attribute nsHTMLAtoms::kClass. If this becomes + // incorrect, then new methods need to be added here. NS_IMETHOD GetID(nsIAtom*& aResult) const = 0; NS_IMETHOD GetClasses(nsVoidArray& aArray) const = 0; NS_IMETHOD_(PRBool) HasClass(nsIAtom* aClass, PRBool aCaseSensitive) const = 0; diff --git a/mozilla/content/base/src/nsStyleSet.cpp b/mozilla/content/base/src/nsStyleSet.cpp index 8bf3ec2763e..e10ac083e9f 100644 --- a/mozilla/content/base/src/nsStyleSet.cpp +++ b/mozilla/content/base/src/nsStyleSet.cpp @@ -208,6 +208,12 @@ public: PRInt32 aStateMask, PRBool* aResult); + NS_IMETHOD HasAttributeDependentStyle(nsIPresContext* aPresContext, + nsIContent* aContent, + nsIAtom* aAtribute, + PRInt32 aModType, + PRBool* aResult); + NS_IMETHOD ConstructRootFrame(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame*& aFrameSubTree); @@ -296,9 +302,6 @@ public: NS_DECL_NSITIMERECORDER #endif - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); - private: static nsrefcnt gInstances; static nsIURI *gQuirkURI; @@ -1501,20 +1504,20 @@ struct StatefulData : public StateRuleProcessorData { nsIContent* aContent, PRInt32 aStateMask) : StateRuleProcessorData(aPresContext, aContent, aStateMask), mMedium(aMedium), - mStateful(PR_FALSE) + mHasStyle(PR_FALSE) {} nsIAtom* mMedium; - PRBool mStateful; + PRBool mHasStyle; }; static PRBool SheetHasStatefulStyle(nsISupports* aProcessor, void *aData) { nsIStyleRuleProcessor* processor = (nsIStyleRuleProcessor*)aProcessor; StatefulData* data = (StatefulData*)aData; - PRBool hasStateful; - processor->HasStateDependentStyle(data, data->mMedium, &hasStateful); - if (hasStateful) { - data->mStateful = PR_TRUE; + PRBool hasStyle; + processor->HasStateDependentStyle(data, data->mMedium, &hasStyle); + if (hasStyle) { + data->mHasStyle = PR_TRUE; // Stop iteration. Note that StyleSetImpl::WalkRuleProcessors uses // this to stop its own iteration in some cases, but not all (the // style rule supplier case). Since this optimization is only for @@ -1539,12 +1542,67 @@ StyleSetImpl::HasStateDependentStyle(nsIPresContext* aPresContext, mUserRuleProcessors || mDocRuleProcessors || mOverrideRuleProcessors)) { - nsIAtom* medium = nsnull; - aPresContext->GetMedium(&medium); + nsCOMPtr medium; + aPresContext->GetMedium(getter_AddRefs(medium)); StatefulData data(aPresContext, medium, aContent, aStateMask); WalkRuleProcessors(SheetHasStatefulStyle, &data); - NS_IF_RELEASE(medium); - *aResult = data.mStateful; + *aResult = data.mHasStyle; + } else { + *aResult = PR_FALSE; + } + + return NS_OK; +} + +struct AttributeData : public AttributeRuleProcessorData { + AttributeData(nsIPresContext* aPresContext, nsIAtom* aMedium, + nsIContent* aContent, nsIAtom* aAttribute, PRInt32 aModType) + : AttributeRuleProcessorData(aPresContext, aContent, aAttribute, aModType), + mMedium(aMedium), + mHasStyle(PR_FALSE) + {} + nsIAtom* mMedium; + PRBool mHasStyle; +}; + +static PRBool SheetHasAttributeStyle(nsISupports* aProcessor, void *aData) +{ + nsIStyleRuleProcessor* processor = (nsIStyleRuleProcessor*)aProcessor; + AttributeData* data = (AttributeData*)aData; + PRBool hasStyle; + processor->HasAttributeDependentStyle(data, data->mMedium, &hasStyle); + if (hasStyle) { + data->mHasStyle = PR_TRUE; + // Stop iteration. Note that StyleSetImpl::WalkRuleProcessors uses + // this to stop its own iteration in some cases, but not all (the + // style rule supplier case). Since this optimization is only for + // the case where we have a lot more work to do, it's not worth the + // code needed to make the stopping perfect. + return PR_FALSE; + } + return PR_TRUE; // continue +} + +// Test if style is dependent on content state +NS_IMETHODIMP +StyleSetImpl::HasAttributeDependentStyle(nsIPresContext* aPresContext, + nsIContent* aContent, + nsIAtom* aAttribute, + PRInt32 aModType, + PRBool* aResult) +{ + GatherRuleProcessors(); + + if (aContent->IsContentOfType(nsIContent::eELEMENT) && + (mAgentRuleProcessors || + mUserRuleProcessors || + mDocRuleProcessors || + mOverrideRuleProcessors)) { + nsCOMPtr medium; + aPresContext->GetMedium(getter_AddRefs(medium)); + AttributeData data(aPresContext, medium, aContent, aAttribute, aModType); + WalkRuleProcessors(SheetHasAttributeStyle, &data); + *aResult = data.mHasStyle; } else { *aResult = PR_FALSE; } @@ -1912,53 +1970,3 @@ void StyleSetImpl::ResetUniqueStyleItems(void) UNIQUE_STYLE_ITEMS(uniqueItems); uniqueItems->Clear(); } - -struct AttributeContentPair { - nsIAtom *attribute; - nsIContent *content; -}; - -static PRBool -EnumAffectsStyle(nsISupports *aElement, void *aData) -{ - nsIStyleSheet *sheet = NS_STATIC_CAST(nsIStyleSheet *, aElement); - AttributeContentPair *pair = (AttributeContentPair *)aData; - PRBool affects; - nsresult res = - sheet->AttributeAffectsStyle(pair->attribute, pair->content, affects); - if (NS_FAILED(res) || affects) - return PR_FALSE; // stop checking - - return PR_TRUE; -} - -NS_IMETHODIMP -StyleSetImpl::AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects) -{ - AttributeContentPair pair; - pair.attribute = aAttribute; - pair.content = aContent; - - /* scoped sheets should be checked first, since - if present - they will contain - the bulk of the applicable rules for the content node. */ - if (mStyleRuleSupplier) - mStyleRuleSupplier->AttributeAffectsStyle(EnumAffectsStyle, &pair, aContent, &aAffects); - - if (!aAffects) { - /* check until we find a sheet that will be affected */ - if ((mDocSheets && !mDocSheets->EnumerateForwards(EnumAffectsStyle, &pair)) || - (mOverrideSheets && !mOverrideSheets->EnumerateForwards(EnumAffectsStyle, - &pair)) || - (mUserSheets && !mUserSheets->EnumerateForwards(EnumAffectsStyle, - &pair)) || - (mAgentSheets && !mAgentSheets->EnumerateForwards(EnumAffectsStyle, - &pair))) { - aAffects = PR_TRUE; - } else { - aAffects = PR_FALSE; - } - } - - return NS_OK; -} diff --git a/mozilla/content/html/style/src/nsCSSStyleSheet.cpp b/mozilla/content/html/style/src/nsCSSStyleSheet.cpp index 0811681cdb4..501f5608066 100644 --- a/mozilla/content/html/style/src/nsCSSStyleSheet.cpp +++ b/mozilla/content/html/style/src/nsCSSStyleSheet.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:tabstop=2:expandtab:shiftwidth=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -107,87 +108,6 @@ #include "nsIJSContextStack.h" #include "nsIScriptSecurityManager.h" -// An |AtomKey| is to be used for storage in the hashtable, and a -// |DependentAtomKey| should be used on the stack to avoid the performance -// cost of refcounting an atom that one is assured to own for the lifetime -// of the key. - -class AtomKey_base : public nsHashKey { -public: - virtual PRUint32 HashCode(void) const; - virtual PRBool Equals(const nsHashKey *aKey) const; - nsIAtom* mAtom; -}; - -class AtomKey : public AtomKey_base { -public: - AtomKey(nsIAtom* aAtom); - AtomKey(const AtomKey_base& aKey); - virtual ~AtomKey(); - virtual nsHashKey *Clone(void) const; -}; - - -class DependentAtomKey : public AtomKey_base { -public: - DependentAtomKey(nsIAtom* aAtom) - { - mAtom = aAtom; - } - DependentAtomKey(const DependentAtomKey& aKey); - virtual ~DependentAtomKey(void); - virtual nsHashKey *Clone(void) const; -}; - -PRUint32 AtomKey_base::HashCode(void) const -{ - return NS_PTR_TO_INT32(mAtom); -} - -PRBool AtomKey_base::Equals(const nsHashKey* aKey) const -{ - return NS_STATIC_CAST(const AtomKey_base*, aKey)->mAtom == mAtom; -} - - -AtomKey::AtomKey(nsIAtom* aAtom) -{ - mAtom = aAtom; - NS_ADDREF(mAtom); -} - -AtomKey::AtomKey(const AtomKey_base& aKey) -{ - mAtom = aKey.mAtom; - NS_ADDREF(mAtom); -} - -AtomKey::~AtomKey() -{ - NS_RELEASE(mAtom); -} - -nsHashKey* AtomKey::Clone(void) const -{ - return new AtomKey(*this); -} - - -DependentAtomKey::DependentAtomKey(const DependentAtomKey& aKey) -{ - NS_NOTREACHED("Should never clone to a dependent atom key."); - mAtom = aKey.mAtom; -} - -DependentAtomKey::~DependentAtomKey() -{ -} - -nsHashKey* DependentAtomKey::Clone(void) const -{ - return new AtomKey(*this); // return a non-dependent key -} - struct RuleValue { RuleValue(nsICSSStyleRule* aRule, PRInt32 aIndex, RuleValue *aNext) : mRule(aRule), mIndex(aIndex), mNext(aNext) {} @@ -708,6 +628,35 @@ void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData) } } +//-------------------------------- + +// Attribute selectors hash table. +struct AttributeSelectorEntry : public PLDHashEntryHdr { + nsIAtom *mAttribute; + nsVoidArray *mSelectors; +}; + +PR_STATIC_CALLBACK(void) +AttributeSelectorClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) +{ + AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*, hdr); + delete entry->mSelectors; + memset(entry, 0, table->entrySize); +} + +static PLDHashTableOps AttributeSelectorOps = { + PL_DHashAllocTable, + PL_DHashFreeTable, + PL_DHashGetKeyStub, + PL_DHashVoidPtrKeyStub, + PL_DHashMatchEntryStub, + PL_DHashMoveEntryStub, + AttributeSelectorClearEntry, + PL_DHashFinalizeStub, + NULL +}; + + //-------------------------------- struct RuleCascadeData { @@ -719,20 +668,45 @@ struct RuleCascadeData { mNext(nsnull) { NS_NewISupportsArray(&mWeightedRules); + PL_DHashTableInit(&mAttributeSelectors, &AttributeSelectorOps, nsnull, + sizeof(AttributeSelectorEntry), 16); } ~RuleCascadeData(void) { NS_IF_RELEASE(mWeightedRules); + PL_DHashTableFinish(&mAttributeSelectors); } nsISupportsArray* mWeightedRules; RuleHash mRuleHash; nsVoidArray mStateSelectors; + PLDHashTable mAttributeSelectors; // nsIAtom* -> nsVoidArray* + + // Looks up or creates the appropriate list in |mAttributeSelectors|. + // Returns null only on allocation failure. + nsVoidArray* AttributeListFor(nsIAtom* aAttribute); nsCOMPtr mMedium; RuleCascadeData* mNext; // for a different medium }; +nsVoidArray* +RuleCascadeData::AttributeListFor(nsIAtom* aAttribute) +{ + AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*, + PL_DHashTableOperate(&mAttributeSelectors, aAttribute, PL_DHASH_ADD)); + if (!entry) + return nsnull; + if (!entry->mSelectors) { + if (!(entry->mSelectors = new nsVoidArray)) { + PL_DHashTableRawRemove(&mAttributeSelectors, entry); + return nsnull; + } + entry->mAttribute = aAttribute; + } + return entry->mSelectors; +} + // ------------------------------- // CSS Style Rule processor // @@ -761,6 +735,10 @@ public: nsIAtom* aMedium, PRBool* aResult); + NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult); + protected: RuleCascadeData* GetRuleCascade(nsIPresContext* aPresContext, nsIAtom* aMedium); @@ -795,7 +773,6 @@ public: nsCOMPtr mNameSpace; PRInt32 mDefaultNameSpaceID; - nsHashtable mRelevantAttributes; PRPackedBool mComplete; }; @@ -855,15 +832,6 @@ public: NS_IMETHOD AppendStyleSheet(nsICSSStyleSheet* aSheet); NS_IMETHOD InsertStyleSheetAt(nsICSSStyleSheet* aSheet, PRInt32 aIndex); - - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); - - // Find attributes in selector for rule, for use with AttributeAffectsStyle - NS_IMETHOD CheckRuleForAttributes(nsICSSRule* aRule); - // XXX do these belong here or are they generic? NS_IMETHOD PrependStyleRule(nsICSSRule* aRule); NS_IMETHOD AppendStyleRule(nsICSSRule* aRule); @@ -1473,7 +1441,6 @@ CSSStyleSheetInner::CSSStyleSheetInner(nsICSSStyleSheet* aParentSheet) mOrderedRules(nsnull), mNameSpace(nsnull), mDefaultNameSpaceID(kNameSpaceID_None), - mRelevantAttributes(), mComplete(PR_FALSE) { MOZ_COUNT_CTOR(CSSStyleSheetInner); @@ -1494,24 +1461,12 @@ CloneRuleInto(nsISupports* aRule, void* aArray) return PR_TRUE; } -static PRBool PR_CALLBACK -CopyRelevantAttributes(nsHashKey *aAttrKey, void *aAtom, void *aTable) -{ - nsHashtable *table = NS_STATIC_CAST(nsHashtable *, aTable); - AtomKey *key = NS_STATIC_CAST(AtomKey *, aAttrKey); - table->Put(key, key->mAtom); - // we don't need to addref the atom here, because we're also copying the - // rules when we clone, and that will add a ref for us - return PR_TRUE; -} - CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheetInner& aCopy, nsICSSStyleSheet* aParentSheet) : mSheets(), mURL(aCopy.mURL), mNameSpace(nsnull), mDefaultNameSpaceID(aCopy.mDefaultNameSpaceID), - mRelevantAttributes(), mComplete(aCopy.mComplete) { MOZ_COUNT_CTOR(CSSStyleSheetInner); @@ -1527,8 +1482,6 @@ CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheetInner& aCopy, else { mOrderedRules = nsnull; } - aCopy.mRelevantAttributes.Enumerate(CopyRelevantAttributes, - &mRelevantAttributes); RebuildNameSpaces(); } @@ -2104,21 +2057,6 @@ CSSStyleSheetImpl::InsertStyleSheetAt(nsICSSStyleSheet* aSheet, PRInt32 aIndex) return result; } -NS_IMETHODIMP -CSSStyleSheetImpl::AttributeAffectsStyle(nsIAtom *aAttribute, - nsIContent *aContent, - PRBool &aAffects) -{ - DependentAtomKey key(aAttribute); - aAffects = !!mInner->mRelevantAttributes.Get(&key); - for (CSSStyleSheetImpl *child = mFirstChild; - child && !aAffects; - child = child->mNext) { - child->AttributeAffectsStyle(aAttribute, aContent, aAffects); - } - return NS_OK; -} - NS_IMETHODIMP CSSStyleSheetImpl::PrependStyleRule(nsICSSRule* aRule) { @@ -2138,8 +2076,6 @@ CSSStyleSheetImpl::PrependStyleRule(nsICSSRule* aRule) if (nsICSSRule::NAMESPACE_RULE == type) { // no api to prepend a namespace (ugh), release old ones and re-create them all mInner->RebuildNameSpaces(); - } else { - CheckRuleForAttributes(aRule); } } } @@ -2181,8 +2117,6 @@ CSSStyleSheetImpl::AppendStyleRule(nsICSSRule* aRule) mInner->mNameSpace = newNameSpace; } } - } else { - CheckRuleForAttributes(aRule); } } @@ -2190,68 +2124,6 @@ CSSStyleSheetImpl::AppendStyleRule(nsICSSRule* aRule) return NS_OK; } -static PRBool -CheckRuleForAttributesEnum(nsISupports *aRule, void *aData) -{ - nsICSSRule *rule = NS_STATIC_CAST(nsICSSRule *, aRule); - CSSStyleSheetImpl *sheet = NS_STATIC_CAST(CSSStyleSheetImpl *, aData); - return NS_SUCCEEDED(sheet->CheckRuleForAttributes(rule)); -} - -NS_IMETHODIMP -CSSStyleSheetImpl::CheckRuleForAttributes(nsICSSRule *aRule) -{ - PRInt32 ruleType = nsICSSRule::UNKNOWN_RULE; - aRule->GetType(ruleType); - switch (ruleType) { - case nsICSSRule::MEDIA_RULE: { - nsICSSMediaRule *mediaRule = (nsICSSMediaRule *)aRule; - return mediaRule->EnumerateRulesForwards(CheckRuleForAttributesEnum, - (void *)this); - } - case nsICSSRule::STYLE_RULE: { - nsICSSStyleRule *styleRule = NS_STATIC_CAST(nsICSSStyleRule *, aRule); - nsCSSSelector *iter; - for (iter = styleRule->FirstSelector(); iter; iter = iter->mNext) { - /* P.classname means we have to check the attribute "class" */ - if (iter->mIDList) { - DependentAtomKey idKey(nsHTMLAtoms::id); - mInner->mRelevantAttributes.Put(&idKey, nsHTMLAtoms::id); - } - if (iter->mClassList) { - DependentAtomKey classKey(nsHTMLAtoms::kClass); - mInner->mRelevantAttributes.Put(&classKey, nsHTMLAtoms::kClass); - } - for (nsAttrSelector *sel = iter->mAttrList; sel; sel = sel->mNext) { - /* store it in this sheet's attributes-that-matter table */ - /* XXX store tag name too, but handle collisions */ -#ifdef DEBUG_shaver_off - nsAutoString str; - sel->mAttr->ToString(str); - char * chars = ToNewCString(str); - fprintf(stderr, "[%s@%p]", chars, this); - nsMemory::Free(chars); -#endif - DependentAtomKey key(sel->mAttr); - mInner->mRelevantAttributes.Put(&key, sel->mAttr); - } - - // Search for the :lang() pseudo class. If it is there add - // the lang attribute to the relevant attribute list. - for (nsAtomStringList* p = iter->mPseudoClassList; p; p = p->mNext) { - if (p->mAtom == nsCSSPseudoClasses::lang) { - DependentAtomKey langKey(nsHTMLAtoms::lang); - mInner->mRelevantAttributes.Put(&langKey, nsHTMLAtoms::lang); - break; - } - } - } - } /* fall-through */ - default: - return NS_OK; - } -} - NS_IMETHODIMP CSSStyleSheetImpl::StyleRuleCount(PRInt32& aCount) const { @@ -2843,9 +2715,6 @@ CSSStyleSheetImpl::InsertRule(const nsAString& aRule, mInner->mNameSpace = newNameSpace; } } - else { - CheckRuleForAttributes(cssRule); - } // We don't notify immediately for @import rules, but rather when // the sheet the rule is importing is loaded @@ -3048,7 +2917,6 @@ CSSStyleSheetImpl::InsertRuleIntoGroup(const nsAString & aRule, nsICSSGroupRule* DidDirty(); for (counter = 0; counter < rulecount; counter++) { rule = dont_AddRef((nsICSSRule*)rules->ElementAt(counter)); - CheckRuleForAttributes(rule); if (mDocument) { result = mDocument->StyleRuleAdded(this, rule); @@ -3516,6 +3384,7 @@ DashMatchCompare(const nsAString& aAttributeValue, static PRBool SelectorMatches(RuleProcessorData &data, nsCSSSelector* aSelector, PRInt32 aStateMask, // states NOT to test + nsIAtom* aAttribute, // attribute NOT to test PRInt8 aNegationIndex) { @@ -3746,15 +3615,20 @@ static PRBool SelectorMatches(RuleProcessorData &data, } // namespace/tag match - if (result && aSelector->mAttrList) { // test for attribute match - // if no attributes on the content, no match - if(!data.mHasAttributes) { + + if (result && aSelector->mAttrList) { + // test for attribute match + if (!data.mHasAttributes && !aAttribute) { + // if no attributes on the content, no match result = localFalse; } else { result = localTrue; nsAttrSelector* attr = aSelector->mAttrList; do { - if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) { + if (attr->mAttr == aAttribute) { + result = PR_TRUE; + } + else if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) { result = localFalse; } else if (attr->mFunction != NS_ATTR_FUNC_SET) { @@ -3826,7 +3700,7 @@ static PRBool SelectorMatches(RuleProcessorData &data, // case sensitivity: bug 93371 PRBool isCaseSensitive = data.mCompatMode != eCompatibility_NavQuirks; nsAtomList* IDList = aSelector->mIDList; - if (nsnull == IDList) { + if (nsnull == IDList || aAttribute == nsHTMLAtoms::id) { result = PR_TRUE; } else if (nsnull != data.mContentID) { @@ -3857,7 +3731,7 @@ static PRBool SelectorMatches(RuleProcessorData &data, } } - if (result) { + if (result && aAttribute != nsHTMLAtoms::kClass) { nsAtomList* classList = aSelector->mClassList; while (nsnull != classList) { if (localTrue == (!data.mStyledContent->HasClass(classList->mAtom, isCaseSensitive))) { @@ -3873,7 +3747,7 @@ static PRBool SelectorMatches(RuleProcessorData &data, // apply SelectorMatches to the negated selectors in the chain if (result && (nsnull != aSelector->mNegations)) { result = SelectorMatches(data, aSelector->mNegations, aStateMask, - aNegationIndex+1); + aAttribute, aNegationIndex+1); } return result; } @@ -3948,7 +3822,7 @@ static PRBool SelectorMatchesTree(RuleProcessorData &data, NS_ASSERTION(!content, "content must be null"); break; } - if (SelectorMatches(*newdata, selector, 0, 0)) { + if (SelectorMatches(*newdata, selector, 0, nsnull, 0)) { // to avoid greedy matching, we need to recurse if this is a // descendant combinator and the next combinator is not if ((NS_IS_GREEDY_OPERATOR(selector->mOperator)) && @@ -3992,7 +3866,7 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData) ElementRuleProcessorData* data = (ElementRuleProcessorData*)aData; nsCSSSelector* selector = aRule->FirstSelector(); - if (SelectorMatches(*data, selector, 0, 0)) { + if (SelectorMatches(*data, selector, 0, nsnull, 0)) { selector = selector->mNext; if (SelectorMatchesTree(*data, selector)) { // for performance, require that every implementation of @@ -4048,7 +3922,7 @@ static void PseudoEnumFunc(nsICSSStyleRule* aRule, void* aData) if (PRUnichar('+') == selector->mOperator) { return; // not valid here, can't match } - if (SelectorMatches(*data, selector, 0, 0)) { + if (SelectorMatches(*data, selector, 0, nsnull, 0)) { selector = selector->mNext; } else { @@ -4093,19 +3967,17 @@ CSSRuleProcessor::RulesMatching(PseudoRuleProcessorData* aData, return NS_OK; } -static -PRBool PR_CALLBACK StateEnumFunc(void* aSelector, void* aData) -{ - StateRuleProcessorData* data = (StateRuleProcessorData*)aData; - nsCSSSelector* selector = (nsCSSSelector*)aSelector; - if (SelectorMatches(*data, selector, data->mStateMask, 0)) { - selector = selector->mNext; - if (SelectorMatchesTree(*data, selector)) { - return PR_FALSE; - } - } - return PR_TRUE; +PR_STATIC_CALLBACK(PRBool) StateEnumFunc(void* aSelector, void* aData) +{ + StateRuleProcessorData* data = + NS_STATIC_CAST(StateRuleProcessorData*, aData); + nsCSSSelector* selector = NS_STATIC_CAST(nsCSSSelector*, aSelector); + + // Return whether we want to halt the enumeration, which is the + // negation of whether we find a match (ignoring the event state mask). + return !( SelectorMatches(*data, selector, data->mStateMask, nsnull, 0) && + SelectorMatchesTree(*data, selector->mNext) ); } NS_IMETHODIMP @@ -4135,6 +4007,46 @@ CSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData, return NS_OK; } +PR_STATIC_CALLBACK(PRBool) AttributeEnumFunc(void* aSelector, void* aData) +{ + AttributeRuleProcessorData* data = + NS_STATIC_CAST(AttributeRuleProcessorData*, aData); + nsCSSSelector* selector = NS_STATIC_CAST(nsCSSSelector*, aSelector); + + // Return whether we want to halt the enumeration, which is the + // negation of whether we find a match (ignoring the event state mask). + return !( SelectorMatches(*data, selector, 0, data->mAttribute, 0) && + SelectorMatchesTree(*data, selector->mNext) ); +} + +NS_IMETHODIMP +CSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult) +{ + NS_PRECONDITION(aData->mContent->IsContentOfType(nsIContent::eELEMENT), + "content must be element"); + + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext, aMedium); + + // We do the same thing for attributes that we do for state selectors + // (see HasStateDependentStyle), except that instead of one big list + // we have a hashtable with a per-attribute list. + + *aResult = PR_FALSE; + if (cascade) { + AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*, + PL_DHashTableOperate(&cascade->mAttributeSelectors, aData->mAttribute, + PL_DHASH_LOOKUP)); + if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + *aResult = + !entry->mSelectors->EnumerateForwards(AttributeEnumFunc, aData); + } + } + + return NS_OK; +} + NS_IMETHODIMP CSSRuleProcessor::ClearRuleCascades(void) { @@ -4170,18 +4082,41 @@ PRBool IsStateSelector(nsCSSSelector& aSelector) } static PRBool -BuildRuleHashAndStateSelectors(nsISupports* aRule, void* aCascade) +AddRule(nsISupports* aRule, void* aCascade) { nsICSSStyleRule* rule = NS_STATIC_CAST(nsICSSStyleRule*, aRule); RuleCascadeData *cascade = NS_STATIC_CAST(RuleCascadeData*, aCascade); + // Build the rule hash. cascade->mRuleHash.PrependRule(rule); - nsVoidArray* array = &cascade->mStateSelectors; + nsVoidArray* stateArray = &cascade->mStateSelectors; for (nsCSSSelector* selector = rule->FirstSelector(); - selector; selector = selector->mNext) + selector; selector = selector->mNext) { + // Build mStateSelectors. if (IsStateSelector(*selector)) + stateArray->AppendElement(selector); + + // Build mAttributeSelectors. + if (selector->mIDList) { + nsVoidArray *array = cascade->AttributeListFor(nsHTMLAtoms::id); + if (!array) + return PR_FALSE; array->AppendElement(selector); + } + if (selector->mClassList) { + nsVoidArray *array = cascade->AttributeListFor(nsHTMLAtoms::kClass); + if (!array) + return PR_FALSE; + array->AppendElement(selector); + } + for (nsAttrSelector *attr = selector->mAttrList; attr; attr = attr->mNext) { + nsVoidArray *array = cascade->AttributeListFor(attr->mAttr); + if (!array) + return PR_FALSE; + array->AppendElement(selector); + } + } return PR_TRUE; } @@ -4328,14 +4263,16 @@ CSSRuleProcessor::GetRuleCascade(nsIPresContext* aPresContext, nsIAtom* aMedium) cascade = new RuleCascadeData(aMedium, eCompatibility_NavQuirks == quirkMode); if (cascade) { - *cascadep = cascade; - CascadeEnumData data(aMedium); mSheets->EnumerateForwards(CascadeSheetRulesInto, &data); PutRulesInList(&data.mRuleArrays, cascade->mWeightedRules); - cascade->mWeightedRules->EnumerateBackwards( - BuildRuleHashAndStateSelectors, cascade); + if (!cascade->mWeightedRules->EnumerateBackwards(AddRule, cascade)) { + delete cascade; + cascade = nsnull; + } + + *cascadep = cascade; } } return cascade; diff --git a/mozilla/content/html/style/src/nsHTMLAttributes.cpp b/mozilla/content/html/style/src/nsHTMLAttributes.cpp index 8baad62713e..d00564aa30e 100644 --- a/mozilla/content/html/style/src/nsHTMLAttributes.cpp +++ b/mozilla/content/html/style/src/nsHTMLAttributes.cpp @@ -384,36 +384,9 @@ 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_IMPL_ISUPPORTS2(nsHTMLMappedAttributes, + nsIHTMLMappedAttributes, + nsIStyleRule) NS_IMETHODIMP nsHTMLMappedAttributes::Init(nsIHTMLStyleSheet* aSheet, diff --git a/mozilla/content/html/style/src/nsHTMLCSSStyleSheet.cpp b/mozilla/content/html/style/src/nsHTMLCSSStyleSheet.cpp index b3b72e1fdc2..f0c76de27ce 100644 --- a/mozilla/content/html/style/src/nsHTMLCSSStyleSheet.cpp +++ b/mozilla/content/html/style/src/nsHTMLCSSStyleSheet.cpp @@ -181,16 +181,14 @@ public: nsIAtom* aMedium, PRBool* aResult); - // XXX style rule enumerations + NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult); #ifdef DEBUG virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const; #endif - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); private: // These are not supported and are not implemented! HTMLCSSStyleSheetImpl(const HTMLCSSStyleSheetImpl& aCopy); @@ -321,6 +319,16 @@ HTMLCSSStyleSheetImpl::HasStateDependentStyle(StateRuleProcessorData* aData, return NS_OK; } +// Test if style is dependent on attribute +NS_IMETHODIMP +HTMLCSSStyleSheetImpl::HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult) +{ + *aResult = PR_FALSE; + return NS_OK; +} + NS_IMETHODIMP @@ -447,16 +455,6 @@ void HTMLCSSStyleSheetImpl::List(FILE* out, PRInt32 aIndent) const } #endif -NS_IMETHODIMP -HTMLCSSStyleSheetImpl::AttributeAffectsStyle(nsIAtom *aAttribute, - nsIContent *aContent, - PRBool &aAffects) -{ - // XXX can attributes affect rules in these? - aAffects = PR_FALSE; - return NS_OK; -} - // XXX For backwards compatibility and convenience NS_EXPORT nsresult NS_NewHTMLCSSStyleSheet(nsIHTMLCSSStyleSheet** aInstancePtrResult, nsIURI* aURL, diff --git a/mozilla/content/html/style/src/nsHTMLStyleSheet.cpp b/mozilla/content/html/style/src/nsHTMLStyleSheet.cpp index b6975adbbd4..6eb3c335af5 100644 --- a/mozilla/content/html/style/src/nsHTMLStyleSheet.cpp +++ b/mozilla/content/html/style/src/nsHTMLStyleSheet.cpp @@ -646,6 +646,10 @@ public: nsIAtom* aMedium, PRBool* aResult); + NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult); + // nsIHTMLStyleSheet api NS_IMETHOD Init(nsIURI* aURL, nsIDocument* aDocument); NS_IMETHOD Reset(nsIURI* aURL); @@ -665,10 +669,6 @@ public: virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const; #endif - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); private: // These are not supported and are not implemented! HTMLStyleSheetImpl(const HTMLStyleSheetImpl& aCopy); @@ -919,6 +919,43 @@ HTMLStyleSheetImpl::HasStateDependentStyle(StateRuleProcessorData* aData, return NS_OK; } +NS_IMETHODIMP +HTMLStyleSheetImpl::HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult) +{ + // Result is true for |href| changes on HTML links if we have link rules. + nsIStyledContent *styledContent = aData->mStyledContent; + if (aData->mAttribute == nsHTMLAtoms::href && + (mLinkRule || mVisitedRule || mActiveRule) && + styledContent && + styledContent->IsContentOfType(nsIContent::eHTML) && + aData->mContentTag == nsHTMLAtoms::a) { + *aResult = PR_TRUE; + return NS_OK; + } + + // Don't worry about the HTMLDocumentColorRule since it only applies + // to descendants of body, when we're already reresolving. + + // Handle the content style rules. + if (styledContent) { + nsChangeHint hint = NS_STYLE_HINT_NONE; + styledContent->GetMappedAttributeImpact(aData->mAttribute, + aData->mModType, hint); + // This is the same test that nsGenericHTMLElement uses when calling + // nsHTMLAttributes::SetAttributeFor. + if ((hint & ~(nsChangeHint_AttrChange | nsChangeHint_Aural | + nsChangeHint_Content)) != 0) { + *aResult = PR_TRUE; + return NS_OK; + } + } + + *aResult = PR_FALSE; + return NS_OK; +} + NS_IMETHODIMP HTMLStyleSheetImpl::RulesMatching(PseudoRuleProcessorData* aData, @@ -1223,17 +1260,6 @@ void HTMLStyleSheetImpl::List(FILE* out, PRInt32 aIndent) const } #endif -NS_IMETHODIMP -HTMLStyleSheetImpl::AttributeAffectsStyle(nsIAtom *aAttribute, - nsIContent *aContent, - PRBool &aAffects) -{ - // XXX we should be checking to see if this is an href on an being - // XXX tweaked, in which case we really want to restyle - aAffects = PR_FALSE; - return NS_OK; -} - // XXX For convenience and backwards compatibility NS_EXPORT nsresult NS_NewHTMLStyleSheet(nsIHTMLStyleSheet** aInstancePtrResult, nsIURI* aURL, diff --git a/mozilla/content/xbl/public/nsIXBLBinding.h b/mozilla/content/xbl/public/nsIXBLBinding.h index 611e6b6c7ba..57d6fea9cf6 100644 --- a/mozilla/content/xbl/public/nsIXBLBinding.h +++ b/mozilla/content/xbl/public/nsIXBLBinding.h @@ -118,7 +118,6 @@ public: NS_IMETHOD InheritsStyle(PRBool* aResult)=0; NS_IMETHOD WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData)=0; - NS_IMETHOD AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, void* aData, PRBool* aAffects)=0; NS_IMETHOD MarkForDeath()=0; NS_IMETHOD MarkedForDeath(PRBool* aResult)=0; diff --git a/mozilla/content/xbl/src/nsBindingManager.cpp b/mozilla/content/xbl/src/nsBindingManager.cpp index 4132f66301d..5ebcc9ca39f 100644 --- a/mozilla/content/xbl/src/nsBindingManager.cpp +++ b/mozilla/content/xbl/src/nsBindingManager.cpp @@ -423,9 +423,6 @@ public: NS_IMETHOD WalkRules(nsIStyleSet* aStyleSet, nsISupportsArrayEnumFunc aFunc, RuleProcessorData* aData); - NS_IMETHOD AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, - void* aData, nsIContent* aContent, - PRBool* aAffects); // nsIDocumentObserver NS_DECL_NSIDOCUMENTOBSERVER @@ -437,9 +434,6 @@ protected: void WalkRules(nsISupportsArrayEnumFunc aFunc, RuleProcessorData* aData, nsIContent* aParent, nsIContent* aCurrContent); - void AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, void* aData, - nsIContent* aParent, nsIContent* aCurrContent, PRBool* aAffects); - nsresult GetNestedInsertionPoint(nsIContent* aParent, nsIContent* aChild, nsIContent** aResult); // MEMBER VARIABLES @@ -1387,47 +1381,6 @@ nsBindingManager::WalkRules(nsIStyleSet* aStyleSet, return NS_OK; } -void -nsBindingManager::AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, void* aData, - nsIContent* aParent, nsIContent* aCurrContent, PRBool* aAffects) -{ - nsCOMPtr binding; - GetBinding(aCurrContent, getter_AddRefs(binding)); - if (binding) { - binding->AttributeAffectsStyle(aFunc, aData, aAffects); - } - - if (*aAffects) - return; - - if (aParent != aCurrContent) { - nsCOMPtr par; - GetEnclosingScope(aCurrContent, getter_AddRefs(par)); - if (par) - AttributeAffectsStyle(aFunc, aData, aParent, par, aAffects); - } -} - - -NS_IMETHODIMP -nsBindingManager::AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, void* aData, - nsIContent* aContent, PRBool* aAffects) -{ - *aAffects = PR_FALSE; - if (!aContent) - return NS_OK; - - nsCOMPtr parent; - GetOutermostStyleScope(aContent, getter_AddRefs(parent)); - - AttributeAffectsStyle(aFunc, aData, parent, aContent, aAffects); - - if (*aAffects) - return NS_OK; - - return NS_OK; -} - NS_IMETHODIMP nsBindingManager::ShouldBuildChildFrames(nsIContent* aContent, PRBool* aResult) { diff --git a/mozilla/content/xbl/src/nsXBLBinding.cpp b/mozilla/content/xbl/src/nsXBLBinding.cpp index b65dab6f322..1088b965ad7 100644 --- a/mozilla/content/xbl/src/nsXBLBinding.cpp +++ b/mozilla/content/xbl/src/nsXBLBinding.cpp @@ -1207,31 +1207,6 @@ nsXBLBinding::WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData) return rv; } -NS_IMETHODIMP -nsXBLBinding::AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, void* aData, PRBool* aAffects) -{ - nsresult rv = NS_OK; - if (mNextBinding) { - rv = mNextBinding->AttributeAffectsStyle(aFunc, aData, aAffects); - if (NS_FAILED(rv)) - return rv; - - if (*aAffects) - return NS_OK; - } - - nsCOMPtr sheets; - mPrototypeBinding->GetStyleSheets(getter_AddRefs(sheets)); - if (sheets) { - if (!sheets->EnumerateForwards(aFunc, aData)) - *aAffects = PR_TRUE; - else - *aAffects = PR_FALSE; - } - - return rv; -} - // Internal helper methods //////////////////////////////////////////////////////////////// // static diff --git a/mozilla/content/xbl/src/nsXBLBinding.h b/mozilla/content/xbl/src/nsXBLBinding.h index 408a2e05463..02cd271998f 100644 --- a/mozilla/content/xbl/src/nsXBLBinding.h +++ b/mozilla/content/xbl/src/nsXBLBinding.h @@ -112,7 +112,6 @@ class nsXBLBinding: public nsIXBLBinding NS_IMETHOD InheritsStyle(PRBool* aResult); NS_IMETHOD WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData); - NS_IMETHOD AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, void* aData, PRBool* aAffects); NS_IMETHOD MarkForDeath(); NS_IMETHOD MarkedForDeath(PRBool* aResult); diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index 4ee4f8367da..6d339defae6 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -10731,7 +10731,8 @@ nsCSSFrameConstructor::AttributeChanged(nsIPresContext* aPresContext, shell->GetFrameManager(getter_AddRefs(frameManager)); PRBool affects; - frameManager->AttributeAffectsStyle(aAttribute, aContent, affects); + frameManager->HasAttributeDependentStyle(aPresContext, aContent, + aAttribute, aModType, &affects); if (affects) { #ifdef DEBUG_shaver fputc('+', stderr); diff --git a/mozilla/layout/base/nsFrameManager.cpp b/mozilla/layout/base/nsFrameManager.cpp index 10a01caf312..fe906475414 100644 --- a/mozilla/layout/base/nsFrameManager.cpp +++ b/mozilla/layout/base/nsFrameManager.cpp @@ -346,8 +346,11 @@ public: nsStyleChangeList& aChangeList, nsChangeHint aMinChange, nsChangeHint& aTopLevelChange); - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); + NS_IMETHOD HasAttributeDependentStyle(nsIPresContext *aPresContext, + nsIContent *aContent, + nsIAtom *aAttribute, + PRInt32 aModType, + PRBool *aResult); // Capture state from the entire frame heirarchy and store in aState NS_IMETHOD CaptureFrameState(nsIPresContext* aPresContext, @@ -2066,31 +2069,25 @@ FrameManager::ComputeStyleChangeFor(nsIPresContext* aPresContext, NS_IMETHODIMP -FrameManager::AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects) +FrameManager::HasAttributeDependentStyle(nsIPresContext *aPresContext, + nsIContent *aContent, + nsIAtom *aAttribute, + PRInt32 aModType, + PRBool *aResult) { - nsresult rv = NS_OK; NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE); if (aAttribute == nsHTMLAtoms::style) { // Perhaps should check that it's XUL, SVG, (or HTML) namespace, but - // it doesn't really matter. - aAffects = PR_TRUE; + // it doesn't really matter. Or we could even let + // HTMLCSSStyleSheetImpl::HasAttributeDependentStyle handle it. + *aResult = PR_TRUE; return NS_OK; } - nsCOMPtr xml(do_QueryInterface(aContent)); - if (xml) { - rv = mStyleSet->AttributeAffectsStyle(aAttribute, aContent, aAffects); - } else { - // not an XML element, so assume it is an HTML element and further assume that - // any attribute may affect style - // NOTE: we could list all of the presentation-hint attributes and check them, - // but there are a lot of them and this is safer. - aAffects = PR_TRUE; - rv = NS_OK; - } - return rv; + return mStyleSet->HasAttributeDependentStyle(aPresContext, aContent, + aAttribute, aModType, + aResult); } // Capture state for a given frame. diff --git a/mozilla/layout/base/public/nsIFrameManager.h b/mozilla/layout/base/public/nsIFrameManager.h index 81efb4699b8..dacee94689a 100644 --- a/mozilla/layout/base/public/nsIFrameManager.h +++ b/mozilla/layout/base/public/nsIFrameManager.h @@ -162,8 +162,11 @@ public: nsChangeHint& aTopLevelChange) = 0; // Determine whether an attribute affects style - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects) = 0; + NS_IMETHOD HasAttributeDependentStyle(nsIPresContext* aPresContext, + nsIContent *aContent, + nsIAtom *aAttribute, + PRInt32 aModType, + PRBool *aResult) = 0; /** * Capture/restore frame state for the frame subtree rooted at aFrame. diff --git a/mozilla/layout/base/public/nsIStyleSet.h b/mozilla/layout/base/public/nsIStyleSet.h index 7d3b210e656..ab3ba5ae6ca 100644 --- a/mozilla/layout/base/public/nsIStyleSet.h +++ b/mozilla/layout/base/public/nsIStyleSet.h @@ -203,6 +203,13 @@ public: PRInt32 aStateMask, PRBool* aResult) = 0; + // Test if style is dependent on the presence of an attribute. + NS_IMETHOD HasAttributeDependentStyle(nsIPresContext* aPresContext, + nsIContent* aContent, + nsIAtom* aAttribute, + PRInt32 aModType, + PRBool* aResult) = 0; + // Create frames for the root content element and its child content NS_IMETHOD ConstructRootFrame(nsIPresContext* aPresContext, nsIContent* aDocElement, @@ -316,11 +323,6 @@ public: #endif virtual void ResetUniqueStyleItems(void) = 0; - - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects) = 0; }; extern NS_EXPORT nsresult diff --git a/mozilla/layout/html/base/src/nsFrameManager.cpp b/mozilla/layout/html/base/src/nsFrameManager.cpp index 10a01caf312..fe906475414 100644 --- a/mozilla/layout/html/base/src/nsFrameManager.cpp +++ b/mozilla/layout/html/base/src/nsFrameManager.cpp @@ -346,8 +346,11 @@ public: nsStyleChangeList& aChangeList, nsChangeHint aMinChange, nsChangeHint& aTopLevelChange); - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); + NS_IMETHOD HasAttributeDependentStyle(nsIPresContext *aPresContext, + nsIContent *aContent, + nsIAtom *aAttribute, + PRInt32 aModType, + PRBool *aResult); // Capture state from the entire frame heirarchy and store in aState NS_IMETHOD CaptureFrameState(nsIPresContext* aPresContext, @@ -2066,31 +2069,25 @@ FrameManager::ComputeStyleChangeFor(nsIPresContext* aPresContext, NS_IMETHODIMP -FrameManager::AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects) +FrameManager::HasAttributeDependentStyle(nsIPresContext *aPresContext, + nsIContent *aContent, + nsIAtom *aAttribute, + PRInt32 aModType, + PRBool *aResult) { - nsresult rv = NS_OK; NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE); if (aAttribute == nsHTMLAtoms::style) { // Perhaps should check that it's XUL, SVG, (or HTML) namespace, but - // it doesn't really matter. - aAffects = PR_TRUE; + // it doesn't really matter. Or we could even let + // HTMLCSSStyleSheetImpl::HasAttributeDependentStyle handle it. + *aResult = PR_TRUE; return NS_OK; } - nsCOMPtr xml(do_QueryInterface(aContent)); - if (xml) { - rv = mStyleSet->AttributeAffectsStyle(aAttribute, aContent, aAffects); - } else { - // not an XML element, so assume it is an HTML element and further assume that - // any attribute may affect style - // NOTE: we could list all of the presentation-hint attributes and check them, - // but there are a lot of them and this is safer. - aAffects = PR_TRUE; - rv = NS_OK; - } - return rv; + return mStyleSet->HasAttributeDependentStyle(aPresContext, aContent, + aAttribute, aModType, + aResult); } // Capture state for a given frame. diff --git a/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp b/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp index 4ee4f8367da..6d339defae6 100644 --- a/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp @@ -10731,7 +10731,8 @@ nsCSSFrameConstructor::AttributeChanged(nsIPresContext* aPresContext, shell->GetFrameManager(getter_AddRefs(frameManager)); PRBool affects; - frameManager->AttributeAffectsStyle(aAttribute, aContent, affects); + frameManager->HasAttributeDependentStyle(aPresContext, aContent, + aAttribute, aModType, &affects); if (affects) { #ifdef DEBUG_shaver fputc('+', stderr); diff --git a/mozilla/layout/style/nsCSSStyleSheet.cpp b/mozilla/layout/style/nsCSSStyleSheet.cpp index 0811681cdb4..501f5608066 100644 --- a/mozilla/layout/style/nsCSSStyleSheet.cpp +++ b/mozilla/layout/style/nsCSSStyleSheet.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:tabstop=2:expandtab:shiftwidth=2: /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * @@ -107,87 +108,6 @@ #include "nsIJSContextStack.h" #include "nsIScriptSecurityManager.h" -// An |AtomKey| is to be used for storage in the hashtable, and a -// |DependentAtomKey| should be used on the stack to avoid the performance -// cost of refcounting an atom that one is assured to own for the lifetime -// of the key. - -class AtomKey_base : public nsHashKey { -public: - virtual PRUint32 HashCode(void) const; - virtual PRBool Equals(const nsHashKey *aKey) const; - nsIAtom* mAtom; -}; - -class AtomKey : public AtomKey_base { -public: - AtomKey(nsIAtom* aAtom); - AtomKey(const AtomKey_base& aKey); - virtual ~AtomKey(); - virtual nsHashKey *Clone(void) const; -}; - - -class DependentAtomKey : public AtomKey_base { -public: - DependentAtomKey(nsIAtom* aAtom) - { - mAtom = aAtom; - } - DependentAtomKey(const DependentAtomKey& aKey); - virtual ~DependentAtomKey(void); - virtual nsHashKey *Clone(void) const; -}; - -PRUint32 AtomKey_base::HashCode(void) const -{ - return NS_PTR_TO_INT32(mAtom); -} - -PRBool AtomKey_base::Equals(const nsHashKey* aKey) const -{ - return NS_STATIC_CAST(const AtomKey_base*, aKey)->mAtom == mAtom; -} - - -AtomKey::AtomKey(nsIAtom* aAtom) -{ - mAtom = aAtom; - NS_ADDREF(mAtom); -} - -AtomKey::AtomKey(const AtomKey_base& aKey) -{ - mAtom = aKey.mAtom; - NS_ADDREF(mAtom); -} - -AtomKey::~AtomKey() -{ - NS_RELEASE(mAtom); -} - -nsHashKey* AtomKey::Clone(void) const -{ - return new AtomKey(*this); -} - - -DependentAtomKey::DependentAtomKey(const DependentAtomKey& aKey) -{ - NS_NOTREACHED("Should never clone to a dependent atom key."); - mAtom = aKey.mAtom; -} - -DependentAtomKey::~DependentAtomKey() -{ -} - -nsHashKey* DependentAtomKey::Clone(void) const -{ - return new AtomKey(*this); // return a non-dependent key -} - struct RuleValue { RuleValue(nsICSSStyleRule* aRule, PRInt32 aIndex, RuleValue *aNext) : mRule(aRule), mIndex(aIndex), mNext(aNext) {} @@ -708,6 +628,35 @@ void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData) } } +//-------------------------------- + +// Attribute selectors hash table. +struct AttributeSelectorEntry : public PLDHashEntryHdr { + nsIAtom *mAttribute; + nsVoidArray *mSelectors; +}; + +PR_STATIC_CALLBACK(void) +AttributeSelectorClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) +{ + AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*, hdr); + delete entry->mSelectors; + memset(entry, 0, table->entrySize); +} + +static PLDHashTableOps AttributeSelectorOps = { + PL_DHashAllocTable, + PL_DHashFreeTable, + PL_DHashGetKeyStub, + PL_DHashVoidPtrKeyStub, + PL_DHashMatchEntryStub, + PL_DHashMoveEntryStub, + AttributeSelectorClearEntry, + PL_DHashFinalizeStub, + NULL +}; + + //-------------------------------- struct RuleCascadeData { @@ -719,20 +668,45 @@ struct RuleCascadeData { mNext(nsnull) { NS_NewISupportsArray(&mWeightedRules); + PL_DHashTableInit(&mAttributeSelectors, &AttributeSelectorOps, nsnull, + sizeof(AttributeSelectorEntry), 16); } ~RuleCascadeData(void) { NS_IF_RELEASE(mWeightedRules); + PL_DHashTableFinish(&mAttributeSelectors); } nsISupportsArray* mWeightedRules; RuleHash mRuleHash; nsVoidArray mStateSelectors; + PLDHashTable mAttributeSelectors; // nsIAtom* -> nsVoidArray* + + // Looks up or creates the appropriate list in |mAttributeSelectors|. + // Returns null only on allocation failure. + nsVoidArray* AttributeListFor(nsIAtom* aAttribute); nsCOMPtr mMedium; RuleCascadeData* mNext; // for a different medium }; +nsVoidArray* +RuleCascadeData::AttributeListFor(nsIAtom* aAttribute) +{ + AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*, + PL_DHashTableOperate(&mAttributeSelectors, aAttribute, PL_DHASH_ADD)); + if (!entry) + return nsnull; + if (!entry->mSelectors) { + if (!(entry->mSelectors = new nsVoidArray)) { + PL_DHashTableRawRemove(&mAttributeSelectors, entry); + return nsnull; + } + entry->mAttribute = aAttribute; + } + return entry->mSelectors; +} + // ------------------------------- // CSS Style Rule processor // @@ -761,6 +735,10 @@ public: nsIAtom* aMedium, PRBool* aResult); + NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult); + protected: RuleCascadeData* GetRuleCascade(nsIPresContext* aPresContext, nsIAtom* aMedium); @@ -795,7 +773,6 @@ public: nsCOMPtr mNameSpace; PRInt32 mDefaultNameSpaceID; - nsHashtable mRelevantAttributes; PRPackedBool mComplete; }; @@ -855,15 +832,6 @@ public: NS_IMETHOD AppendStyleSheet(nsICSSStyleSheet* aSheet); NS_IMETHOD InsertStyleSheetAt(nsICSSStyleSheet* aSheet, PRInt32 aIndex); - - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); - - // Find attributes in selector for rule, for use with AttributeAffectsStyle - NS_IMETHOD CheckRuleForAttributes(nsICSSRule* aRule); - // XXX do these belong here or are they generic? NS_IMETHOD PrependStyleRule(nsICSSRule* aRule); NS_IMETHOD AppendStyleRule(nsICSSRule* aRule); @@ -1473,7 +1441,6 @@ CSSStyleSheetInner::CSSStyleSheetInner(nsICSSStyleSheet* aParentSheet) mOrderedRules(nsnull), mNameSpace(nsnull), mDefaultNameSpaceID(kNameSpaceID_None), - mRelevantAttributes(), mComplete(PR_FALSE) { MOZ_COUNT_CTOR(CSSStyleSheetInner); @@ -1494,24 +1461,12 @@ CloneRuleInto(nsISupports* aRule, void* aArray) return PR_TRUE; } -static PRBool PR_CALLBACK -CopyRelevantAttributes(nsHashKey *aAttrKey, void *aAtom, void *aTable) -{ - nsHashtable *table = NS_STATIC_CAST(nsHashtable *, aTable); - AtomKey *key = NS_STATIC_CAST(AtomKey *, aAttrKey); - table->Put(key, key->mAtom); - // we don't need to addref the atom here, because we're also copying the - // rules when we clone, and that will add a ref for us - return PR_TRUE; -} - CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheetInner& aCopy, nsICSSStyleSheet* aParentSheet) : mSheets(), mURL(aCopy.mURL), mNameSpace(nsnull), mDefaultNameSpaceID(aCopy.mDefaultNameSpaceID), - mRelevantAttributes(), mComplete(aCopy.mComplete) { MOZ_COUNT_CTOR(CSSStyleSheetInner); @@ -1527,8 +1482,6 @@ CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheetInner& aCopy, else { mOrderedRules = nsnull; } - aCopy.mRelevantAttributes.Enumerate(CopyRelevantAttributes, - &mRelevantAttributes); RebuildNameSpaces(); } @@ -2104,21 +2057,6 @@ CSSStyleSheetImpl::InsertStyleSheetAt(nsICSSStyleSheet* aSheet, PRInt32 aIndex) return result; } -NS_IMETHODIMP -CSSStyleSheetImpl::AttributeAffectsStyle(nsIAtom *aAttribute, - nsIContent *aContent, - PRBool &aAffects) -{ - DependentAtomKey key(aAttribute); - aAffects = !!mInner->mRelevantAttributes.Get(&key); - for (CSSStyleSheetImpl *child = mFirstChild; - child && !aAffects; - child = child->mNext) { - child->AttributeAffectsStyle(aAttribute, aContent, aAffects); - } - return NS_OK; -} - NS_IMETHODIMP CSSStyleSheetImpl::PrependStyleRule(nsICSSRule* aRule) { @@ -2138,8 +2076,6 @@ CSSStyleSheetImpl::PrependStyleRule(nsICSSRule* aRule) if (nsICSSRule::NAMESPACE_RULE == type) { // no api to prepend a namespace (ugh), release old ones and re-create them all mInner->RebuildNameSpaces(); - } else { - CheckRuleForAttributes(aRule); } } } @@ -2181,8 +2117,6 @@ CSSStyleSheetImpl::AppendStyleRule(nsICSSRule* aRule) mInner->mNameSpace = newNameSpace; } } - } else { - CheckRuleForAttributes(aRule); } } @@ -2190,68 +2124,6 @@ CSSStyleSheetImpl::AppendStyleRule(nsICSSRule* aRule) return NS_OK; } -static PRBool -CheckRuleForAttributesEnum(nsISupports *aRule, void *aData) -{ - nsICSSRule *rule = NS_STATIC_CAST(nsICSSRule *, aRule); - CSSStyleSheetImpl *sheet = NS_STATIC_CAST(CSSStyleSheetImpl *, aData); - return NS_SUCCEEDED(sheet->CheckRuleForAttributes(rule)); -} - -NS_IMETHODIMP -CSSStyleSheetImpl::CheckRuleForAttributes(nsICSSRule *aRule) -{ - PRInt32 ruleType = nsICSSRule::UNKNOWN_RULE; - aRule->GetType(ruleType); - switch (ruleType) { - case nsICSSRule::MEDIA_RULE: { - nsICSSMediaRule *mediaRule = (nsICSSMediaRule *)aRule; - return mediaRule->EnumerateRulesForwards(CheckRuleForAttributesEnum, - (void *)this); - } - case nsICSSRule::STYLE_RULE: { - nsICSSStyleRule *styleRule = NS_STATIC_CAST(nsICSSStyleRule *, aRule); - nsCSSSelector *iter; - for (iter = styleRule->FirstSelector(); iter; iter = iter->mNext) { - /* P.classname means we have to check the attribute "class" */ - if (iter->mIDList) { - DependentAtomKey idKey(nsHTMLAtoms::id); - mInner->mRelevantAttributes.Put(&idKey, nsHTMLAtoms::id); - } - if (iter->mClassList) { - DependentAtomKey classKey(nsHTMLAtoms::kClass); - mInner->mRelevantAttributes.Put(&classKey, nsHTMLAtoms::kClass); - } - for (nsAttrSelector *sel = iter->mAttrList; sel; sel = sel->mNext) { - /* store it in this sheet's attributes-that-matter table */ - /* XXX store tag name too, but handle collisions */ -#ifdef DEBUG_shaver_off - nsAutoString str; - sel->mAttr->ToString(str); - char * chars = ToNewCString(str); - fprintf(stderr, "[%s@%p]", chars, this); - nsMemory::Free(chars); -#endif - DependentAtomKey key(sel->mAttr); - mInner->mRelevantAttributes.Put(&key, sel->mAttr); - } - - // Search for the :lang() pseudo class. If it is there add - // the lang attribute to the relevant attribute list. - for (nsAtomStringList* p = iter->mPseudoClassList; p; p = p->mNext) { - if (p->mAtom == nsCSSPseudoClasses::lang) { - DependentAtomKey langKey(nsHTMLAtoms::lang); - mInner->mRelevantAttributes.Put(&langKey, nsHTMLAtoms::lang); - break; - } - } - } - } /* fall-through */ - default: - return NS_OK; - } -} - NS_IMETHODIMP CSSStyleSheetImpl::StyleRuleCount(PRInt32& aCount) const { @@ -2843,9 +2715,6 @@ CSSStyleSheetImpl::InsertRule(const nsAString& aRule, mInner->mNameSpace = newNameSpace; } } - else { - CheckRuleForAttributes(cssRule); - } // We don't notify immediately for @import rules, but rather when // the sheet the rule is importing is loaded @@ -3048,7 +2917,6 @@ CSSStyleSheetImpl::InsertRuleIntoGroup(const nsAString & aRule, nsICSSGroupRule* DidDirty(); for (counter = 0; counter < rulecount; counter++) { rule = dont_AddRef((nsICSSRule*)rules->ElementAt(counter)); - CheckRuleForAttributes(rule); if (mDocument) { result = mDocument->StyleRuleAdded(this, rule); @@ -3516,6 +3384,7 @@ DashMatchCompare(const nsAString& aAttributeValue, static PRBool SelectorMatches(RuleProcessorData &data, nsCSSSelector* aSelector, PRInt32 aStateMask, // states NOT to test + nsIAtom* aAttribute, // attribute NOT to test PRInt8 aNegationIndex) { @@ -3746,15 +3615,20 @@ static PRBool SelectorMatches(RuleProcessorData &data, } // namespace/tag match - if (result && aSelector->mAttrList) { // test for attribute match - // if no attributes on the content, no match - if(!data.mHasAttributes) { + + if (result && aSelector->mAttrList) { + // test for attribute match + if (!data.mHasAttributes && !aAttribute) { + // if no attributes on the content, no match result = localFalse; } else { result = localTrue; nsAttrSelector* attr = aSelector->mAttrList; do { - if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) { + if (attr->mAttr == aAttribute) { + result = PR_TRUE; + } + else if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) { result = localFalse; } else if (attr->mFunction != NS_ATTR_FUNC_SET) { @@ -3826,7 +3700,7 @@ static PRBool SelectorMatches(RuleProcessorData &data, // case sensitivity: bug 93371 PRBool isCaseSensitive = data.mCompatMode != eCompatibility_NavQuirks; nsAtomList* IDList = aSelector->mIDList; - if (nsnull == IDList) { + if (nsnull == IDList || aAttribute == nsHTMLAtoms::id) { result = PR_TRUE; } else if (nsnull != data.mContentID) { @@ -3857,7 +3731,7 @@ static PRBool SelectorMatches(RuleProcessorData &data, } } - if (result) { + if (result && aAttribute != nsHTMLAtoms::kClass) { nsAtomList* classList = aSelector->mClassList; while (nsnull != classList) { if (localTrue == (!data.mStyledContent->HasClass(classList->mAtom, isCaseSensitive))) { @@ -3873,7 +3747,7 @@ static PRBool SelectorMatches(RuleProcessorData &data, // apply SelectorMatches to the negated selectors in the chain if (result && (nsnull != aSelector->mNegations)) { result = SelectorMatches(data, aSelector->mNegations, aStateMask, - aNegationIndex+1); + aAttribute, aNegationIndex+1); } return result; } @@ -3948,7 +3822,7 @@ static PRBool SelectorMatchesTree(RuleProcessorData &data, NS_ASSERTION(!content, "content must be null"); break; } - if (SelectorMatches(*newdata, selector, 0, 0)) { + if (SelectorMatches(*newdata, selector, 0, nsnull, 0)) { // to avoid greedy matching, we need to recurse if this is a // descendant combinator and the next combinator is not if ((NS_IS_GREEDY_OPERATOR(selector->mOperator)) && @@ -3992,7 +3866,7 @@ static void ContentEnumFunc(nsICSSStyleRule* aRule, void* aData) ElementRuleProcessorData* data = (ElementRuleProcessorData*)aData; nsCSSSelector* selector = aRule->FirstSelector(); - if (SelectorMatches(*data, selector, 0, 0)) { + if (SelectorMatches(*data, selector, 0, nsnull, 0)) { selector = selector->mNext; if (SelectorMatchesTree(*data, selector)) { // for performance, require that every implementation of @@ -4048,7 +3922,7 @@ static void PseudoEnumFunc(nsICSSStyleRule* aRule, void* aData) if (PRUnichar('+') == selector->mOperator) { return; // not valid here, can't match } - if (SelectorMatches(*data, selector, 0, 0)) { + if (SelectorMatches(*data, selector, 0, nsnull, 0)) { selector = selector->mNext; } else { @@ -4093,19 +3967,17 @@ CSSRuleProcessor::RulesMatching(PseudoRuleProcessorData* aData, return NS_OK; } -static -PRBool PR_CALLBACK StateEnumFunc(void* aSelector, void* aData) -{ - StateRuleProcessorData* data = (StateRuleProcessorData*)aData; - nsCSSSelector* selector = (nsCSSSelector*)aSelector; - if (SelectorMatches(*data, selector, data->mStateMask, 0)) { - selector = selector->mNext; - if (SelectorMatchesTree(*data, selector)) { - return PR_FALSE; - } - } - return PR_TRUE; +PR_STATIC_CALLBACK(PRBool) StateEnumFunc(void* aSelector, void* aData) +{ + StateRuleProcessorData* data = + NS_STATIC_CAST(StateRuleProcessorData*, aData); + nsCSSSelector* selector = NS_STATIC_CAST(nsCSSSelector*, aSelector); + + // Return whether we want to halt the enumeration, which is the + // negation of whether we find a match (ignoring the event state mask). + return !( SelectorMatches(*data, selector, data->mStateMask, nsnull, 0) && + SelectorMatchesTree(*data, selector->mNext) ); } NS_IMETHODIMP @@ -4135,6 +4007,46 @@ CSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData, return NS_OK; } +PR_STATIC_CALLBACK(PRBool) AttributeEnumFunc(void* aSelector, void* aData) +{ + AttributeRuleProcessorData* data = + NS_STATIC_CAST(AttributeRuleProcessorData*, aData); + nsCSSSelector* selector = NS_STATIC_CAST(nsCSSSelector*, aSelector); + + // Return whether we want to halt the enumeration, which is the + // negation of whether we find a match (ignoring the event state mask). + return !( SelectorMatches(*data, selector, 0, data->mAttribute, 0) && + SelectorMatchesTree(*data, selector->mNext) ); +} + +NS_IMETHODIMP +CSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult) +{ + NS_PRECONDITION(aData->mContent->IsContentOfType(nsIContent::eELEMENT), + "content must be element"); + + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext, aMedium); + + // We do the same thing for attributes that we do for state selectors + // (see HasStateDependentStyle), except that instead of one big list + // we have a hashtable with a per-attribute list. + + *aResult = PR_FALSE; + if (cascade) { + AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*, + PL_DHashTableOperate(&cascade->mAttributeSelectors, aData->mAttribute, + PL_DHASH_LOOKUP)); + if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + *aResult = + !entry->mSelectors->EnumerateForwards(AttributeEnumFunc, aData); + } + } + + return NS_OK; +} + NS_IMETHODIMP CSSRuleProcessor::ClearRuleCascades(void) { @@ -4170,18 +4082,41 @@ PRBool IsStateSelector(nsCSSSelector& aSelector) } static PRBool -BuildRuleHashAndStateSelectors(nsISupports* aRule, void* aCascade) +AddRule(nsISupports* aRule, void* aCascade) { nsICSSStyleRule* rule = NS_STATIC_CAST(nsICSSStyleRule*, aRule); RuleCascadeData *cascade = NS_STATIC_CAST(RuleCascadeData*, aCascade); + // Build the rule hash. cascade->mRuleHash.PrependRule(rule); - nsVoidArray* array = &cascade->mStateSelectors; + nsVoidArray* stateArray = &cascade->mStateSelectors; for (nsCSSSelector* selector = rule->FirstSelector(); - selector; selector = selector->mNext) + selector; selector = selector->mNext) { + // Build mStateSelectors. if (IsStateSelector(*selector)) + stateArray->AppendElement(selector); + + // Build mAttributeSelectors. + if (selector->mIDList) { + nsVoidArray *array = cascade->AttributeListFor(nsHTMLAtoms::id); + if (!array) + return PR_FALSE; array->AppendElement(selector); + } + if (selector->mClassList) { + nsVoidArray *array = cascade->AttributeListFor(nsHTMLAtoms::kClass); + if (!array) + return PR_FALSE; + array->AppendElement(selector); + } + for (nsAttrSelector *attr = selector->mAttrList; attr; attr = attr->mNext) { + nsVoidArray *array = cascade->AttributeListFor(attr->mAttr); + if (!array) + return PR_FALSE; + array->AppendElement(selector); + } + } return PR_TRUE; } @@ -4328,14 +4263,16 @@ CSSRuleProcessor::GetRuleCascade(nsIPresContext* aPresContext, nsIAtom* aMedium) cascade = new RuleCascadeData(aMedium, eCompatibility_NavQuirks == quirkMode); if (cascade) { - *cascadep = cascade; - CascadeEnumData data(aMedium); mSheets->EnumerateForwards(CascadeSheetRulesInto, &data); PutRulesInList(&data.mRuleArrays, cascade->mWeightedRules); - cascade->mWeightedRules->EnumerateBackwards( - BuildRuleHashAndStateSelectors, cascade); + if (!cascade->mWeightedRules->EnumerateBackwards(AddRule, cascade)) { + delete cascade; + cascade = nsnull; + } + + *cascadep = cascade; } } return cascade; diff --git a/mozilla/layout/style/nsHTMLCSSStyleSheet.cpp b/mozilla/layout/style/nsHTMLCSSStyleSheet.cpp index b3b72e1fdc2..f0c76de27ce 100644 --- a/mozilla/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/mozilla/layout/style/nsHTMLCSSStyleSheet.cpp @@ -181,16 +181,14 @@ public: nsIAtom* aMedium, PRBool* aResult); - // XXX style rule enumerations + NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult); #ifdef DEBUG virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const; #endif - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); private: // These are not supported and are not implemented! HTMLCSSStyleSheetImpl(const HTMLCSSStyleSheetImpl& aCopy); @@ -321,6 +319,16 @@ HTMLCSSStyleSheetImpl::HasStateDependentStyle(StateRuleProcessorData* aData, return NS_OK; } +// Test if style is dependent on attribute +NS_IMETHODIMP +HTMLCSSStyleSheetImpl::HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult) +{ + *aResult = PR_FALSE; + return NS_OK; +} + NS_IMETHODIMP @@ -447,16 +455,6 @@ void HTMLCSSStyleSheetImpl::List(FILE* out, PRInt32 aIndent) const } #endif -NS_IMETHODIMP -HTMLCSSStyleSheetImpl::AttributeAffectsStyle(nsIAtom *aAttribute, - nsIContent *aContent, - PRBool &aAffects) -{ - // XXX can attributes affect rules in these? - aAffects = PR_FALSE; - return NS_OK; -} - // XXX For backwards compatibility and convenience NS_EXPORT nsresult NS_NewHTMLCSSStyleSheet(nsIHTMLCSSStyleSheet** aInstancePtrResult, nsIURI* aURL, diff --git a/mozilla/layout/style/nsHTMLStyleSheet.cpp b/mozilla/layout/style/nsHTMLStyleSheet.cpp index b6975adbbd4..6eb3c335af5 100644 --- a/mozilla/layout/style/nsHTMLStyleSheet.cpp +++ b/mozilla/layout/style/nsHTMLStyleSheet.cpp @@ -646,6 +646,10 @@ public: nsIAtom* aMedium, PRBool* aResult); + NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult); + // nsIHTMLStyleSheet api NS_IMETHOD Init(nsIURI* aURL, nsIDocument* aDocument); NS_IMETHOD Reset(nsIURI* aURL); @@ -665,10 +669,6 @@ public: virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const; #endif - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); private: // These are not supported and are not implemented! HTMLStyleSheetImpl(const HTMLStyleSheetImpl& aCopy); @@ -919,6 +919,43 @@ HTMLStyleSheetImpl::HasStateDependentStyle(StateRuleProcessorData* aData, return NS_OK; } +NS_IMETHODIMP +HTMLStyleSheetImpl::HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult) +{ + // Result is true for |href| changes on HTML links if we have link rules. + nsIStyledContent *styledContent = aData->mStyledContent; + if (aData->mAttribute == nsHTMLAtoms::href && + (mLinkRule || mVisitedRule || mActiveRule) && + styledContent && + styledContent->IsContentOfType(nsIContent::eHTML) && + aData->mContentTag == nsHTMLAtoms::a) { + *aResult = PR_TRUE; + return NS_OK; + } + + // Don't worry about the HTMLDocumentColorRule since it only applies + // to descendants of body, when we're already reresolving. + + // Handle the content style rules. + if (styledContent) { + nsChangeHint hint = NS_STYLE_HINT_NONE; + styledContent->GetMappedAttributeImpact(aData->mAttribute, + aData->mModType, hint); + // This is the same test that nsGenericHTMLElement uses when calling + // nsHTMLAttributes::SetAttributeFor. + if ((hint & ~(nsChangeHint_AttrChange | nsChangeHint_Aural | + nsChangeHint_Content)) != 0) { + *aResult = PR_TRUE; + return NS_OK; + } + } + + *aResult = PR_FALSE; + return NS_OK; +} + NS_IMETHODIMP HTMLStyleSheetImpl::RulesMatching(PseudoRuleProcessorData* aData, @@ -1223,17 +1260,6 @@ void HTMLStyleSheetImpl::List(FILE* out, PRInt32 aIndent) const } #endif -NS_IMETHODIMP -HTMLStyleSheetImpl::AttributeAffectsStyle(nsIAtom *aAttribute, - nsIContent *aContent, - PRBool &aAffects) -{ - // XXX we should be checking to see if this is an href on an being - // XXX tweaked, in which case we really want to restyle - aAffects = PR_FALSE; - return NS_OK; -} - // XXX For convenience and backwards compatibility NS_EXPORT nsresult NS_NewHTMLStyleSheet(nsIHTMLStyleSheet** aInstancePtrResult, nsIURI* aURL, diff --git a/mozilla/layout/style/nsIStyleRuleProcessor.h b/mozilla/layout/style/nsIStyleRuleProcessor.h index b392d76f84a..aca01834244 100644 --- a/mozilla/layout/style/nsIStyleRuleProcessor.h +++ b/mozilla/layout/style/nsIStyleRuleProcessor.h @@ -145,6 +145,21 @@ struct StateRuleProcessorData : public RuleProcessorData { // Constants defined in nsIEventStateManager.h . }; +struct AttributeRuleProcessorData : public RuleProcessorData { + AttributeRuleProcessorData(nsIPresContext* aPresContext, + nsIContent* aContent, + nsIAtom* aAttribute, + PRInt32 aModType) + : RuleProcessorData(aPresContext, aContent, nsnull), + mAttribute(aAttribute), + mModType(aModType) + { + NS_PRECONDITION(aContent, "null pointer"); + } + nsIAtom* mAttribute; // |HasAttributeDependentStyle| for which attribute? + PRInt32 mModType; // The type of modification (see nsIDOMMutationEvent). +}; + // IID for the nsIStyleRuleProcessor interface {015575fe-7b6c-11d3-ba05-001083023c2b} #define NS_ISTYLE_RULE_PROCESSOR_IID \ @@ -172,6 +187,10 @@ public: NS_IMETHOD HasStateDependentStyle(StateRuleProcessorData* aData, nsIAtom* aMedium, PRBool* aResult) = 0; + // Test if style is dependent on attribute + NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + nsIAtom* aMedium, + PRBool* aResult) = 0; }; #endif /* nsIStyleRuleProcessor_h___ */ diff --git a/mozilla/layout/style/nsIStyleRuleSupplier.h b/mozilla/layout/style/nsIStyleRuleSupplier.h index 7d3de0cbe89..e2e75a9d68a 100644 --- a/mozilla/layout/style/nsIStyleRuleSupplier.h +++ b/mozilla/layout/style/nsIStyleRuleSupplier.h @@ -20,11 +20,6 @@ class nsIStyleRuleSupplier : public nsISupports { NS_IMETHOD WalkRules(nsIStyleSet* aStyleSet, nsISupportsArrayEnumFunc aFunc, RuleProcessorData* aData)=0; - - NS_IMETHOD AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, - void* aData, - nsIContent* aContent, - PRBool* aAffects)=0; }; #endif /* _nsIStyleRuleSupplier_h */ diff --git a/mozilla/layout/style/nsIStyleSheet.h b/mozilla/layout/style/nsIStyleSheet.h index 6752ca94e84..193b565428e 100644 --- a/mozilla/layout/style/nsIStyleSheet.h +++ b/mozilla/layout/style/nsIStyleSheet.h @@ -97,17 +97,9 @@ public: NS_IMETHOD GetStyleRuleProcessor(nsIStyleRuleProcessor*& aProcessor, nsIStyleRuleProcessor* aPrevProcessor) = 0; - // XXX style rule enumerations - - // If changing the given attribute cannot affect style context, aAffects - // will be PR_FALSE on return. - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects) = 0; - #ifdef DEBUG virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const = 0; #endif - }; #endif /* nsIStyleSheet_h___ */ diff --git a/mozilla/layout/style/nsStyleSet.cpp b/mozilla/layout/style/nsStyleSet.cpp index 8bf3ec2763e..e10ac083e9f 100644 --- a/mozilla/layout/style/nsStyleSet.cpp +++ b/mozilla/layout/style/nsStyleSet.cpp @@ -208,6 +208,12 @@ public: PRInt32 aStateMask, PRBool* aResult); + NS_IMETHOD HasAttributeDependentStyle(nsIPresContext* aPresContext, + nsIContent* aContent, + nsIAtom* aAtribute, + PRInt32 aModType, + PRBool* aResult); + NS_IMETHOD ConstructRootFrame(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame*& aFrameSubTree); @@ -296,9 +302,6 @@ public: NS_DECL_NSITIMERECORDER #endif - NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects); - private: static nsrefcnt gInstances; static nsIURI *gQuirkURI; @@ -1501,20 +1504,20 @@ struct StatefulData : public StateRuleProcessorData { nsIContent* aContent, PRInt32 aStateMask) : StateRuleProcessorData(aPresContext, aContent, aStateMask), mMedium(aMedium), - mStateful(PR_FALSE) + mHasStyle(PR_FALSE) {} nsIAtom* mMedium; - PRBool mStateful; + PRBool mHasStyle; }; static PRBool SheetHasStatefulStyle(nsISupports* aProcessor, void *aData) { nsIStyleRuleProcessor* processor = (nsIStyleRuleProcessor*)aProcessor; StatefulData* data = (StatefulData*)aData; - PRBool hasStateful; - processor->HasStateDependentStyle(data, data->mMedium, &hasStateful); - if (hasStateful) { - data->mStateful = PR_TRUE; + PRBool hasStyle; + processor->HasStateDependentStyle(data, data->mMedium, &hasStyle); + if (hasStyle) { + data->mHasStyle = PR_TRUE; // Stop iteration. Note that StyleSetImpl::WalkRuleProcessors uses // this to stop its own iteration in some cases, but not all (the // style rule supplier case). Since this optimization is only for @@ -1539,12 +1542,67 @@ StyleSetImpl::HasStateDependentStyle(nsIPresContext* aPresContext, mUserRuleProcessors || mDocRuleProcessors || mOverrideRuleProcessors)) { - nsIAtom* medium = nsnull; - aPresContext->GetMedium(&medium); + nsCOMPtr medium; + aPresContext->GetMedium(getter_AddRefs(medium)); StatefulData data(aPresContext, medium, aContent, aStateMask); WalkRuleProcessors(SheetHasStatefulStyle, &data); - NS_IF_RELEASE(medium); - *aResult = data.mStateful; + *aResult = data.mHasStyle; + } else { + *aResult = PR_FALSE; + } + + return NS_OK; +} + +struct AttributeData : public AttributeRuleProcessorData { + AttributeData(nsIPresContext* aPresContext, nsIAtom* aMedium, + nsIContent* aContent, nsIAtom* aAttribute, PRInt32 aModType) + : AttributeRuleProcessorData(aPresContext, aContent, aAttribute, aModType), + mMedium(aMedium), + mHasStyle(PR_FALSE) + {} + nsIAtom* mMedium; + PRBool mHasStyle; +}; + +static PRBool SheetHasAttributeStyle(nsISupports* aProcessor, void *aData) +{ + nsIStyleRuleProcessor* processor = (nsIStyleRuleProcessor*)aProcessor; + AttributeData* data = (AttributeData*)aData; + PRBool hasStyle; + processor->HasAttributeDependentStyle(data, data->mMedium, &hasStyle); + if (hasStyle) { + data->mHasStyle = PR_TRUE; + // Stop iteration. Note that StyleSetImpl::WalkRuleProcessors uses + // this to stop its own iteration in some cases, but not all (the + // style rule supplier case). Since this optimization is only for + // the case where we have a lot more work to do, it's not worth the + // code needed to make the stopping perfect. + return PR_FALSE; + } + return PR_TRUE; // continue +} + +// Test if style is dependent on content state +NS_IMETHODIMP +StyleSetImpl::HasAttributeDependentStyle(nsIPresContext* aPresContext, + nsIContent* aContent, + nsIAtom* aAttribute, + PRInt32 aModType, + PRBool* aResult) +{ + GatherRuleProcessors(); + + if (aContent->IsContentOfType(nsIContent::eELEMENT) && + (mAgentRuleProcessors || + mUserRuleProcessors || + mDocRuleProcessors || + mOverrideRuleProcessors)) { + nsCOMPtr medium; + aPresContext->GetMedium(getter_AddRefs(medium)); + AttributeData data(aPresContext, medium, aContent, aAttribute, aModType); + WalkRuleProcessors(SheetHasAttributeStyle, &data); + *aResult = data.mHasStyle; } else { *aResult = PR_FALSE; } @@ -1912,53 +1970,3 @@ void StyleSetImpl::ResetUniqueStyleItems(void) UNIQUE_STYLE_ITEMS(uniqueItems); uniqueItems->Clear(); } - -struct AttributeContentPair { - nsIAtom *attribute; - nsIContent *content; -}; - -static PRBool -EnumAffectsStyle(nsISupports *aElement, void *aData) -{ - nsIStyleSheet *sheet = NS_STATIC_CAST(nsIStyleSheet *, aElement); - AttributeContentPair *pair = (AttributeContentPair *)aData; - PRBool affects; - nsresult res = - sheet->AttributeAffectsStyle(pair->attribute, pair->content, affects); - if (NS_FAILED(res) || affects) - return PR_FALSE; // stop checking - - return PR_TRUE; -} - -NS_IMETHODIMP -StyleSetImpl::AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent, - PRBool &aAffects) -{ - AttributeContentPair pair; - pair.attribute = aAttribute; - pair.content = aContent; - - /* scoped sheets should be checked first, since - if present - they will contain - the bulk of the applicable rules for the content node. */ - if (mStyleRuleSupplier) - mStyleRuleSupplier->AttributeAffectsStyle(EnumAffectsStyle, &pair, aContent, &aAffects); - - if (!aAffects) { - /* check until we find a sheet that will be affected */ - if ((mDocSheets && !mDocSheets->EnumerateForwards(EnumAffectsStyle, &pair)) || - (mOverrideSheets && !mOverrideSheets->EnumerateForwards(EnumAffectsStyle, - &pair)) || - (mUserSheets && !mUserSheets->EnumerateForwards(EnumAffectsStyle, - &pair)) || - (mAgentSheets && !mAgentSheets->EnumerateForwards(EnumAffectsStyle, - &pair))) { - aAffects = PR_TRUE; - } else { - aAffects = PR_FALSE; - } - } - - return NS_OK; -}