mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
d8cbd4d866
Patch co-authored by Emmanuele Bassi <ebassi@gmail.com> This defines a CSSVariableDeclarations class that holds a set of variable declarations. This is at the specified value stage, so values can either be 'initial', 'inherit' or a token stream (which is what you normally have). The variables are stored in a hash table. Although it's a bit of a hack, we store 'initial' and 'inherit' using special string values that can't be valid token streams (we use "!" and ";"). Declaration objects now can have two CSSVariableDeclarations objects on them, to store normal and !important variable declarations. So that we keep preserving the order of declarations on the object, we inflate mOrder to store uint32_ts, where values from eCSSProperty_COUNT onwards represent custom properties. mVariableOrder stores the names of the variables corresponding to those entries in mOrder. We also add a new nsCSSProperty value, eCSSPropertyExtra_variable, which is used to represent any custom property name. nsCSSProps::LookupProperty can return this value. The changes to nsCSSParser are straightforward. Custom properties are parsed and checked for syntactic validity (e.g. "var(a,)" being invalid) and stored on the Declaration. We use nsCSSScanner's recording ability to grab the unparsed CSS string corresponding to the variable's value.
1290 lines
47 KiB
C++
1290 lines
47 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* representation of a declaration block (or style attribute) in a CSS
|
|
* stylesheet
|
|
*/
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/MemoryReporting.h"
|
|
|
|
#include "mozilla/css/Declaration.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "gfxFontConstants.h"
|
|
|
|
namespace mozilla {
|
|
namespace css {
|
|
|
|
Declaration::Declaration()
|
|
: mImmutable(false)
|
|
{
|
|
MOZ_COUNT_CTOR(mozilla::css::Declaration);
|
|
}
|
|
|
|
Declaration::Declaration(const Declaration& aCopy)
|
|
: mOrder(aCopy.mOrder),
|
|
mVariableOrder(aCopy.mVariableOrder),
|
|
mData(aCopy.mData ? aCopy.mData->Clone() : nullptr),
|
|
mImportantData(aCopy.mImportantData ?
|
|
aCopy.mImportantData->Clone() : nullptr),
|
|
mVariables(aCopy.mVariables ?
|
|
new CSSVariableDeclarations(*aCopy.mVariables) :
|
|
nullptr),
|
|
mImportantVariables(aCopy.mImportantVariables ?
|
|
new CSSVariableDeclarations(*aCopy.mImportantVariables) :
|
|
nullptr),
|
|
mImmutable(false)
|
|
{
|
|
MOZ_COUNT_CTOR(mozilla::css::Declaration);
|
|
}
|
|
|
|
Declaration::~Declaration()
|
|
{
|
|
MOZ_COUNT_DTOR(mozilla::css::Declaration);
|
|
}
|
|
|
|
void
|
|
Declaration::ValueAppended(nsCSSProperty aProperty)
|
|
{
|
|
NS_ABORT_IF_FALSE(!mData && !mImportantData,
|
|
"should only be called while expanded");
|
|
NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty),
|
|
"shorthands forbidden");
|
|
// order IS important for CSS, so remove and add to the end
|
|
mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
|
|
mOrder.AppendElement(static_cast<uint32_t>(aProperty));
|
|
}
|
|
|
|
void
|
|
Declaration::RemoveProperty(nsCSSProperty aProperty)
|
|
{
|
|
MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT);
|
|
|
|
nsCSSExpandedDataBlock data;
|
|
ExpandTo(&data);
|
|
NS_ABORT_IF_FALSE(!mData && !mImportantData, "Expand didn't null things out");
|
|
|
|
if (nsCSSProps::IsShorthand(aProperty)) {
|
|
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
|
|
data.ClearLonghandProperty(*p);
|
|
mOrder.RemoveElement(static_cast<uint32_t>(*p));
|
|
}
|
|
} else {
|
|
data.ClearLonghandProperty(aProperty);
|
|
mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
|
|
}
|
|
|
|
CompressFrom(&data);
|
|
}
|
|
|
|
bool
|
|
Declaration::HasProperty(nsCSSProperty aProperty) const
|
|
{
|
|
NS_ABORT_IF_FALSE(0 <= aProperty &&
|
|
aProperty < eCSSProperty_COUNT_no_shorthands,
|
|
"property ID out of range");
|
|
|
|
nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
|
|
? mImportantData : mData;
|
|
const nsCSSValue *val = data->ValueFor(aProperty);
|
|
return !!val;
|
|
}
|
|
|
|
bool
|
|
Declaration::AppendValueToString(nsCSSProperty aProperty,
|
|
nsAString& aResult) const
|
|
{
|
|
NS_ABORT_IF_FALSE(0 <= aProperty &&
|
|
aProperty < eCSSProperty_COUNT_no_shorthands,
|
|
"property ID out of range");
|
|
|
|
nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
|
|
? mImportantData : mData;
|
|
const nsCSSValue *val = data->ValueFor(aProperty);
|
|
if (!val) {
|
|
return false;
|
|
}
|
|
|
|
val->AppendToString(aProperty, aResult);
|
|
return true;
|
|
}
|
|
|
|
// Helper to append |aString| with the shorthand sides notation used in e.g.
|
|
// 'padding'. |aProperties| and |aValues| are expected to have 4 elements.
|
|
static void
|
|
AppendSidesShorthandToString(const nsCSSProperty aProperties[],
|
|
const nsCSSValue* aValues[],
|
|
nsAString& aString)
|
|
{
|
|
const nsCSSValue& value1 = *aValues[0];
|
|
const nsCSSValue& value2 = *aValues[1];
|
|
const nsCSSValue& value3 = *aValues[2];
|
|
const nsCSSValue& value4 = *aValues[3];
|
|
|
|
NS_ABORT_IF_FALSE(value1.GetUnit() != eCSSUnit_Null, "null value 1");
|
|
value1.AppendToString(aProperties[0], aString);
|
|
if (value1 != value2 || value1 != value3 || value1 != value4) {
|
|
aString.Append(PRUnichar(' '));
|
|
NS_ABORT_IF_FALSE(value2.GetUnit() != eCSSUnit_Null, "null value 2");
|
|
value2.AppendToString(aProperties[1], aString);
|
|
if (value1 != value3 || value2 != value4) {
|
|
aString.Append(PRUnichar(' '));
|
|
NS_ABORT_IF_FALSE(value3.GetUnit() != eCSSUnit_Null, "null value 3");
|
|
value3.AppendToString(aProperties[2], aString);
|
|
if (value2 != value4) {
|
|
aString.Append(PRUnichar(' '));
|
|
NS_ABORT_IF_FALSE(value4.GetUnit() != eCSSUnit_Null, "null value 4");
|
|
value4.AppendToString(aProperties[3], aString);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
|
|
{
|
|
aValue.Truncate(0);
|
|
|
|
// simple properties are easy.
|
|
if (!nsCSSProps::IsShorthand(aProperty)) {
|
|
AppendValueToString(aProperty, aValue);
|
|
return;
|
|
}
|
|
|
|
// DOM Level 2 Style says (when describing CSS2Properties, although
|
|
// not CSSStyleDeclaration.getPropertyValue):
|
|
// However, if there is no shorthand declaration that could be added
|
|
// to the ruleset without changing in any way the rules already
|
|
// declared in the ruleset (i.e., by adding longhand rules that were
|
|
// previously not declared in the ruleset), then the empty string
|
|
// should be returned for the shorthand property.
|
|
// This means we need to check a number of cases:
|
|
// (1) Since a shorthand sets all sub-properties, if some of its
|
|
// subproperties were not specified, we must return the empty
|
|
// string.
|
|
// (2) Since 'inherit', 'initial' and 'unset' can only be specified
|
|
// as the values for entire properties, we need to return the
|
|
// empty string if some but not all of the subproperties have one
|
|
// of those values.
|
|
// (3) Since a single value only makes sense with or without
|
|
// !important, we return the empty string if some values are
|
|
// !important and some are not.
|
|
// 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.
|
|
uint32_t totalCount = 0, importantCount = 0,
|
|
initialCount = 0, inheritCount = 0, unsetCount = 0;
|
|
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
|
|
if (*p == eCSSProperty__x_system_font ||
|
|
nsCSSProps::PropHasFlags(*p, CSS_PROPERTY_DIRECTIONAL_SOURCE)) {
|
|
// The system-font subproperty and the *-source properties don't count.
|
|
continue;
|
|
}
|
|
++totalCount;
|
|
const nsCSSValue *val = mData->ValueFor(*p);
|
|
NS_ABORT_IF_FALSE(!val || !mImportantData || !mImportantData->ValueFor(*p),
|
|
"can't be in both blocks");
|
|
if (!val && mImportantData) {
|
|
++importantCount;
|
|
val = mImportantData->ValueFor(*p);
|
|
}
|
|
if (!val) {
|
|
// Case (1) above: some subproperties not specified.
|
|
return;
|
|
}
|
|
if (val->GetUnit() == eCSSUnit_Inherit) {
|
|
++inheritCount;
|
|
} else if (val->GetUnit() == eCSSUnit_Initial) {
|
|
++initialCount;
|
|
} else if (val->GetUnit() == eCSSUnit_Unset) {
|
|
++unsetCount;
|
|
}
|
|
}
|
|
if (importantCount != 0 && importantCount != totalCount) {
|
|
// Case (3), no consistent importance.
|
|
return;
|
|
}
|
|
if (initialCount == totalCount) {
|
|
// Simplify serialization below by serializing initial up-front.
|
|
nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue);
|
|
return;
|
|
}
|
|
if (inheritCount == totalCount) {
|
|
// Simplify serialization below by serializing inherit up-front.
|
|
nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue);
|
|
return;
|
|
}
|
|
if (unsetCount == totalCount) {
|
|
// Simplify serialization below by serializing unset up-front.
|
|
nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue);
|
|
return;
|
|
}
|
|
if (initialCount != 0 || inheritCount != 0 || unsetCount != 0) {
|
|
// Case (2): partially initial, inherit or unset.
|
|
return;
|
|
}
|
|
|
|
nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
|
|
switch (aProperty) {
|
|
case eCSSProperty_margin:
|
|
case eCSSProperty_padding:
|
|
case eCSSProperty_border_color:
|
|
case eCSSProperty_border_style:
|
|
case eCSSProperty_border_width: {
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
|
|
kNotFound, "first subprop must be top");
|
|
NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
|
|
kNotFound, "second subprop must be right");
|
|
NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
|
|
kNotFound, "third subprop must be bottom");
|
|
NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
|
|
kNotFound, "fourth subprop must be left");
|
|
const nsCSSValue* vals[4] = {
|
|
data->ValueFor(subprops[0]),
|
|
data->ValueFor(subprops[1]),
|
|
data->ValueFor(subprops[2]),
|
|
data->ValueFor(subprops[3])
|
|
};
|
|
AppendSidesShorthandToString(subprops, vals, aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty_border_radius:
|
|
case eCSSProperty__moz_outline_radius: {
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
const nsCSSValue* vals[4] = {
|
|
data->ValueFor(subprops[0]),
|
|
data->ValueFor(subprops[1]),
|
|
data->ValueFor(subprops[2]),
|
|
data->ValueFor(subprops[3])
|
|
};
|
|
|
|
// For compatibility, only write a slash and the y-values
|
|
// if they're not identical to the x-values.
|
|
bool needY = false;
|
|
const nsCSSValue* xVals[4];
|
|
const nsCSSValue* yVals[4];
|
|
for (int i = 0; i < 4; i++) {
|
|
if (vals[i]->GetUnit() == eCSSUnit_Pair) {
|
|
needY = true;
|
|
xVals[i] = &vals[i]->GetPairValue().mXValue;
|
|
yVals[i] = &vals[i]->GetPairValue().mYValue;
|
|
} else {
|
|
xVals[i] = yVals[i] = vals[i];
|
|
}
|
|
}
|
|
|
|
AppendSidesShorthandToString(subprops, xVals, aValue);
|
|
if (needY) {
|
|
aValue.AppendLiteral(" / ");
|
|
AppendSidesShorthandToString(subprops, yVals, aValue);
|
|
}
|
|
break;
|
|
}
|
|
case eCSSProperty_border_image: {
|
|
// Even though there are some cases where we could omit
|
|
// 'border-image-source' (when it's none), it's probably not a
|
|
// good idea since it's likely to be confusing. It would also
|
|
// require adding the extra check that we serialize *something*.
|
|
AppendValueToString(eCSSProperty_border_image_source, aValue);
|
|
|
|
bool sliceDefault = data->HasDefaultBorderImageSlice();
|
|
bool widthDefault = data->HasDefaultBorderImageWidth();
|
|
bool outsetDefault = data->HasDefaultBorderImageOutset();
|
|
|
|
if (!sliceDefault || !widthDefault || !outsetDefault) {
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(eCSSProperty_border_image_slice, aValue);
|
|
if (!widthDefault || !outsetDefault) {
|
|
aValue.Append(NS_LITERAL_STRING(" /"));
|
|
if (!widthDefault) {
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(eCSSProperty_border_image_width, aValue);
|
|
}
|
|
if (!outsetDefault) {
|
|
aValue.Append(NS_LITERAL_STRING(" / "));
|
|
AppendValueToString(eCSSProperty_border_image_outset, aValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool repeatDefault = data->HasDefaultBorderImageRepeat();
|
|
if (!repeatDefault) {
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(eCSSProperty_border_image_repeat, aValue);
|
|
}
|
|
break;
|
|
}
|
|
case eCSSProperty_border: {
|
|
// If we have a non-default value for any of the properties that
|
|
// this shorthand sets but cannot specify, we have to return the
|
|
// empty string.
|
|
if (data->ValueFor(eCSSProperty_border_image_source)->GetUnit() !=
|
|
eCSSUnit_None ||
|
|
!data->HasDefaultBorderImageSlice() ||
|
|
!data->HasDefaultBorderImageWidth() ||
|
|
!data->HasDefaultBorderImageOutset() ||
|
|
!data->HasDefaultBorderImageRepeat() ||
|
|
data->ValueFor(eCSSProperty_border_top_colors)->GetUnit() !=
|
|
eCSSUnit_None ||
|
|
data->ValueFor(eCSSProperty_border_right_colors)->GetUnit() !=
|
|
eCSSUnit_None ||
|
|
data->ValueFor(eCSSProperty_border_bottom_colors)->GetUnit() !=
|
|
eCSSUnit_None ||
|
|
data->ValueFor(eCSSProperty_border_left_colors)->GetUnit() !=
|
|
eCSSUnit_None) {
|
|
break;
|
|
}
|
|
|
|
const nsCSSProperty* subproptables[3] = {
|
|
nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
|
|
nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
|
|
nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
|
|
};
|
|
bool match = true;
|
|
for (const nsCSSProperty** subprops = subproptables,
|
|
**subprops_end = ArrayEnd(subproptables);
|
|
subprops < subprops_end; ++subprops) {
|
|
// Check only the first four subprops in each table, since the
|
|
// others are extras for dimensional box properties.
|
|
const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
|
|
for (int32_t side = 1; side < 4; ++side) {
|
|
const nsCSSValue *otherSide =
|
|
data->ValueFor((*subprops)[side]);
|
|
if (*firstSide != *otherSide)
|
|
match = false;
|
|
}
|
|
}
|
|
if (!match) {
|
|
// We can't express what we have in the border shorthand
|
|
break;
|
|
}
|
|
// tweak aProperty and fall through
|
|
aProperty = eCSSProperty_border_top;
|
|
}
|
|
case eCSSProperty_border_top:
|
|
case eCSSProperty_border_right:
|
|
case eCSSProperty_border_bottom:
|
|
case eCSSProperty_border_left:
|
|
case eCSSProperty_border_start:
|
|
case eCSSProperty_border_end:
|
|
case eCSSProperty__moz_column_rule:
|
|
case eCSSProperty_outline: {
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
|
|
NS_LITERAL_CSTRING("-color")) ||
|
|
StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
|
|
NS_LITERAL_CSTRING("-color-value")),
|
|
"third subprop must be the color property");
|
|
const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
|
|
bool isMozUseTextColor =
|
|
colorValue->GetUnit() == eCSSUnit_Enumerated &&
|
|
colorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR;
|
|
if (!AppendValueToString(subprops[0], aValue) ||
|
|
!(aValue.Append(PRUnichar(' ')),
|
|
AppendValueToString(subprops[1], aValue)) ||
|
|
// Don't output a third value when it's -moz-use-text-color.
|
|
!(isMozUseTextColor ||
|
|
(aValue.Append(PRUnichar(' ')),
|
|
AppendValueToString(subprops[2], aValue)))) {
|
|
aValue.Truncate();
|
|
}
|
|
break;
|
|
}
|
|
case eCSSProperty_margin_left:
|
|
case eCSSProperty_margin_right:
|
|
case eCSSProperty_margin_start:
|
|
case eCSSProperty_margin_end:
|
|
case eCSSProperty_padding_left:
|
|
case eCSSProperty_padding_right:
|
|
case eCSSProperty_padding_start:
|
|
case eCSSProperty_padding_end:
|
|
case eCSSProperty_border_left_color:
|
|
case eCSSProperty_border_left_style:
|
|
case eCSSProperty_border_left_width:
|
|
case eCSSProperty_border_right_color:
|
|
case eCSSProperty_border_right_style:
|
|
case eCSSProperty_border_right_width:
|
|
case eCSSProperty_border_start_color:
|
|
case eCSSProperty_border_start_style:
|
|
case eCSSProperty_border_start_width:
|
|
case eCSSProperty_border_end_color:
|
|
case eCSSProperty_border_end_style:
|
|
case eCSSProperty_border_end_width: {
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
NS_ABORT_IF_FALSE(subprops[3] == eCSSProperty_UNKNOWN,
|
|
"not box property with physical vs. logical cascading");
|
|
AppendValueToString(subprops[0], aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty_background: {
|
|
// We know from above that all subproperties were specified.
|
|
// However, we still can't represent that in the shorthand unless
|
|
// they're all lists of the same length. So if they're different
|
|
// lengths, we need to bail out.
|
|
// We also need to bail out if an item has background-clip and
|
|
// background-origin that are different and not the default
|
|
// values. (We omit them if they're both default.)
|
|
const nsCSSValueList *image =
|
|
data->ValueFor(eCSSProperty_background_image)->
|
|
GetListValue();
|
|
const nsCSSValuePairList *repeat =
|
|
data->ValueFor(eCSSProperty_background_repeat)->
|
|
GetPairListValue();
|
|
const nsCSSValueList *attachment =
|
|
data->ValueFor(eCSSProperty_background_attachment)->
|
|
GetListValue();
|
|
const nsCSSValueList *position =
|
|
data->ValueFor(eCSSProperty_background_position)->
|
|
GetListValue();
|
|
const nsCSSValueList *clip =
|
|
data->ValueFor(eCSSProperty_background_clip)->
|
|
GetListValue();
|
|
const nsCSSValueList *origin =
|
|
data->ValueFor(eCSSProperty_background_origin)->
|
|
GetListValue();
|
|
const nsCSSValuePairList *size =
|
|
data->ValueFor(eCSSProperty_background_size)->
|
|
GetPairListValue();
|
|
for (;;) {
|
|
image->mValue.AppendToString(eCSSProperty_background_image, aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue);
|
|
if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
|
|
repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue);
|
|
}
|
|
aValue.Append(PRUnichar(' '));
|
|
attachment->mValue.AppendToString(eCSSProperty_background_attachment,
|
|
aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
position->mValue.AppendToString(eCSSProperty_background_position,
|
|
aValue);
|
|
|
|
if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
|
|
size->mYValue.GetUnit() != eCSSUnit_Auto) {
|
|
aValue.Append(PRUnichar(' '));
|
|
aValue.Append(PRUnichar('/'));
|
|
aValue.Append(PRUnichar(' '));
|
|
size->mXValue.AppendToString(eCSSProperty_background_size, aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
size->mYValue.AppendToString(eCSSProperty_background_size, aValue);
|
|
}
|
|
|
|
NS_ABORT_IF_FALSE(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
|
|
origin->mValue.GetUnit() == eCSSUnit_Enumerated,
|
|
"should not have inherit/initial within list");
|
|
|
|
if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
|
|
origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
|
|
MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
|
|
eCSSProperty_background_origin] ==
|
|
nsCSSProps::kBackgroundOriginKTable);
|
|
MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
|
|
eCSSProperty_background_clip] ==
|
|
nsCSSProps::kBackgroundOriginKTable);
|
|
static_assert(NS_STYLE_BG_CLIP_BORDER ==
|
|
NS_STYLE_BG_ORIGIN_BORDER &&
|
|
NS_STYLE_BG_CLIP_PADDING ==
|
|
NS_STYLE_BG_ORIGIN_PADDING &&
|
|
NS_STYLE_BG_CLIP_CONTENT ==
|
|
NS_STYLE_BG_ORIGIN_CONTENT,
|
|
"bg-clip and bg-origin style constants must agree");
|
|
aValue.Append(PRUnichar(' '));
|
|
origin->mValue.AppendToString(eCSSProperty_background_origin, aValue);
|
|
|
|
if (clip->mValue != origin->mValue) {
|
|
aValue.Append(PRUnichar(' '));
|
|
clip->mValue.AppendToString(eCSSProperty_background_clip, aValue);
|
|
}
|
|
}
|
|
|
|
image = image->mNext;
|
|
repeat = repeat->mNext;
|
|
attachment = attachment->mNext;
|
|
position = position->mNext;
|
|
clip = clip->mNext;
|
|
origin = origin->mNext;
|
|
size = size->mNext;
|
|
|
|
if (!image) {
|
|
if (repeat || attachment || position || clip || origin || size) {
|
|
// Uneven length lists, so can't be serialized as shorthand.
|
|
aValue.Truncate();
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
if (!repeat || !attachment || !position || !clip || !origin || !size) {
|
|
// Uneven length lists, so can't be serialized as shorthand.
|
|
aValue.Truncate();
|
|
return;
|
|
}
|
|
aValue.Append(PRUnichar(','));
|
|
aValue.Append(PRUnichar(' '));
|
|
}
|
|
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(eCSSProperty_background_color, aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty_font: {
|
|
// systemFont might not be present; other values are guaranteed to be
|
|
// available based on the shorthand check at the beginning of the
|
|
// function, as long as the prop is enabled
|
|
const nsCSSValue *systemFont =
|
|
data->ValueFor(eCSSProperty__x_system_font);
|
|
const nsCSSValue *style =
|
|
data->ValueFor(eCSSProperty_font_style);
|
|
const nsCSSValue *variant =
|
|
data->ValueFor(eCSSProperty_font_variant);
|
|
const nsCSSValue *weight =
|
|
data->ValueFor(eCSSProperty_font_weight);
|
|
const nsCSSValue *size =
|
|
data->ValueFor(eCSSProperty_font_size);
|
|
const nsCSSValue *lh =
|
|
data->ValueFor(eCSSProperty_line_height);
|
|
const nsCSSValue *family =
|
|
data->ValueFor(eCSSProperty_font_family);
|
|
const nsCSSValue *stretch =
|
|
data->ValueFor(eCSSProperty_font_stretch);
|
|
const nsCSSValue *sizeAdjust =
|
|
data->ValueFor(eCSSProperty_font_size_adjust);
|
|
const nsCSSValue *featureSettings =
|
|
data->ValueFor(eCSSProperty_font_feature_settings);
|
|
const nsCSSValue *languageOverride =
|
|
data->ValueFor(eCSSProperty_font_language_override);
|
|
const nsCSSValue *fontKerning =
|
|
data->ValueFor(eCSSProperty_font_kerning);
|
|
const nsCSSValue *fontSynthesis =
|
|
data->ValueFor(eCSSProperty_font_synthesis);
|
|
const nsCSSValue *fontVariantAlternates =
|
|
data->ValueFor(eCSSProperty_font_variant_alternates);
|
|
const nsCSSValue *fontVariantCaps =
|
|
data->ValueFor(eCSSProperty_font_variant_caps);
|
|
const nsCSSValue *fontVariantEastAsian =
|
|
data->ValueFor(eCSSProperty_font_variant_east_asian);
|
|
const nsCSSValue *fontVariantLigatures =
|
|
data->ValueFor(eCSSProperty_font_variant_ligatures);
|
|
const nsCSSValue *fontVariantNumeric =
|
|
data->ValueFor(eCSSProperty_font_variant_numeric);
|
|
const nsCSSValue *fontVariantPosition =
|
|
data->ValueFor(eCSSProperty_font_variant_position);
|
|
|
|
// if font features are not enabled, pointers for fontVariant
|
|
// values above may be null since the shorthand check ignores them
|
|
// font-variant-alternates enabled ==> layout.css.font-features.enabled is true
|
|
bool fontFeaturesEnabled =
|
|
nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
|
|
|
|
if (systemFont &&
|
|
systemFont->GetUnit() != eCSSUnit_None &&
|
|
systemFont->GetUnit() != eCSSUnit_Null) {
|
|
if (style->GetUnit() != eCSSUnit_System_Font ||
|
|
variant->GetUnit() != eCSSUnit_System_Font ||
|
|
weight->GetUnit() != eCSSUnit_System_Font ||
|
|
size->GetUnit() != eCSSUnit_System_Font ||
|
|
lh->GetUnit() != eCSSUnit_System_Font ||
|
|
family->GetUnit() != eCSSUnit_System_Font ||
|
|
stretch->GetUnit() != eCSSUnit_System_Font ||
|
|
sizeAdjust->GetUnit() != eCSSUnit_System_Font ||
|
|
featureSettings->GetUnit() != eCSSUnit_System_Font ||
|
|
languageOverride->GetUnit() != eCSSUnit_System_Font ||
|
|
(fontFeaturesEnabled &&
|
|
(fontKerning->GetUnit() != eCSSUnit_System_Font ||
|
|
fontSynthesis->GetUnit() != eCSSUnit_System_Font ||
|
|
fontVariantAlternates->GetUnit() != eCSSUnit_System_Font ||
|
|
fontVariantCaps->GetUnit() != eCSSUnit_System_Font ||
|
|
fontVariantEastAsian->GetUnit() != eCSSUnit_System_Font ||
|
|
fontVariantLigatures->GetUnit() != eCSSUnit_System_Font ||
|
|
fontVariantNumeric->GetUnit() != eCSSUnit_System_Font ||
|
|
fontVariantPosition->GetUnit() != eCSSUnit_System_Font))) {
|
|
// This can't be represented as a shorthand.
|
|
return;
|
|
}
|
|
systemFont->AppendToString(eCSSProperty__x_system_font, aValue);
|
|
} else {
|
|
// properties reset by this shorthand property to their
|
|
// initial values but not represented in its syntax
|
|
if (stretch->GetUnit() != eCSSUnit_Enumerated ||
|
|
stretch->GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
|
|
sizeAdjust->GetUnit() != eCSSUnit_None ||
|
|
featureSettings->GetUnit() != eCSSUnit_Normal ||
|
|
languageOverride->GetUnit() != eCSSUnit_Normal ||
|
|
(fontFeaturesEnabled &&
|
|
(fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO ||
|
|
fontSynthesis->GetUnit() != eCSSUnit_Enumerated ||
|
|
fontSynthesis->GetIntValue() !=
|
|
(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) ||
|
|
fontVariantAlternates->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantCaps->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantEastAsian->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantLigatures->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantNumeric->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantPosition->GetUnit() != eCSSUnit_Normal))) {
|
|
return;
|
|
}
|
|
|
|
if (style->GetUnit() != eCSSUnit_Enumerated ||
|
|
style->GetIntValue() != NS_FONT_STYLE_NORMAL) {
|
|
style->AppendToString(eCSSProperty_font_style, aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
}
|
|
if (variant->GetUnit() != eCSSUnit_Enumerated ||
|
|
variant->GetIntValue() != NS_FONT_VARIANT_NORMAL) {
|
|
variant->AppendToString(eCSSProperty_font_variant, aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
}
|
|
if (weight->GetUnit() != eCSSUnit_Enumerated ||
|
|
weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
|
|
weight->AppendToString(eCSSProperty_font_weight, aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
}
|
|
size->AppendToString(eCSSProperty_font_size, aValue);
|
|
if (lh->GetUnit() != eCSSUnit_Normal) {
|
|
aValue.Append(PRUnichar('/'));
|
|
lh->AppendToString(eCSSProperty_line_height, aValue);
|
|
}
|
|
aValue.Append(PRUnichar(' '));
|
|
family->AppendToString(eCSSProperty_font_family, aValue);
|
|
}
|
|
break;
|
|
}
|
|
case eCSSProperty_list_style:
|
|
if (AppendValueToString(eCSSProperty_list_style_type, aValue))
|
|
aValue.Append(PRUnichar(' '));
|
|
if (AppendValueToString(eCSSProperty_list_style_position, aValue))
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(eCSSProperty_list_style_image, aValue);
|
|
break;
|
|
case eCSSProperty_overflow: {
|
|
const nsCSSValue &xValue =
|
|
*data->ValueFor(eCSSProperty_overflow_x);
|
|
const nsCSSValue &yValue =
|
|
*data->ValueFor(eCSSProperty_overflow_y);
|
|
if (xValue == yValue)
|
|
xValue.AppendToString(eCSSProperty_overflow_x, aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty_text_decoration: {
|
|
// If text-decoration-color or text-decoration-style isn't initial value,
|
|
// we cannot serialize the text-decoration shorthand value.
|
|
const nsCSSValue *decorationColor =
|
|
data->ValueFor(eCSSProperty_text_decoration_color);
|
|
const nsCSSValue *decorationStyle =
|
|
data->ValueFor(eCSSProperty_text_decoration_style);
|
|
|
|
NS_ABORT_IF_FALSE(decorationStyle->GetUnit() == eCSSUnit_Enumerated,
|
|
nsPrintfCString("bad text-decoration-style unit %d",
|
|
decorationStyle->GetUnit()).get());
|
|
|
|
if (decorationColor->GetUnit() != eCSSUnit_Enumerated ||
|
|
decorationColor->GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR ||
|
|
decorationStyle->GetIntValue() !=
|
|
NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
|
|
return;
|
|
}
|
|
|
|
AppendValueToString(eCSSProperty_text_decoration_line, aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty_transition: {
|
|
const nsCSSValue *transProp =
|
|
data->ValueFor(eCSSProperty_transition_property);
|
|
const nsCSSValue *transDuration =
|
|
data->ValueFor(eCSSProperty_transition_duration);
|
|
const nsCSSValue *transTiming =
|
|
data->ValueFor(eCSSProperty_transition_timing_function);
|
|
const nsCSSValue *transDelay =
|
|
data->ValueFor(eCSSProperty_transition_delay);
|
|
|
|
NS_ABORT_IF_FALSE(transDuration->GetUnit() == eCSSUnit_List ||
|
|
transDuration->GetUnit() == eCSSUnit_ListDep,
|
|
nsPrintfCString("bad t-duration unit %d",
|
|
transDuration->GetUnit()).get());
|
|
NS_ABORT_IF_FALSE(transTiming->GetUnit() == eCSSUnit_List ||
|
|
transTiming->GetUnit() == eCSSUnit_ListDep,
|
|
nsPrintfCString("bad t-timing unit %d",
|
|
transTiming->GetUnit()).get());
|
|
NS_ABORT_IF_FALSE(transDelay->GetUnit() == eCSSUnit_List ||
|
|
transDelay->GetUnit() == eCSSUnit_ListDep,
|
|
nsPrintfCString("bad t-delay unit %d",
|
|
transDelay->GetUnit()).get());
|
|
|
|
const nsCSSValueList* dur = transDuration->GetListValue();
|
|
const nsCSSValueList* tim = transTiming->GetListValue();
|
|
const nsCSSValueList* del = transDelay->GetListValue();
|
|
|
|
if (transProp->GetUnit() == eCSSUnit_None ||
|
|
transProp->GetUnit() == eCSSUnit_All) {
|
|
// If any of the other three lists has more than one element,
|
|
// we can't use the shorthand.
|
|
if (!dur->mNext && !tim->mNext && !del->mNext) {
|
|
transProp->AppendToString(eCSSProperty_transition_property, aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
|
|
aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
del->mValue.AppendToString(eCSSProperty_transition_delay, aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
} else {
|
|
aValue.Truncate();
|
|
}
|
|
} else {
|
|
NS_ABORT_IF_FALSE(transProp->GetUnit() == eCSSUnit_List ||
|
|
transProp->GetUnit() == eCSSUnit_ListDep,
|
|
nsPrintfCString("bad t-prop unit %d",
|
|
transProp->GetUnit()).get());
|
|
const nsCSSValueList* pro = transProp->GetListValue();
|
|
for (;;) {
|
|
pro->mValue.AppendToString(eCSSProperty_transition_property,
|
|
aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
dur->mValue.AppendToString(eCSSProperty_transition_duration,
|
|
aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
|
|
aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
del->mValue.AppendToString(eCSSProperty_transition_delay,
|
|
aValue);
|
|
pro = pro->mNext;
|
|
dur = dur->mNext;
|
|
tim = tim->mNext;
|
|
del = del->mNext;
|
|
if (!pro || !dur || !tim || !del) {
|
|
break;
|
|
}
|
|
aValue.AppendLiteral(", ");
|
|
}
|
|
if (pro || dur || tim || del) {
|
|
// Lists not all the same length, can't use shorthand.
|
|
aValue.Truncate();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case eCSSProperty_animation: {
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
|
|
static const size_t numProps = 7;
|
|
NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
|
|
"unexpected number of subproperties");
|
|
const nsCSSValue* values[numProps];
|
|
const nsCSSValueList* lists[numProps];
|
|
|
|
for (uint32_t i = 0; i < numProps; ++i) {
|
|
values[i] = data->ValueFor(subprops[i]);
|
|
NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
|
|
values[i]->GetUnit() == eCSSUnit_ListDep,
|
|
nsPrintfCString("bad a-duration unit %d",
|
|
values[i]->GetUnit()).get());
|
|
lists[i] = values[i]->GetListValue();
|
|
}
|
|
|
|
for (;;) {
|
|
// We must serialize 'animation-name' last in case it has
|
|
// a value that conflicts with one of the other keyword properties.
|
|
NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
|
|
eCSSProperty_animation_name,
|
|
"animation-name must be last");
|
|
bool done = false;
|
|
for (uint32_t i = 0;;) {
|
|
lists[i]->mValue.AppendToString(subprops[i], aValue);
|
|
lists[i] = lists[i]->mNext;
|
|
if (!lists[i]) {
|
|
done = true;
|
|
}
|
|
if (++i == numProps) {
|
|
break;
|
|
}
|
|
aValue.Append(PRUnichar(' '));
|
|
}
|
|
if (done) {
|
|
break;
|
|
}
|
|
aValue.AppendLiteral(", ");
|
|
}
|
|
for (uint32_t i = 0; i < numProps; ++i) {
|
|
if (lists[i]) {
|
|
// Lists not all the same length, can't use shorthand.
|
|
aValue.Truncate();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case eCSSProperty_marker: {
|
|
const nsCSSValue &endValue =
|
|
*data->ValueFor(eCSSProperty_marker_end);
|
|
const nsCSSValue &midValue =
|
|
*data->ValueFor(eCSSProperty_marker_mid);
|
|
const nsCSSValue &startValue =
|
|
*data->ValueFor(eCSSProperty_marker_start);
|
|
if (endValue == midValue && midValue == startValue)
|
|
AppendValueToString(eCSSProperty_marker_end, aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty__moz_columns: {
|
|
// Two values, column-count and column-width, separated by a space.
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
AppendValueToString(subprops[0], aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(subprops[1], aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty_flex: {
|
|
// flex-grow, flex-shrink, flex-basis, separated by single space
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
|
|
AppendValueToString(subprops[0], aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(subprops[1], aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(subprops[2], aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty_flex_flow: {
|
|
// flex-direction, flex-wrap, separated by single space
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
|
|
"must have exactly two subproperties");
|
|
|
|
AppendValueToString(subprops[0], aValue);
|
|
aValue.Append(PRUnichar(' '));
|
|
AppendValueToString(subprops[1], aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty__moz_transform: {
|
|
// shorthands that are just aliases with different parsing rules
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
NS_ABORT_IF_FALSE(subprops[1] == eCSSProperty_UNKNOWN,
|
|
"must have exactly one subproperty");
|
|
AppendValueToString(subprops[0], aValue);
|
|
break;
|
|
}
|
|
case eCSSProperty_all:
|
|
// If we got here, then we didn't have all "inherit" or "initial" or
|
|
// "unset" values for all of the longhand property components of 'all'.
|
|
// There is no other possible value that is valid for all properties,
|
|
// so serialize as the empty string.
|
|
break;
|
|
default:
|
|
NS_ABORT_IF_FALSE(false, "no other shorthands");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Length of the "var-" prefix of custom property names.
|
|
#define VAR_PREFIX_LENGTH 4
|
|
|
|
bool
|
|
Declaration::GetValueIsImportant(const nsAString& aProperty) const
|
|
{
|
|
nsCSSProperty propID = nsCSSProps::LookupProperty(aProperty, nsCSSProps::eAny);
|
|
if (propID == eCSSProperty_UNKNOWN) {
|
|
return false;
|
|
}
|
|
if (propID == eCSSPropertyExtra_variable) {
|
|
return GetVariableValueIsImportant(Substring(aProperty, VAR_PREFIX_LENGTH));
|
|
}
|
|
return GetValueIsImportant(propID);
|
|
}
|
|
|
|
bool
|
|
Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
|
|
{
|
|
if (!mImportantData)
|
|
return false;
|
|
|
|
// Calling ValueFor is inefficient, but we can assume '!important' is rare.
|
|
|
|
if (!nsCSSProps::IsShorthand(aProperty)) {
|
|
return mImportantData->ValueFor(aProperty) != nullptr;
|
|
}
|
|
|
|
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
|
|
if (*p == eCSSProperty__x_system_font) {
|
|
// The system_font subproperty doesn't count.
|
|
continue;
|
|
}
|
|
if (!mImportantData->ValueFor(*p)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty,
|
|
nsAutoString& aValue,
|
|
nsAString& aResult) const
|
|
{
|
|
NS_ABORT_IF_FALSE(0 <= aProperty && aProperty < eCSSProperty_COUNT,
|
|
"property enum out of range");
|
|
NS_ABORT_IF_FALSE((aProperty < eCSSProperty_COUNT_no_shorthands) ==
|
|
aValue.IsEmpty(),
|
|
"aValue should be given for shorthands but not longhands");
|
|
AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
|
|
aResult.AppendLiteral(": ");
|
|
if (aValue.IsEmpty())
|
|
AppendValueToString(aProperty, aResult);
|
|
else
|
|
aResult.Append(aValue);
|
|
if (GetValueIsImportant(aProperty)) {
|
|
aResult.AppendLiteral(" ! important");
|
|
}
|
|
aResult.AppendLiteral("; ");
|
|
}
|
|
|
|
void
|
|
Declaration::AppendVariableAndValueToString(const nsAString& aName,
|
|
nsAString& aResult) const
|
|
{
|
|
aResult.AppendLiteral("var-");
|
|
aResult.Append(aName);
|
|
CSSVariableDeclarations::Type type;
|
|
nsString value;
|
|
bool important;
|
|
|
|
if (mImportantVariables && mImportantVariables->Get(aName, type, value)) {
|
|
important = true;
|
|
} else {
|
|
MOZ_ASSERT(mVariables);
|
|
MOZ_ASSERT(mVariables->Has(aName));
|
|
mVariables->Get(aName, type, value);
|
|
important = false;
|
|
}
|
|
|
|
switch (type) {
|
|
case CSSVariableDeclarations::eTokenStream:
|
|
if (value.IsEmpty()) {
|
|
aResult.Append(':');
|
|
} else {
|
|
aResult.AppendLiteral(": ");
|
|
aResult.Append(value);
|
|
}
|
|
break;
|
|
|
|
case CSSVariableDeclarations::eInitial:
|
|
aResult.AppendLiteral("initial");
|
|
break;
|
|
|
|
case CSSVariableDeclarations::eInherit:
|
|
aResult.AppendLiteral("inherit");
|
|
break;
|
|
|
|
default:
|
|
MOZ_ASSERT(false, "unexpected variable value type");
|
|
}
|
|
|
|
if (important) {
|
|
aResult.AppendLiteral("! important");
|
|
}
|
|
aResult.AppendLiteral("; ");
|
|
}
|
|
|
|
void
|
|
Declaration::ToString(nsAString& aString) const
|
|
{
|
|
// Someone cares about this declaration's contents, so don't let it
|
|
// change from under them. See e.g. bug 338679.
|
|
SetImmutable();
|
|
|
|
nsCSSCompressedDataBlock *systemFontData =
|
|
GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData;
|
|
const nsCSSValue *systemFont =
|
|
systemFontData->ValueFor(eCSSProperty__x_system_font);
|
|
const bool haveSystemFont = systemFont &&
|
|
systemFont->GetUnit() != eCSSUnit_None &&
|
|
systemFont->GetUnit() != eCSSUnit_Null;
|
|
bool didSystemFont = false;
|
|
|
|
int32_t count = mOrder.Length();
|
|
int32_t index;
|
|
nsAutoTArray<nsCSSProperty, 16> shorthandsUsed;
|
|
for (index = 0; index < count; index++) {
|
|
nsCSSProperty property = GetPropertyAt(index);
|
|
|
|
if (property == eCSSPropertyExtra_variable) {
|
|
uint32_t variableIndex = mOrder[index] - eCSSProperty_COUNT;
|
|
AppendVariableAndValueToString(mVariableOrder[variableIndex], aString);
|
|
continue;
|
|
}
|
|
|
|
if (!nsCSSProps::IsEnabled(property)) {
|
|
continue;
|
|
}
|
|
bool doneProperty = false;
|
|
|
|
// If we already used this property in a shorthand, skip it.
|
|
if (shorthandsUsed.Length() > 0) {
|
|
for (const nsCSSProperty *shorthands =
|
|
nsCSSProps::ShorthandsContaining(property);
|
|
*shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
|
|
if (shorthandsUsed.Contains(*shorthands)) {
|
|
doneProperty = true;
|
|
break;
|
|
}
|
|
}
|
|
if (doneProperty)
|
|
continue;
|
|
}
|
|
|
|
// Try to use this property in a shorthand.
|
|
nsAutoString value;
|
|
for (const nsCSSProperty *shorthands =
|
|
nsCSSProps::ShorthandsContaining(property);
|
|
*shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
|
|
// ShorthandsContaining returns the shorthands in order from those
|
|
// that contain the most subproperties to those that contain the
|
|
// least, which is exactly the order we want to test them.
|
|
nsCSSProperty shorthand = *shorthands;
|
|
|
|
// If GetValue gives us a non-empty string back, we can use that
|
|
// value; otherwise it's not possible to use this shorthand.
|
|
GetValue(shorthand, value);
|
|
if (!value.IsEmpty()) {
|
|
AppendPropertyAndValueToString(shorthand, value, aString);
|
|
shorthandsUsed.AppendElement(shorthand);
|
|
doneProperty = true;
|
|
break;
|
|
}
|
|
|
|
NS_ABORT_IF_FALSE(shorthand != eCSSProperty_font ||
|
|
*(shorthands + 1) == eCSSProperty_UNKNOWN,
|
|
"font should always be the only containing shorthand");
|
|
if (shorthand == eCSSProperty_font) {
|
|
if (haveSystemFont && !didSystemFont) {
|
|
// Output the shorthand font declaration that we will
|
|
// partially override later. But don't add it to
|
|
// |shorthandsUsed|, since we will have to override it.
|
|
systemFont->AppendToString(eCSSProperty__x_system_font, value);
|
|
AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
|
|
value.Truncate();
|
|
didSystemFont = true;
|
|
}
|
|
|
|
// That we output the system font is enough for this property if:
|
|
// (1) it's the hidden system font subproperty (which either
|
|
// means we output it or we don't have it), or
|
|
// (2) its value is the hidden system font value and it matches
|
|
// the hidden system font subproperty in importance, and
|
|
// we output the system font subproperty.
|
|
const nsCSSValue *val = systemFontData->ValueFor(property);
|
|
if (property == eCSSProperty__x_system_font ||
|
|
(haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
|
|
doneProperty = true;
|
|
}
|
|
}
|
|
}
|
|
if (doneProperty)
|
|
continue;
|
|
|
|
NS_ABORT_IF_FALSE(value.IsEmpty(), "value should be empty now");
|
|
AppendPropertyAndValueToString(property, value, aString);
|
|
}
|
|
if (! aString.IsEmpty()) {
|
|
// if the string is not empty, we have trailing whitespace we
|
|
// should remove
|
|
aString.Truncate(aString.Length() - 1);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
Declaration::List(FILE* out, int32_t aIndent) const
|
|
{
|
|
for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
|
|
|
|
fputs("{ ", out);
|
|
nsAutoString s;
|
|
ToString(s);
|
|
fputs(NS_ConvertUTF16toUTF8(s).get(), out);
|
|
fputs("}", out);
|
|
}
|
|
#endif
|
|
|
|
bool
|
|
Declaration::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
|
|
{
|
|
aReturn.Truncate();
|
|
if (aIndex < mOrder.Length()) {
|
|
nsCSSProperty property = GetPropertyAt(aIndex);
|
|
if (property == eCSSPropertyExtra_variable) {
|
|
GetCustomPropertyNameAt(aIndex, aReturn);
|
|
return true;
|
|
}
|
|
if (0 <= property) {
|
|
AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
Declaration::InitializeEmpty()
|
|
{
|
|
NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
|
|
mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
|
|
}
|
|
|
|
Declaration*
|
|
Declaration::EnsureMutable()
|
|
{
|
|
NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
|
|
if (!IsMutable()) {
|
|
return new Declaration(*this);
|
|
} else {
|
|
return this;
|
|
}
|
|
}
|
|
|
|
size_t
|
|
Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
size_t n = aMallocSizeOf(this);
|
|
n += mOrder.SizeOfExcludingThis(aMallocSizeOf);
|
|
n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0;
|
|
n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
|
|
if (mVariables) {
|
|
n += mVariables->SizeOfIncludingThis(aMallocSizeOf);
|
|
}
|
|
if (mImportantVariables) {
|
|
n += mImportantVariables->SizeOfIncludingThis(aMallocSizeOf);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
bool
|
|
Declaration::HasVariableDeclaration(const nsAString& aName) const
|
|
{
|
|
return (mVariables && mVariables->Has(aName)) ||
|
|
(mImportantVariables && mImportantVariables->Has(aName));
|
|
}
|
|
|
|
void
|
|
Declaration::GetVariableDeclaration(const nsAString& aName,
|
|
nsAString& aValue) const
|
|
{
|
|
aValue.Truncate();
|
|
|
|
CSSVariableDeclarations::Type type;
|
|
nsString value;
|
|
|
|
if ((mImportantVariables && mImportantVariables->Get(aName, type, value)) ||
|
|
(mVariables && mVariables->Get(aName, type, value))) {
|
|
switch (type) {
|
|
case CSSVariableDeclarations::eTokenStream:
|
|
aValue.Append(value);
|
|
break;
|
|
|
|
case CSSVariableDeclarations::eInitial:
|
|
aValue.AppendLiteral("initial");
|
|
break;
|
|
|
|
case CSSVariableDeclarations::eInherit:
|
|
aValue.AppendLiteral("inherit");
|
|
break;
|
|
|
|
default:
|
|
MOZ_ASSERT(false, "unexpected variable value type");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Declaration::AddVariableDeclaration(const nsAString& aName,
|
|
CSSVariableDeclarations::Type aType,
|
|
const nsString& aValue,
|
|
bool aIsImportant)
|
|
{
|
|
MOZ_ASSERT(IsMutable());
|
|
|
|
nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
|
|
if (index == nsTArray<nsString>::NoIndex) {
|
|
index = mVariableOrder.Length();
|
|
mVariableOrder.AppendElement(aName);
|
|
}
|
|
|
|
if (!aIsImportant && mImportantVariables && mImportantVariables->Has(aName)) {
|
|
return;
|
|
}
|
|
|
|
CSSVariableDeclarations* variables;
|
|
if (aIsImportant) {
|
|
if (mVariables) {
|
|
mVariables->Remove(aName);
|
|
}
|
|
if (!mImportantVariables) {
|
|
mImportantVariables = new CSSVariableDeclarations;
|
|
}
|
|
variables = mImportantVariables;
|
|
} else {
|
|
if (!mVariables) {
|
|
mVariables = new CSSVariableDeclarations;
|
|
}
|
|
variables = mVariables;
|
|
}
|
|
|
|
switch (aType) {
|
|
case CSSVariableDeclarations::eTokenStream:
|
|
variables->PutTokenStream(aName, aValue);
|
|
break;
|
|
|
|
case CSSVariableDeclarations::eInitial:
|
|
MOZ_ASSERT(aValue.IsEmpty());
|
|
variables->PutInitial(aName);
|
|
break;
|
|
|
|
case CSSVariableDeclarations::eInherit:
|
|
MOZ_ASSERT(aValue.IsEmpty());
|
|
variables->PutInherit(aName);
|
|
break;
|
|
|
|
default:
|
|
MOZ_ASSERT("unexpected aType value");
|
|
}
|
|
|
|
uint32_t propertyIndex = index + eCSSProperty_COUNT;
|
|
mOrder.RemoveElement(propertyIndex);
|
|
mOrder.AppendElement(propertyIndex);
|
|
}
|
|
|
|
void
|
|
Declaration::RemoveVariableDeclaration(const nsAString& aName)
|
|
{
|
|
if (mVariables) {
|
|
mVariables->Remove(aName);
|
|
}
|
|
if (mImportantVariables) {
|
|
mImportantVariables->Remove(aName);
|
|
}
|
|
nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
|
|
if (index != nsTArray<nsString>::NoIndex) {
|
|
mOrder.RemoveElement(index + eCSSProperty_COUNT);
|
|
}
|
|
}
|
|
|
|
bool
|
|
Declaration::GetVariableValueIsImportant(const nsAString& aName) const
|
|
{
|
|
return mImportantVariables && mImportantVariables->Has(aName);
|
|
}
|
|
|
|
} // namespace mozilla::css
|
|
} // namespace mozilla
|