Bug 773296 - Part 23: Support variables in CSSStyleDelcaration methods. r=dbaron

This adds support for custom properties on the remainder of the
CSSStyleDeclaration methods.

A shorthand that was specified using a variable reference is serialized
to its originally specified value, rather than by concatenating the
serializations of its longhand components.

A shorthand that was not specified using a variable references but which
has a component longhand that was is serialized to the empty string.
This commit is contained in:
Cameron McCormack 2013-12-12 13:09:46 +11:00
Родитель 99b85f3cd1
Коммит 5e31fc11d0
6 изменённых файлов: 241 добавлений и 11 удалений

Просмотреть файл

@ -175,8 +175,15 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
// Since we're doing this check for 'inherit' and 'initial' up front,
// we can also simplify the property serialization code by serializing
// those values up front as well.
//
// Additionally, if a shorthand property was set using a value with a
// variable reference and none of its component longhand properties were
// then overridden on the declaration, we return the token stream
// assigned to the shorthand.
const nsCSSValue* tokenStream = nullptr;
uint32_t totalCount = 0, importantCount = 0,
initialCount = 0, inheritCount = 0, unsetCount = 0;
initialCount = 0, inheritCount = 0, unsetCount = 0,
matchingTokenStreamCount = 0;
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
if (*p == eCSSProperty__x_system_font ||
nsCSSProps::PropHasFlags(*p, CSS_PROPERTY_DIRECTIONAL_SOURCE)) {
@ -201,6 +208,10 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
++initialCount;
} else if (val->GetUnit() == eCSSUnit_Unset) {
++unsetCount;
} else if (val->GetUnit() == eCSSUnit_TokenStream &&
val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) {
tokenStream = val;
++matchingTokenStreamCount;
}
}
if (importantCount != 0 && importantCount != totalCount) {
@ -226,6 +237,16 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
// Case (2): partially initial, inherit or unset.
return;
}
if (tokenStream) {
if (matchingTokenStreamCount == totalCount) {
// Shorthand was specified using variable references and all of its
// longhand components were set by the shorthand.
aValue.Append(tokenStream->GetTokenStreamValue()->mTokenStream);
} else {
// In all other cases, serialize to the empty string.
}
return;
}
nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
switch (aProperty) {
@ -1210,7 +1231,8 @@ void
Declaration::AddVariableDeclaration(const nsAString& aName,
CSSVariableDeclarations::Type aType,
const nsString& aValue,
bool aIsImportant)
bool aIsImportant,
bool aOverrideImportant)
{
MOZ_ASSERT(IsMutable());
@ -1220,7 +1242,8 @@ Declaration::AddVariableDeclaration(const nsAString& aName,
mVariableOrder.AppendElement(aName);
}
if (!aIsImportant && mImportantVariables && mImportantVariables->Has(aName)) {
if (!aIsImportant && !aOverrideImportant &&
mImportantVariables && mImportantVariables->Has(aName)) {
return;
}
@ -1234,6 +1257,9 @@ Declaration::AddVariableDeclaration(const nsAString& aName,
}
variables = mImportantVariables;
} else {
if (mImportantVariables) {
mImportantVariables->Remove(aName);
}
if (!mVariables) {
mVariables = new CSSVariableDeclarations;
}

Просмотреть файл

@ -78,11 +78,14 @@ public:
* @param aValue The value of the variable, if aType is
* CSSVariableDeclarations::eTokenStream.
* @param aIsImportant Whether the declaration is !important.
* @param aOverrideImportant When aIsImportant is false, whether an
* existing !important declaration will be overridden.
*/
void AddVariableDeclaration(const nsAString& aName,
CSSVariableDeclarations::Type aType,
const nsString& aValue,
bool aIsImportant);
bool aIsImportant,
bool aOverrideImportant);
/**
* Removes a custom property declaration from this object.

Просмотреть файл

@ -131,6 +131,15 @@ public:
nsMediaList* aMediaList,
bool aHTMLMode);
nsresult ParseVariable(const nsAString& aVariableName,
const nsAString& aPropValue,
nsIURI* aSheetURL,
nsIURI* aBaseURL,
nsIPrincipal* aSheetPrincipal,
css::Declaration* aDeclaration,
bool* aChanged,
bool aIsImportant);
bool ParseColorString(const nsSubstring& aBuffer,
nsIURI* aURL, // for error reporting
uint32_t aLineNumber, // for error reporting
@ -1257,6 +1266,7 @@ CSSParserImpl::ParseRule(const nsAString& aRule,
#pragma warning( push )
#pragma warning( disable : 4748 )
#endif
nsresult
CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
const nsAString& aPropValue,
@ -1331,6 +1341,63 @@ CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
ReleaseScanner();
return NS_OK;
}
nsresult
CSSParserImpl::ParseVariable(const nsAString& aVariableName,
const nsAString& aPropValue,
nsIURI* aSheetURI,
nsIURI* aBaseURI,
nsIPrincipal* aSheetPrincipal,
css::Declaration* aDeclaration,
bool* aChanged,
bool aIsImportant)
{
NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
NS_PRECONDITION(aBaseURI, "need base URI");
NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(),
"expected Variables to be enabled");
mData.AssertInitialState();
mTempData.AssertInitialState();
aDeclaration->AssertMutable();
nsCSSScanner scanner(aPropValue, 0);
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
mSection = eCSSSection_General;
*aChanged = false;
CSSVariableDeclarations::Type variableType;
nsString variableValue;
bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
// We should now be at EOF
if (parsedOK && GetToken(true)) {
REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
parsedOK = false;
}
if (!parsedOK) {
REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("var-") +
aVariableName);
REPORT_UNEXPECTED(PEDeclDropped);
OUTPUT_ERROR();
} else {
CLEAR_ERROR();
aDeclaration->AddVariableDeclaration(aVariableName, variableType,
variableValue, aIsImportant, true);
*aChanged = true;
}
mTempData.AssertInitialState();
ReleaseScanner();
return NS_OK;
}
#ifdef _MSC_VER
#pragma warning( pop )
#pragma optimize( "", on )
@ -5541,6 +5608,7 @@ CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
NS_PRECONDITION(aContext == eCSSContext_General ||
aContext == eCSSContext_Page,
"Must be page or general context");
bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
mTempData.AssertInitialState();
@ -5683,7 +5751,7 @@ CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
0, VAR_PREFIX_LENGTH).EqualsLiteral("var-"));
nsDependentString varName(propertyName, VAR_PREFIX_LENGTH); // remove 'var-'
aDeclaration->AddVariableDeclaration(varName, variableType, variableValue,
status == ePriority_Important);
status == ePriority_Important, false);
} else {
*aChanged |= mData.TransferFromBlock(mTempData, propID,
status == ePriority_Important,
@ -12667,6 +12735,21 @@ nsCSSParser::ParseProperty(const nsCSSProperty aPropID,
aIsImportant, aIsSVGMode);
}
nsresult
nsCSSParser::ParseVariable(const nsAString& aVariableName,
const nsAString& aPropValue,
nsIURI* aSheetURI,
nsIURI* aBaseURI,
nsIPrincipal* aSheetPrincipal,
css::Declaration* aDeclaration,
bool* aChanged,
bool aIsImportant)
{
return static_cast<CSSParserImpl*>(mImpl)->
ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
}
void
nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
nsIURI* aURI,

Просмотреть файл

@ -129,6 +129,15 @@ public:
bool aIsImportant,
bool aIsSVGMode = false);
// The same as ParseProperty but for a variable.
nsresult ParseVariable(const nsAString& aVariableName,
const nsAString& aPropValue,
nsIURI* aSheetURL,
nsIURI* aBaseURL,
nsIPrincipal* aSheetPrincipal,
mozilla::css::Declaration* aDeclaration,
bool* aChanged,
bool aIsImportant);
/**
* Parse aBuffer into a media list |aMediaList|, which must be
* non-null, replacing its current contents. If aHTMLMode is true,

Просмотреть файл

@ -52,6 +52,26 @@ nsDOMCSSDeclaration::GetPropertyValue(const nsCSSProperty aPropID,
return NS_OK;
}
// Length of the "var-" prefix of custom property names.
#define VAR_PREFIX_LENGTH 4
void
nsDOMCSSDeclaration::GetCustomPropertyValue(const nsAString& aPropertyName,
nsAString& aValue)
{
MOZ_ASSERT(Substring(aPropertyName,
0, VAR_PREFIX_LENGTH).EqualsLiteral("var-"));
css::Declaration* decl = GetCSSDeclaration(false);
if (!decl) {
aValue.Truncate();
return;
}
decl->GetVariableDeclaration(Substring(aPropertyName, VAR_PREFIX_LENGTH),
aValue);
}
NS_IMETHODIMP
nsDOMCSSDeclaration::SetPropertyValue(const nsCSSProperty aPropID,
const nsAString& aValue)
@ -156,6 +176,11 @@ nsDOMCSSDeclaration::GetPropertyValue(const nsAString& aPropertyName,
return NS_OK;
}
if (propID == eCSSPropertyExtra_variable) {
GetCustomPropertyValue(aPropertyName, aReturn);
return NS_OK;
}
return GetPropertyValue(propID, aReturn);
}
@ -189,19 +214,26 @@ nsDOMCSSDeclaration::SetProperty(const nsAString& aPropertyName,
// If the new value of the property is an empty string we remove the
// property.
// XXX this ignores the priority string, should it?
if (propID == eCSSPropertyExtra_variable) {
return RemoveCustomProperty(aPropertyName);
}
return RemoveProperty(propID);
}
bool important;
if (aPriority.IsEmpty()) {
return ParsePropertyValue(propID, aValue, false);
important = false;
} else if (aPriority.EqualsLiteral("important")) {
important = true;
} else {
// XXX silent failure?
return NS_OK;
}
if (aPriority.EqualsLiteral("important")) {
return ParsePropertyValue(propID, aValue, true);
if (propID == eCSSPropertyExtra_variable) {
return ParseCustomPropertyValue(aPropertyName, aValue, important);
}
// XXX silent failure?
return NS_OK;
return ParsePropertyValue(propID, aValue, important);
}
NS_IMETHODIMP
@ -215,6 +247,11 @@ nsDOMCSSDeclaration::RemoveProperty(const nsAString& aPropertyName,
return NS_OK;
}
if (propID == eCSSPropertyExtra_variable) {
RemoveCustomProperty(aPropertyName);
return NS_OK;
}
nsresult rv = GetPropertyValue(propID, aReturn);
NS_ENSURE_SUCCESS(rv, rv);
@ -278,6 +315,49 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSProperty aPropID,
return SetCSSDeclaration(decl);
}
nsresult
nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName,
const nsAString& aPropValue,
bool aIsImportant)
{
MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
css::Declaration* olddecl = GetCSSDeclaration(true);
if (!olddecl) {
return NS_ERROR_FAILURE;
}
CSSParsingEnvironment env;
GetCSSParsingEnvironment(env);
if (!env.mPrincipal) {
return NS_ERROR_NOT_AVAILABLE;
}
// For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
// Attribute setting code, which leads in turn to BeginUpdate. We
// need to start the update now so that the old rule doesn't get used
// between when we mutate the declaration and when we set the new
// rule (see stack in bug 209575).
mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
css::Declaration* decl = olddecl->EnsureMutable();
nsCSSParser cssParser(env.mCSSLoader);
bool changed;
nsresult result = cssParser.ParseVariable(Substring(aPropertyName,
VAR_PREFIX_LENGTH),
aPropValue, env.mSheetURI,
env.mBaseURI, env.mPrincipal, decl,
&changed, aIsImportant);
if (NS_FAILED(result) || !changed) {
if (decl != olddecl) {
delete decl;
}
return result;
}
return SetCSSDeclaration(decl);
}
nsresult
nsDOMCSSDeclaration::RemoveProperty(const nsCSSProperty aPropID)
{
@ -297,3 +377,26 @@ nsDOMCSSDeclaration::RemoveProperty(const nsCSSProperty aPropID)
decl->RemoveProperty(aPropID);
return SetCSSDeclaration(decl);
}
nsresult
nsDOMCSSDeclaration::RemoveCustomProperty(const nsAString& aPropertyName)
{
MOZ_ASSERT(Substring(aPropertyName,
0, VAR_PREFIX_LENGTH).EqualsLiteral("var-"));
css::Declaration* decl = GetCSSDeclaration(false);
if (!decl) {
return NS_OK; // no decl, so nothing to remove
}
// For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
// Attribute setting code, which leads in turn to BeginUpdate. We
// need to start the update now so that the old rule doesn't get used
// between when we mutate the declaration and when we set the new
// rule (see stack in bug 209575).
mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
decl = decl->EnsureMutable();
decl->RemoveVariableDeclaration(Substring(aPropertyName, VAR_PREFIX_LENGTH));
return SetCSSDeclaration(decl);
}

Просмотреть файл

@ -138,6 +138,12 @@ protected:
// return the old value; it just does a straight removal.
nsresult RemoveProperty(const nsCSSProperty aPropID);
void GetCustomPropertyValue(const nsAString& aPropertyName, nsAString& aValue);
nsresult RemoveCustomProperty(const nsAString& aPropertyName);
nsresult ParseCustomPropertyValue(const nsAString& aPropertyName,
const nsAString& aPropValue,
bool aIsImportant);
protected:
virtual ~nsDOMCSSDeclaration();
nsDOMCSSDeclaration()