From b2fc2a6576a80ffb7e691ae5e0c741f9d0a8a8fe Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 26 Jul 2008 09:14:48 -0700 Subject: [PATCH] Implement media queries, part 3: infrastructure for dynamic change handling at the rule processor and style set level. (Bug 156716) r+sr=bzbarsky --- layout/style/nsCSSRuleProcessor.cpp | 62 +++++++++++++++++++++------- layout/style/nsCSSRuleProcessor.h | 8 ++++ layout/style/nsHTMLCSSStyleSheet.cpp | 9 ++++ layout/style/nsHTMLStyleSheet.cpp | 8 ++++ layout/style/nsHTMLStyleSheet.h | 2 + layout/style/nsIStyleRuleProcessor.h | 8 ++++ layout/style/nsStyleSet.cpp | 20 +++++++++ layout/style/nsStyleSet.h | 8 ++++ 8 files changed, 111 insertions(+), 14 deletions(-) diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 028ba2bb129..2f16ac2b4b7 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -731,8 +731,9 @@ RuleCascadeData::AttributeListFor(nsIAtom* aAttribute) // nsCSSRuleProcessor::nsCSSRuleProcessor(const nsCOMArray& aSheets) - : mSheets(aSheets), - mRuleCascades(nsnull) + : mSheets(aSheets) + , mRuleCascades(nsnull) + , mLastPresContext(nsnull) { for (PRInt32 i = mSheets.Count() - 1; i >= 0; --i) mSheets[i]->AddRuleProcessor(this); @@ -2085,6 +2086,16 @@ nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData return NS_OK; } +NS_IMETHODIMP +nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext, + PRBool* aRulesChanged) +{ + RuleCascadeData *old = mRuleCascades; + RefreshRuleCascade(aPresContext); + *aRulesChanged = (old != mRuleCascades); + return NS_OK; +} + nsresult nsCSSRuleProcessor::ClearRuleCascades() { @@ -2346,6 +2357,24 @@ FillWeightArray(PLDHashTable *table, PLDHashEntryHdr *hdr, RuleCascadeData* nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext) +{ + // If anything changes about the presentation context, we will be + // notified. Otherwise, our cache is valid if mLastPresContext + // matches aPresContext. (The only rule processors used for multiple + // pres contexts are for XBL. These rule processors are probably less + // likely to have @media rules, and thus the cache is pretty likely to + // hit instantly even when we're switching between pres contexts.) + + if (!mRuleCascades || aPresContext != mLastPresContext) { + RefreshRuleCascade(aPresContext); + } + mLastPresContext = aPresContext; + + return mRuleCascades; +} + +void +nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext) { // Having RuleCascadeData objects be per-medium (over all variation // caused by media queries, handled through mCacheKey) works for now @@ -2353,12 +2382,16 @@ nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext) // set of stylesheets they can vary based on medium (@media) or // document (@-moz-document).) - RuleCascadeData **cascadep = &mRuleCascades; - RuleCascadeData *cascade; - while ((cascade = *cascadep)) { - if (cascade->mCacheKey.Matches(aPresContext)) - return cascade; - cascadep = &cascade->mNext; + for (RuleCascadeData **cascadep = &mRuleCascades, *cascade; + (cascade = *cascadep); cascadep = &cascade->mNext) { + if (cascade->mCacheKey.Matches(aPresContext)) { + // Ensure that the current one is always mRuleCascades. + *cascadep = cascade->mNext; + cascade->mNext = mRuleCascades; + mRuleCascades = cascade; + + return; + } } if (mSheets.Count() != 0) { @@ -2369,9 +2402,9 @@ nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext) CascadeEnumData data(aPresContext, newCascade->mCacheKey, newCascade->mRuleHash.Arena()); if (!data.mRulesByWeight.ops) - return nsnull; + return; /* out of memory */ if (!mSheets.EnumerateForwards(CascadeSheetRulesInto, &data)) - return nsnull; + return; /* out of memory */ // Sort the hash table of per-weight linked lists by weight. PRUint32 weightCount = data.mRulesByWeight.entryCount; @@ -2393,16 +2426,17 @@ nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext) // Calling |AddRule| reuses mNext! RuleValue *next = ruleValue->mNext; if (!AddRule(ruleValue, newCascade)) - return nsnull; + return; /* out of memory */ ruleValue = next; } while (ruleValue); } - *cascadep = newCascade; - cascade = newCascade.forget(); + // Ensure that the current one is always mRuleCascades. + newCascade->mNext = mRuleCascades; + mRuleCascades = newCascade.forget(); } } - return cascade; + return; } /* static */ PRBool diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index 5c581374d21..741ab9bd7ad 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -93,13 +93,21 @@ public: NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, nsReStyleHint* aResult); + NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext, + PRBool* aRulesChanged); + protected: RuleCascadeData* GetRuleCascade(nsPresContext* aPresContext); + void RefreshRuleCascade(nsPresContext* aPresContext); // The sheet order here is the same as in nsStyleSet::mSheets nsCOMArray mSheets; + // active first, then cached (most recent first) RuleCascadeData* mRuleCascades; + + // The last pres context for which GetRuleCascades was called. + nsPresContext *mLastPresContext; }; #endif /* nsCSSRuleProcessor_h_ */ diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index 3d5c3b075c8..2bbbd965567 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -366,6 +366,8 @@ public: NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, nsReStyleHint* aResult); + NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext, + PRBool* aResult); #ifdef DEBUG virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const; @@ -492,6 +494,13 @@ HTMLCSSStyleSheetImpl::HasAttributeDependentStyle(AttributeRuleProcessorData* aD return NS_OK; } +NS_IMETHODIMP +HTMLCSSStyleSheetImpl::MediumFeaturesChanged(nsPresContext* aPresContext, + PRBool* aRulesChanged) +{ + *aRulesChanged = PR_FALSE; + return NS_OK; +} NS_IMETHODIMP diff --git a/layout/style/nsHTMLStyleSheet.cpp b/layout/style/nsHTMLStyleSheet.cpp index 29fefe38a87..06d3174e2dc 100644 --- a/layout/style/nsHTMLStyleSheet.cpp +++ b/layout/style/nsHTMLStyleSheet.cpp @@ -562,6 +562,14 @@ nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData, return NS_OK; } +NS_IMETHODIMP +nsHTMLStyleSheet::MediumFeaturesChanged(nsPresContext* aPresContext, + PRBool* aRulesChanged) +{ + *aRulesChanged = PR_FALSE; + return NS_OK; +} + NS_IMETHODIMP nsHTMLStyleSheet::RulesMatching(PseudoRuleProcessorData* aData) diff --git a/layout/style/nsHTMLStyleSheet.h b/layout/style/nsHTMLStyleSheet.h index 674d08662fa..d0727af34da 100644 --- a/layout/style/nsHTMLStyleSheet.h +++ b/layout/style/nsHTMLStyleSheet.h @@ -83,6 +83,8 @@ public: nsReStyleHint* aResult); NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, nsReStyleHint* aResult); + NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext, + PRBool* aRulesChanged); nsresult Init(nsIURI* aURL, nsIDocument* aDocument); nsresult Reset(nsIURI* aURL); diff --git a/layout/style/nsIStyleRuleProcessor.h b/layout/style/nsIStyleRuleProcessor.h index bd53073dea7..39e65c7910c 100644 --- a/layout/style/nsIStyleRuleProcessor.h +++ b/layout/style/nsIStyleRuleProcessor.h @@ -273,6 +273,14 @@ public: */ NS_IMETHOD HasAttributeDependentStyle(AttributeRuleProcessorData* aData, nsReStyleHint* aResult) = 0; + + /** + * Do any processing that needs to happen as a result of a change in + * the characteristics of the medium, and return whether this rule + * processor's rules have changed (e.g., because of media queries). + */ + NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext, + PRBool* aRulesChanged) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIStyleRuleProcessor, diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 716acc8f7f1..adb2ef6de2b 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -22,6 +22,7 @@ * Contributor(s): * Daniel Glazman * Brian Ryner + * L. David Baron , Mozilla Corporation * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -962,3 +963,22 @@ nsStyleSet::HasAttributeDependentStyle(nsPresContext* aPresContext, return result; } + +PRBool +nsStyleSet::MediumFeaturesChanged(nsPresContext* aPresContext) +{ + // We can't use WalkRuleProcessors without a content node. + // XXX We don't notify mBindingManager. Should we? + PRBool stylesChanged = PR_FALSE; + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mRuleProcessors); ++i) { + nsIStyleRuleProcessor *processor = mRuleProcessors[i]; + if (!processor) { + continue; + } + PRBool thisChanged = PR_FALSE; + processor->MediumFeaturesChanged(aPresContext, &thisChanged); + stylesChanged = stylesChanged || thisChanged; + } + + return stylesChanged; +} diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h index 4fbec37094d..d8e84b94c78 100644 --- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -22,6 +22,7 @@ * Contributor(s): * Daniel Glazman * Brian Ryner + * L. David Baron , Mozilla Corporation * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -143,6 +144,13 @@ class nsStyleSet PRInt32 aModType, PRUint32 aStateMask); + /* + * Do any processing that needs to happen as a result of a change in + * the characteristics of the medium, and return whether style rules + * may have changed as a result. + */ + PRBool MediumFeaturesChanged(nsPresContext* aPresContext); + // APIs for registering objects that can supply additional // rules during processing. void SetBindingManager(nsBindingManager* aBindingManager)