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:
Cameron McCormack 2013-12-12 13:09:44 +11:00
parent a9096d4a6d
commit 7eb5d97a49
7 changed files with 255 additions and 1 deletions

View File

@ -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'.

View File

@ -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()

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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);