mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
Bug 773296 - Part 17: Resolve property values that have variable references at computed value time. r=dbaron
This re-parses property values at computed value time if they had a specified value that was a token stream. We add a function nsRuleNode::ResolveVariableReferences that looks at all the values in the nsRuleData and calls in to a new nsCSSParser::ParsePropertyWithVariableReferences function if they have a token stream value. We add a nsCSSExpandedDataBlock::MapRuleInfoInto function that will take the re-parsed property value and copy it back into the nsRuleData. nsRuleNode::ResolveVariableReferences returns whether any variables were attempted to be resolved, so that nsRuleNode::WalkRuleTree wil recompute the rule detail in case any became 'inherit'.
This commit is contained in:
parent
a9096d4a6d
commit
7eb5d97a49
@ -146,3 +146,6 @@ PEExpectedNonnegativeNP=Expected non-negative number or percentage.
|
||||
PEFilterFunctionArgumentsParsingError=Error in parsing arguments for filter function.
|
||||
PEVariableEOF=variable
|
||||
PEVariableEmpty=Expected variable value but found '%1$S'.
|
||||
PEValueWithVariablesParsingError=Error in parsing value for '%1$S' after substituting variables.
|
||||
PEValueWithVariablesFallbackInherit=Falling back to 'inherit'.
|
||||
PEValueWithVariablesFallbackInitial=Falling back to 'initial'.
|
||||
|
@ -545,6 +545,22 @@ nsCSSExpandedDataBlock::DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock,
|
||||
return changed;
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSExpandedDataBlock::MapRuleInfoInto(nsCSSProperty aPropID,
|
||||
nsRuleData* aRuleData) const
|
||||
{
|
||||
MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID));
|
||||
|
||||
const nsCSSValue* src = PropertyAt(aPropID);
|
||||
MOZ_ASSERT(src->GetUnit() != eCSSUnit_Null);
|
||||
|
||||
nsCSSValue* dest = aRuleData->ValueFor(aPropID);
|
||||
MOZ_ASSERT(dest->GetUnit() == eCSSUnit_TokenStream &&
|
||||
dest->GetTokenStreamValue()->mPropertyID == aPropID);
|
||||
|
||||
MapSinglePropertyInto(aPropID, src, dest, aRuleData);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
nsCSSExpandedDataBlock::DoAssertInitialState()
|
||||
|
@ -251,6 +251,14 @@ public:
|
||||
bool aMustCallValueAppended,
|
||||
mozilla::css::Declaration* aDeclaration);
|
||||
|
||||
/**
|
||||
* Copies the values for aPropID into the specified aRuleData object.
|
||||
*
|
||||
* This is used for copying parsed-at-computed-value-time properties
|
||||
* that had variable references. aPropID must be a longhand property.
|
||||
*/
|
||||
void MapRuleInfoInto(nsCSSProperty aPropID, nsRuleData* aRuleData) const;
|
||||
|
||||
void AssertInitialState() {
|
||||
#ifdef DEBUG
|
||||
DoAssertInitialState();
|
||||
@ -303,6 +311,12 @@ private:
|
||||
"property out of range");
|
||||
return &mValues[aProperty];
|
||||
}
|
||||
const nsCSSValue* PropertyAt(nsCSSProperty aProperty) const {
|
||||
NS_ABORT_IF_FALSE(0 <= aProperty &&
|
||||
aProperty < eCSSProperty_COUNT_no_shorthands,
|
||||
"property out of range");
|
||||
return &mValues[aProperty];
|
||||
}
|
||||
|
||||
void SetPropertyBit(nsCSSProperty aProperty) {
|
||||
mPropertiesSet.AddProperty(aProperty);
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include "nsMediaFeatures.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsRuleData.h"
|
||||
#include "mozilla/CSSVariableValues.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
@ -198,6 +200,38 @@ public:
|
||||
nsCSSTokenSerializationType& aFirstToken,
|
||||
nsCSSTokenSerializationType& aLastToken);
|
||||
|
||||
/**
|
||||
* Parses a string as a CSS token stream value for particular property,
|
||||
* resolving any variable references. The parsed property value is stored
|
||||
* in the specified nsRuleData object. If aShorthandPropertyID has a value
|
||||
* other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
|
||||
* otherwise, aPropertyID will be parsed. Either way, only aPropertyID,
|
||||
* a longhand property, will be copied over to the rule data.
|
||||
*
|
||||
* If the property cannot be parsed, it will be treated as if 'initial' or
|
||||
* 'inherit' were specified, for non-inherited and inherited properties
|
||||
* respectively.
|
||||
*
|
||||
* @param aPropertyID The ID of the longhand property whose value is to be
|
||||
* copied to the rule data.
|
||||
* @param aShorthandPropertyID The ID of the shorthand property to be parsed.
|
||||
* If a longhand property is to be parsed, aPropertyID is that property,
|
||||
* and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
|
||||
* @param aValue The CSS token stream value.
|
||||
* @param aVariables The set of variable values to use when resolving variable
|
||||
* references.
|
||||
* @param aRuleData The rule data object into which parsed property value for
|
||||
* aPropertyID will be stored.
|
||||
*/
|
||||
void ParsePropertyWithVariableReferences(nsCSSProperty aPropertyID,
|
||||
nsCSSProperty aShorthandPropertyID,
|
||||
const nsAString& aValue,
|
||||
const CSSVariableValues* aVariables,
|
||||
nsRuleData* aRuleData,
|
||||
nsIURI* aDocURL,
|
||||
nsIURI* aBaseURL,
|
||||
nsIPrincipal* aDocPrincipal);
|
||||
|
||||
protected:
|
||||
class nsAutoParseCompoundProperty;
|
||||
friend class nsAutoParseCompoundProperty;
|
||||
@ -2000,6 +2034,83 @@ CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
|
||||
return valid;
|
||||
}
|
||||
|
||||
void
|
||||
CSSParserImpl::ParsePropertyWithVariableReferences(
|
||||
nsCSSProperty aPropertyID,
|
||||
nsCSSProperty aShorthandPropertyID,
|
||||
const nsAString& aValue,
|
||||
const CSSVariableValues* aVariables,
|
||||
nsRuleData* aRuleData,
|
||||
nsIURI* aDocURL,
|
||||
nsIURI* aBaseURL,
|
||||
nsIPrincipal* aDocPrincipal)
|
||||
{
|
||||
mTempData.AssertInitialState();
|
||||
|
||||
bool valid;
|
||||
nsString expandedValue;
|
||||
|
||||
// Resolve any variable references in the property value.
|
||||
{
|
||||
nsCSSScanner scanner(aValue, 0);
|
||||
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
|
||||
InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
|
||||
|
||||
nsCSSTokenSerializationType firstToken, lastToken;
|
||||
valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
|
||||
firstToken, lastToken);
|
||||
ReleaseScanner();
|
||||
}
|
||||
|
||||
nsCSSProperty propertyToParse =
|
||||
aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
|
||||
aPropertyID;
|
||||
|
||||
// Parse the property with that resolved value.
|
||||
{
|
||||
nsCSSScanner scanner(expandedValue, 0);
|
||||
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
|
||||
InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
|
||||
bool parsedOK = ParseProperty(propertyToParse);
|
||||
if (parsedOK && GetToken(true)) {
|
||||
REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
|
||||
parsedOK = false;
|
||||
}
|
||||
if (!parsedOK) {
|
||||
NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
|
||||
propertyToParse));
|
||||
REPORT_UNEXPECTED_P(PEValueWithVariablesParsingError, propName);
|
||||
if (nsCSSProps::IsInherited(aPropertyID)) {
|
||||
REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
|
||||
} else {
|
||||
REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
|
||||
}
|
||||
OUTPUT_ERROR();
|
||||
valid = false;
|
||||
}
|
||||
ReleaseScanner();
|
||||
}
|
||||
|
||||
// If the property could not be parsed with the resolved value, then we
|
||||
// treat it as if the value were 'initial' or 'inherit', depending on whether
|
||||
// the property is an inherited property.
|
||||
if (!valid) {
|
||||
nsCSSValue defaultValue;
|
||||
if (nsCSSProps::IsInherited(aPropertyID)) {
|
||||
defaultValue.SetInheritValue();
|
||||
} else {
|
||||
defaultValue.SetInitialValue();
|
||||
}
|
||||
mTempData.AddLonghandProperty(aPropertyID, defaultValue);
|
||||
}
|
||||
|
||||
// Copy the property value into the rule data.
|
||||
mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
|
||||
|
||||
mTempData.ClearProperty(propertyToParse);
|
||||
mTempData.AssertInitialState();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
@ -12619,3 +12730,20 @@ nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
|
||||
ResolveVariableValue(aPropertyValue, aVariables,
|
||||
aResult, aFirstToken, aLastToken);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSParser::ParsePropertyWithVariableReferences(
|
||||
nsCSSProperty aPropertyID,
|
||||
nsCSSProperty aShorthandPropertyID,
|
||||
const nsAString& aValue,
|
||||
const CSSVariableValues* aVariables,
|
||||
nsRuleData* aRuleData,
|
||||
nsIURI* aDocURL,
|
||||
nsIURI* aBaseURL,
|
||||
nsIPrincipal* aDocPrincipal)
|
||||
{
|
||||
static_cast<CSSParserImpl*>(mImpl)->
|
||||
ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
|
||||
aValue, aVariables, aRuleData, aDocURL,
|
||||
aBaseURL, aDocPrincipal);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ struct nsCSSSelectorList;
|
||||
class nsMediaList;
|
||||
class nsCSSKeyframeRule;
|
||||
class nsCSSValue;
|
||||
class nsRuleData;
|
||||
|
||||
namespace mozilla {
|
||||
class CSSVariableValues;
|
||||
@ -220,6 +221,28 @@ public:
|
||||
nsCSSTokenSerializationType& aFirstToken,
|
||||
nsCSSTokenSerializationType& aLastToken);
|
||||
|
||||
/**
|
||||
* Parses a string as a CSS token stream value for particular property,
|
||||
* resolving any variable references. The parsed property value is stored
|
||||
* in the specified nsRuleData object. If aShorthandPropertyID has a value
|
||||
* other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
|
||||
* otherwise, aPropertyID will be parsed. Either way, only aPropertyID,
|
||||
* a longhand property, will be copied over to the rule data.
|
||||
*
|
||||
* If the property cannot be parsed, it will be treated as if 'initial' or
|
||||
* 'inherit' were specified, for non-inherited and inherited properties
|
||||
* respectively.
|
||||
*/
|
||||
void ParsePropertyWithVariableReferences(
|
||||
nsCSSProperty aPropertyID,
|
||||
nsCSSProperty aShorthandPropertyID,
|
||||
const nsAString& aValue,
|
||||
const mozilla::CSSVariableValues* aVariables,
|
||||
nsRuleData* aRuleData,
|
||||
nsIURI* aDocURL,
|
||||
nsIURI* aBaseURL,
|
||||
nsIPrincipal* aDocPrincipal);
|
||||
|
||||
protected:
|
||||
// This is a CSSParserImpl*, but if we expose that type name in this
|
||||
// header, we can't put the type definition (in nsCSSParser.cpp) in
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "nsIDocument.h"
|
||||
#include "prtime.h"
|
||||
#include "CSSVariableResolver.h"
|
||||
#include "nsCSSParser.h"
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#include <malloc.h>
|
||||
@ -2021,6 +2022,44 @@ private:
|
||||
size_t mCount;
|
||||
};
|
||||
|
||||
/* static */ bool
|
||||
nsRuleNode::ResolveVariableReferences(const nsStyleStructID aSID,
|
||||
nsRuleData* aRuleData,
|
||||
nsStyleContext* aContext)
|
||||
{
|
||||
MOZ_ASSERT(aSID != eStyleStruct_Variables);
|
||||
MOZ_ASSERT(aRuleData->mSIDs & nsCachedStyleData::GetBitForSID(aSID));
|
||||
MOZ_ASSERT(aRuleData->mValueOffsets[aSID] == 0);
|
||||
|
||||
nsCSSParser parser;
|
||||
bool anyTokenStreams = false;
|
||||
|
||||
// Look at each property in the nsRuleData for the given style struct.
|
||||
size_t nprops = nsCSSProps::PropertyCountInStruct(aSID);
|
||||
for (nsCSSValue* value = aRuleData->mValueStorage,
|
||||
*values_end = aRuleData->mValueStorage + nprops;
|
||||
value != values_end; value++) {
|
||||
if (value->GetUnit() != eCSSUnit_TokenStream) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const CSSVariableValues* variables =
|
||||
&aContext->StyleVariables()->mVariables;
|
||||
nsCSSValueTokenStream* tokenStream = value->GetTokenStreamValue();
|
||||
|
||||
parser.ParsePropertyWithVariableReferences(
|
||||
tokenStream->mPropertyID, tokenStream->mShorthandPropertyID,
|
||||
tokenStream->mTokenStream, variables, aRuleData,
|
||||
tokenStream->mSheetURI, tokenStream->mBaseURI,
|
||||
tokenStream->mSheetPrincipal);
|
||||
|
||||
aRuleData->mCanStoreInRuleTree = false;
|
||||
anyTokenStreams = true;
|
||||
}
|
||||
|
||||
return anyTokenStreams;
|
||||
}
|
||||
|
||||
const void*
|
||||
nsRuleNode::WalkRuleTree(const nsStyleStructID aSID,
|
||||
nsStyleContext* aContext)
|
||||
@ -2107,6 +2146,19 @@ nsRuleNode::WalkRuleTree(const nsStyleStructID aSID,
|
||||
ruleNode = ruleNode->mParent;
|
||||
}
|
||||
|
||||
bool recomputeDetail = false;
|
||||
|
||||
// If we are computing a style struct other than nsStyleVariables, and
|
||||
// ruleData has any properties with variable references (nsCSSValues of
|
||||
// type eCSSUnit_TokenStream), then we need to resolve these.
|
||||
if (aSID != eStyleStruct_Variables) {
|
||||
// A property's value might have became 'inherit' after resolving
|
||||
// variable references. (This happens when an inherited property
|
||||
// fails to parse its resolved value.) We need to recompute
|
||||
// |detail| in case this happened.
|
||||
recomputeDetail = ResolveVariableReferences(aSID, &ruleData, aContext);
|
||||
}
|
||||
|
||||
// If needed, unset the properties that don't have a flag that allows
|
||||
// them to be set for this style context. (For example, only some
|
||||
// properties apply to :first-line and :first-letter.)
|
||||
@ -2114,9 +2166,13 @@ nsRuleNode::WalkRuleTree(const nsStyleStructID aSID,
|
||||
if (pseudoRestriction) {
|
||||
UnsetPropertiesWithoutFlags(aSID, &ruleData, pseudoRestriction);
|
||||
|
||||
// Recompute |detail| based on the restrictions we just applied.
|
||||
// We need to recompute |detail| based on the restrictions we just applied.
|
||||
// We can adjust |detail| arbitrarily because of the restriction
|
||||
// rule added in nsStyleSet::WalkRestrictionRule.
|
||||
recomputeDetail = true;
|
||||
}
|
||||
|
||||
if (recomputeDetail) {
|
||||
detail = CheckSpecifiedProperties(aSID, &ruleData);
|
||||
}
|
||||
|
||||
@ -3733,6 +3789,8 @@ nsRuleNode::SetGenericFont(nsPresContext* aPresContext,
|
||||
if (i != 0)
|
||||
ruleData.ValueForFontFamily()->Reset();
|
||||
|
||||
ResolveVariableReferences(eStyleStruct_Font, &ruleData, aContext);
|
||||
|
||||
nsRuleNode::SetFont(aPresContext, context,
|
||||
aGenericFontID, &ruleData, &parentFont, aFont,
|
||||
false, dummy);
|
||||
|
@ -423,6 +423,18 @@ protected:
|
||||
const void* SetDefaultOnRoot(const nsStyleStructID aSID,
|
||||
nsStyleContext* aContext);
|
||||
|
||||
/**
|
||||
* Resolves any property values in aRuleData for a given style struct that
|
||||
* have eCSSUnit_TokenStream values, by resolving them against the computed
|
||||
* variable values on the style context and re-parsing the property.
|
||||
*
|
||||
* @return Whether any properties with eCSSUnit_TokenStream values were
|
||||
* encountered.
|
||||
*/
|
||||
static bool ResolveVariableReferences(const nsStyleStructID aSID,
|
||||
nsRuleData* aRuleData,
|
||||
nsStyleContext* aContext);
|
||||
|
||||
const void*
|
||||
WalkRuleTree(const nsStyleStructID aSID, nsStyleContext* aContext);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user