mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-14 15:37:55 +00:00
41cdae473a
Backed out changeset 936703c75200 (bug 649142) Backed out changeset b0252d2620d8 (bug 649142) Backed out changeset 69ddb2036c50 (bug 649142) Backed out changeset 67748675e669 (bug 649142) Backed out changeset 15ed55c61f4e (bug 649142) Backed out changeset 35c42cd138e1 (bug 649142) Backed out changeset 1335630cf287 (bug 649142) Backed out changeset b5725cd39a31 (bug 649142) Backed out changeset b0eb691d6695 (bug 649142)
1558 lines
58 KiB
C++
1558 lines
58 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"
|
|
#include "nsStyleUtil.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,
|
|
nsCSSValue::Serialization aSerialization) 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, aSerialization);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
|
|
{
|
|
GetValue(aProperty, aValue, nsCSSValue::eNormalized);
|
|
}
|
|
|
|
void
|
|
Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const
|
|
{
|
|
GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified);
|
|
}
|
|
|
|
void
|
|
Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue,
|
|
nsCSSValue::Serialization aSerialization) const
|
|
{
|
|
aValue.Truncate(0);
|
|
|
|
// simple properties are easy.
|
|
if (!nsCSSProps::IsShorthand(aProperty)) {
|
|
AppendValueToString(aProperty, aValue, aSerialization);
|
|
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.
|
|
//
|
|
// 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,
|
|
matchingTokenStreamCount = 0, nonMatchingTokenStreamCount = 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;
|
|
} else if (val->GetUnit() == eCSSUnit_TokenStream) {
|
|
if (val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) {
|
|
tokenStream = val;
|
|
++matchingTokenStreamCount;
|
|
} else {
|
|
++nonMatchingTokenStreamCount;
|
|
}
|
|
}
|
|
}
|
|
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,
|
|
nsCSSValue::eNormalized);
|
|
return;
|
|
}
|
|
if (inheritCount == totalCount) {
|
|
// Simplify serialization below by serializing inherit up-front.
|
|
nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue,
|
|
nsCSSValue::eNormalized);
|
|
return;
|
|
}
|
|
if (unsetCount == totalCount) {
|
|
// Simplify serialization below by serializing unset up-front.
|
|
nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue,
|
|
nsCSSValue::eNormalized);
|
|
return;
|
|
}
|
|
if (initialCount != 0 || inheritCount != 0 ||
|
|
unsetCount != 0 || nonMatchingTokenStreamCount != 0) {
|
|
// Case (2): partially initial, inherit, unset or token stream.
|
|
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) {
|
|
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])
|
|
};
|
|
nsCSSValue::AppendSidesShorthandToString(subprops, vals, aValue,
|
|
aSerialization);
|
|
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])
|
|
};
|
|
nsCSSValue::AppendBasicShapeRadiusToString(subprops, vals, aValue,
|
|
aSerialization);
|
|
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,
|
|
aSerialization);
|
|
|
|
bool sliceDefault = data->HasDefaultBorderImageSlice();
|
|
bool widthDefault = data->HasDefaultBorderImageWidth();
|
|
bool outsetDefault = data->HasDefaultBorderImageOutset();
|
|
|
|
if (!sliceDefault || !widthDefault || !outsetDefault) {
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(eCSSProperty_border_image_slice, aValue,
|
|
aSerialization);
|
|
if (!widthDefault || !outsetDefault) {
|
|
aValue.AppendLiteral(" /");
|
|
if (!widthDefault) {
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(eCSSProperty_border_image_width, aValue,
|
|
aSerialization);
|
|
}
|
|
if (!outsetDefault) {
|
|
aValue.AppendLiteral(" / ");
|
|
AppendValueToString(eCSSProperty_border_image_outset, aValue,
|
|
aSerialization);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool repeatDefault = data->HasDefaultBorderImageRepeat();
|
|
if (!repeatDefault) {
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(eCSSProperty_border_image_repeat, aValue,
|
|
aSerialization);
|
|
}
|
|
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, aSerialization) ||
|
|
!(aValue.Append(char16_t(' ')),
|
|
AppendValueToString(subprops[1], aValue, aSerialization)) ||
|
|
// Don't output a third value when it's -moz-use-text-color.
|
|
!(isMozUseTextColor ||
|
|
(aValue.Append(char16_t(' ')),
|
|
AppendValueToString(subprops[2], aValue, aSerialization)))) {
|
|
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, aSerialization);
|
|
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,
|
|
aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue,
|
|
aSerialization);
|
|
if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
|
|
repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue,
|
|
aSerialization);
|
|
}
|
|
aValue.Append(char16_t(' '));
|
|
attachment->mValue.AppendToString(eCSSProperty_background_attachment,
|
|
aValue, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
position->mValue.AppendToString(eCSSProperty_background_position,
|
|
aValue, aSerialization);
|
|
|
|
if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
|
|
size->mYValue.GetUnit() != eCSSUnit_Auto) {
|
|
aValue.Append(char16_t(' '));
|
|
aValue.Append(char16_t('/'));
|
|
aValue.Append(char16_t(' '));
|
|
size->mXValue.AppendToString(eCSSProperty_background_size, aValue,
|
|
aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
size->mYValue.AppendToString(eCSSProperty_background_size, aValue,
|
|
aSerialization);
|
|
}
|
|
|
|
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(char16_t(' '));
|
|
origin->mValue.AppendToString(eCSSProperty_background_origin, aValue,
|
|
aSerialization);
|
|
|
|
if (clip->mValue != origin->mValue) {
|
|
aValue.Append(char16_t(' '));
|
|
clip->mValue.AppendToString(eCSSProperty_background_clip, aValue,
|
|
aSerialization);
|
|
}
|
|
}
|
|
|
|
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(char16_t(','));
|
|
aValue.Append(char16_t(' '));
|
|
}
|
|
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(eCSSProperty_background_color, aValue,
|
|
aSerialization);
|
|
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 *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 (systemFont &&
|
|
systemFont->GetUnit() != eCSSUnit_None &&
|
|
systemFont->GetUnit() != eCSSUnit_Null) {
|
|
if (style->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 ||
|
|
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,
|
|
aSerialization);
|
|
} 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 ||
|
|
fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO ||
|
|
fontSynthesis->GetUnit() != eCSSUnit_Enumerated ||
|
|
fontSynthesis->GetIntValue() !=
|
|
(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) ||
|
|
fontVariantAlternates->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantEastAsian->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantLigatures->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantNumeric->GetUnit() != eCSSUnit_Normal ||
|
|
fontVariantPosition->GetUnit() != eCSSUnit_Normal) {
|
|
return;
|
|
}
|
|
|
|
// only a normal or small-caps values of font-variant-caps can
|
|
// be represented in the font shorthand
|
|
if (fontVariantCaps->GetUnit() != eCSSUnit_Normal &&
|
|
(fontVariantCaps->GetUnit() != eCSSUnit_Enumerated ||
|
|
fontVariantCaps->GetIntValue() != NS_FONT_VARIANT_CAPS_SMALLCAPS)) {
|
|
return;
|
|
}
|
|
|
|
if (style->GetUnit() != eCSSUnit_Enumerated ||
|
|
style->GetIntValue() != NS_FONT_STYLE_NORMAL) {
|
|
style->AppendToString(eCSSProperty_font_style, aValue,
|
|
aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
}
|
|
if (fontVariantCaps->GetUnit() != eCSSUnit_Normal) {
|
|
fontVariantCaps->AppendToString(eCSSProperty_font_variant_caps, aValue,
|
|
aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
}
|
|
if (weight->GetUnit() != eCSSUnit_Enumerated ||
|
|
weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
|
|
weight->AppendToString(eCSSProperty_font_weight, aValue,
|
|
aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
}
|
|
size->AppendToString(eCSSProperty_font_size, aValue, aSerialization);
|
|
if (lh->GetUnit() != eCSSUnit_Normal) {
|
|
aValue.Append(char16_t('/'));
|
|
lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization);
|
|
}
|
|
aValue.Append(char16_t(' '));
|
|
family->AppendToString(eCSSProperty_font_family, aValue,
|
|
aSerialization);
|
|
}
|
|
break;
|
|
}
|
|
case eCSSProperty_font_variant: {
|
|
const nsCSSProperty *subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
const nsCSSValue *fontVariantLigatures =
|
|
data->ValueFor(eCSSProperty_font_variant_ligatures);
|
|
|
|
// all subproperty values normal? system font?
|
|
bool normalLigs = true, normalNonLigs = true, systemFont = true,
|
|
hasSystem = false;
|
|
for (const nsCSSProperty *sp = subprops; *sp != eCSSProperty_UNKNOWN; sp++) {
|
|
const nsCSSValue *spVal = data->ValueFor(*sp);
|
|
bool isNormal = (spVal->GetUnit() == eCSSUnit_Normal);
|
|
if (*sp == eCSSProperty_font_variant_ligatures) {
|
|
normalLigs = normalLigs && isNormal;
|
|
} else {
|
|
normalNonLigs = normalNonLigs && isNormal;
|
|
}
|
|
bool isSystem = (spVal->GetUnit() == eCSSUnit_System_Font);
|
|
systemFont = systemFont && isSystem;
|
|
hasSystem = hasSystem || isSystem;
|
|
}
|
|
|
|
bool ligsNone =
|
|
fontVariantLigatures->GetUnit() == eCSSUnit_None;
|
|
|
|
// normal, none, or system font ==> single value
|
|
if ((normalLigs && normalNonLigs) ||
|
|
(normalNonLigs && ligsNone) ||
|
|
systemFont) {
|
|
fontVariantLigatures->AppendToString(eCSSProperty_font_variant_ligatures,
|
|
aValue,
|
|
aSerialization);
|
|
} else if (ligsNone || hasSystem) {
|
|
// ligatures none but other values are non-normal ==> empty
|
|
// at least one but not all values are system font ==> empty
|
|
return;
|
|
} else {
|
|
// iterate over and append non-normal values
|
|
bool appendSpace = false;
|
|
for (const nsCSSProperty *sp = subprops;
|
|
*sp != eCSSProperty_UNKNOWN; sp++) {
|
|
const nsCSSValue *spVal = data->ValueFor(*sp);
|
|
if (spVal && spVal->GetUnit() != eCSSUnit_Normal) {
|
|
if (appendSpace) {
|
|
aValue.Append(char16_t(' '));
|
|
} else {
|
|
appendSpace = true;
|
|
}
|
|
spVal->AppendToString(*sp, aValue, aSerialization);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case eCSSProperty_list_style:
|
|
if (AppendValueToString(eCSSProperty_list_style_position, aValue,
|
|
aSerialization)) {
|
|
aValue.Append(char16_t(' '));
|
|
}
|
|
if (AppendValueToString(eCSSProperty_list_style_image, aValue,
|
|
aSerialization)) {
|
|
aValue.Append(char16_t(' '));
|
|
}
|
|
AppendValueToString(eCSSProperty_list_style_type, aValue,
|
|
aSerialization);
|
|
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, aSerialization);
|
|
break;
|
|
}
|
|
case eCSSProperty_text_decoration: {
|
|
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());
|
|
|
|
AppendValueToString(eCSSProperty_text_decoration_line, aValue,
|
|
aSerialization);
|
|
if (decorationStyle->GetIntValue() !=
|
|
NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(eCSSProperty_text_decoration_style, aValue,
|
|
aSerialization);
|
|
}
|
|
if (decorationColor->GetUnit() != eCSSUnit_Enumerated ||
|
|
decorationColor->GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR) {
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(eCSSProperty_text_decoration_color, aValue,
|
|
aSerialization);
|
|
}
|
|
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,
|
|
aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue,
|
|
aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
|
|
aValue, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
del->mValue.AppendToString(eCSSProperty_transition_delay, aValue,
|
|
aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
} 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, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
dur->mValue.AppendToString(eCSSProperty_transition_duration,
|
|
aValue, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
|
|
aValue, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
del->mValue.AppendToString(eCSSProperty_transition_delay,
|
|
aValue, aSerialization);
|
|
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 = 8;
|
|
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, aSerialization);
|
|
lists[i] = lists[i]->mNext;
|
|
if (!lists[i]) {
|
|
done = true;
|
|
}
|
|
if (++i == numProps) {
|
|
break;
|
|
}
|
|
aValue.Append(char16_t(' '));
|
|
}
|
|
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, aSerialization);
|
|
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, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(subprops[1], aValue, aSerialization);
|
|
break;
|
|
}
|
|
case eCSSProperty_flex: {
|
|
// flex-grow, flex-shrink, flex-basis, separated by single space
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
|
|
AppendValueToString(subprops[0], aValue, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(subprops[1], aValue, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(subprops[2], aValue, aSerialization);
|
|
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, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(subprops[1], aValue, aSerialization);
|
|
break;
|
|
}
|
|
case eCSSProperty_grid_row:
|
|
case eCSSProperty_grid_column: {
|
|
// grid-{row,column}-start, grid-{row,column}-end, separated by a slash
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
|
|
"must have exactly two subproperties");
|
|
|
|
// TODO: should we simplify when possible?
|
|
AppendValueToString(subprops[0], aValue, aSerialization);
|
|
aValue.AppendLiteral(" / ");
|
|
AppendValueToString(subprops[1], aValue, aSerialization);
|
|
break;
|
|
}
|
|
case eCSSProperty_grid_area: {
|
|
const nsCSSProperty* subprops =
|
|
nsCSSProps::SubpropertyEntryFor(aProperty);
|
|
NS_ABORT_IF_FALSE(subprops[4] == eCSSProperty_UNKNOWN,
|
|
"must have exactly four subproperties");
|
|
|
|
// TODO: should we simplify when possible?
|
|
AppendValueToString(subprops[0], aValue, aSerialization);
|
|
aValue.AppendLiteral(" / ");
|
|
AppendValueToString(subprops[1], aValue, aSerialization);
|
|
aValue.AppendLiteral(" / ");
|
|
AppendValueToString(subprops[2], aValue, aSerialization);
|
|
aValue.AppendLiteral(" / ");
|
|
AppendValueToString(subprops[3], aValue, aSerialization);
|
|
break;
|
|
}
|
|
|
|
// This can express either grid-template-{areas,columns,rows}
|
|
// or grid-auto-{flow,columns,rows}, but not both.
|
|
case eCSSProperty_grid: {
|
|
const nsCSSValue& areasValue =
|
|
*data->ValueFor(eCSSProperty_grid_template_areas);
|
|
const nsCSSValue& columnsValue =
|
|
*data->ValueFor(eCSSProperty_grid_template_columns);
|
|
const nsCSSValue& rowsValue =
|
|
*data->ValueFor(eCSSProperty_grid_template_rows);
|
|
|
|
const nsCSSValue& autoFlowValue =
|
|
*data->ValueFor(eCSSProperty_grid_auto_flow);
|
|
const nsCSSValue& autoColumnsValue =
|
|
*data->ValueFor(eCSSProperty_grid_auto_columns);
|
|
const nsCSSValue& autoRowsValue =
|
|
*data->ValueFor(eCSSProperty_grid_auto_rows);
|
|
|
|
if (areasValue.GetUnit() == eCSSUnit_None &&
|
|
columnsValue.GetUnit() == eCSSUnit_None &&
|
|
rowsValue.GetUnit() == eCSSUnit_None) {
|
|
AppendValueToString(eCSSProperty_grid_auto_flow,
|
|
aValue, aSerialization);
|
|
aValue.Append(char16_t(' '));
|
|
AppendValueToString(eCSSProperty_grid_auto_columns,
|
|
aValue, aSerialization);
|
|
aValue.AppendLiteral(" / ");
|
|
AppendValueToString(eCSSProperty_grid_auto_rows,
|
|
aValue, aSerialization);
|
|
break;
|
|
} else if (!(autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
|
|
autoFlowValue.GetIntValue() == NS_STYLE_GRID_AUTO_FLOW_ROW &&
|
|
autoColumnsValue.GetUnit() == eCSSUnit_Auto &&
|
|
autoRowsValue.GetUnit() == eCSSUnit_Auto)) {
|
|
// Not serializable, bail.
|
|
return;
|
|
}
|
|
// Fall through to eCSSProperty_grid_template
|
|
}
|
|
case eCSSProperty_grid_template: {
|
|
const nsCSSValue& areasValue =
|
|
*data->ValueFor(eCSSProperty_grid_template_areas);
|
|
const nsCSSValue& columnsValue =
|
|
*data->ValueFor(eCSSProperty_grid_template_columns);
|
|
const nsCSSValue& rowsValue =
|
|
*data->ValueFor(eCSSProperty_grid_template_rows);
|
|
if (areasValue.GetUnit() == eCSSUnit_None) {
|
|
AppendValueToString(eCSSProperty_grid_template_columns,
|
|
aValue, aSerialization);
|
|
aValue.AppendLiteral(" / ");
|
|
AppendValueToString(eCSSProperty_grid_template_rows,
|
|
aValue, aSerialization);
|
|
break;
|
|
}
|
|
if (columnsValue.GetUnit() == eCSSUnit_List ||
|
|
columnsValue.GetUnit() == eCSSUnit_ListDep) {
|
|
const nsCSSValueList* columnsItem = columnsValue.GetListValue();
|
|
if (columnsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
|
|
columnsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
|
|
// We have "grid-template-areas:[something]; grid-template-columns:subgrid"
|
|
// which isn't a value that the shorthand can express. Bail.
|
|
return;
|
|
}
|
|
}
|
|
if (rowsValue.GetUnit() != eCSSUnit_List &&
|
|
rowsValue.GetUnit() != eCSSUnit_ListDep) {
|
|
// We have "grid-template-areas:[something]; grid-template-rows:none"
|
|
// which isn't a value that the shorthand can express. Bail.
|
|
return;
|
|
}
|
|
const nsCSSValueList* rowsItem = rowsValue.GetListValue();
|
|
if (rowsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
|
|
rowsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
|
|
// We have "grid-template-areas:[something]; grid-template-rows:subgrid"
|
|
// which isn't a value that the shorthand can express. Bail.
|
|
return;
|
|
}
|
|
const GridTemplateAreasValue* areas = areasValue.GetGridTemplateAreas();
|
|
uint32_t nRowItems = 0;
|
|
while (rowsItem) {
|
|
nRowItems++;
|
|
rowsItem = rowsItem->mNext;
|
|
}
|
|
MOZ_ASSERT(nRowItems % 2 == 1, "expected an odd number of items");
|
|
if ((nRowItems - 1) / 2 != areas->NRows()) {
|
|
// Not serializable, bail.
|
|
return;
|
|
}
|
|
if (columnsValue.GetUnit() != eCSSUnit_None) {
|
|
AppendValueToString(eCSSProperty_grid_template_columns,
|
|
aValue, aSerialization);
|
|
aValue.AppendLiteral(" / ");
|
|
}
|
|
rowsItem = rowsValue.GetListValue();
|
|
uint32_t row = 0;
|
|
for (;;) {
|
|
bool addSpaceSeparator = true;
|
|
nsCSSUnit unit = rowsItem->mValue.GetUnit();
|
|
|
|
if (unit == eCSSUnit_Null) {
|
|
// Empty or omitted <line-names>. Serializes to nothing.
|
|
addSpaceSeparator = false; // Avoid a double space.
|
|
|
|
} else if (unit == eCSSUnit_List || unit == eCSSUnit_ListDep) {
|
|
// Non-empty <line-names>
|
|
aValue.Append('(');
|
|
rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
|
|
aValue, aSerialization);
|
|
aValue.Append(')');
|
|
|
|
} else {
|
|
nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[row++], aValue);
|
|
aValue.Append(char16_t(' '));
|
|
|
|
// <track-size>
|
|
rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
|
|
aValue, aSerialization);
|
|
if (rowsItem->mNext &&
|
|
rowsItem->mNext->mValue.GetUnit() == eCSSUnit_Null &&
|
|
!rowsItem->mNext->mNext) {
|
|
// Break out of the loop early to avoid a trailing space.
|
|
break;
|
|
}
|
|
}
|
|
|
|
rowsItem = rowsItem->mNext;
|
|
if (!rowsItem) {
|
|
break;
|
|
}
|
|
|
|
if (addSpaceSeparator) {
|
|
aValue.Append(char16_t(' '));
|
|
}
|
|
}
|
|
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, aSerialization);
|
|
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;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Declaration::GetValueIsImportant(const nsAString& aProperty) const
|
|
{
|
|
nsCSSProperty propID =
|
|
nsCSSProps::LookupProperty(aProperty, nsCSSProps::eIgnoreEnabledState);
|
|
if (propID == eCSSProperty_UNKNOWN) {
|
|
return false;
|
|
}
|
|
if (propID == eCSSPropertyExtra_variable) {
|
|
const nsSubstring& variableName =
|
|
Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);
|
|
return GetVariableValueIsImportant(variableName);
|
|
}
|
|
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, nsCSSValue::eNormalized);
|
|
else
|
|
aResult.Append(aValue);
|
|
if (GetValueIsImportant(aProperty)) {
|
|
aResult.AppendLiteral(" ! important");
|
|
}
|
|
aResult.AppendLiteral("; ");
|
|
}
|
|
|
|
void
|
|
Declaration::AppendVariableAndValueToString(const nsAString& aName,
|
|
nsAString& aResult) const
|
|
{
|
|
aResult.AppendLiteral("--");
|
|
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;
|
|
|
|
case CSSVariableDeclarations::eUnset:
|
|
aResult.AppendLiteral("unset");
|
|
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;
|
|
|
|
GetValue(shorthand, value);
|
|
|
|
// in the system font case, skip over font-variant shorthand, since all
|
|
// subproperties are already dealt with via the font shorthand
|
|
if (shorthand == eCSSProperty_font_variant &&
|
|
value.EqualsLiteral("-moz-use-system-font")) {
|
|
continue;
|
|
}
|
|
|
|
// If GetValue gives us a non-empty string back, we can use that
|
|
// value; otherwise it's not possible to use this shorthand.
|
|
if (!value.IsEmpty()) {
|
|
AppendPropertyAndValueToString(shorthand, value, aString);
|
|
shorthandsUsed.AppendElement(shorthand);
|
|
doneProperty = true;
|
|
break;
|
|
}
|
|
|
|
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,
|
|
nsCSSValue::eNormalized);
|
|
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;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
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
|
|
{
|
|
nsAutoCString str;
|
|
for (int32_t index = aIndent; --index >= 0; ) {
|
|
str.AppendLiteral(" ");
|
|
}
|
|
|
|
str.AppendLiteral("{ ");
|
|
nsAutoString s;
|
|
ToString(s);
|
|
AppendUTF16toUTF8(s, str);
|
|
str.AppendLiteral("}\n");
|
|
fprintf_stderr(out, "%s", str.get());
|
|
}
|
|
#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;
|
|
|
|
case CSSVariableDeclarations::eUnset:
|
|
aValue.AppendLiteral("unset");
|
|
break;
|
|
|
|
default:
|
|
MOZ_ASSERT(false, "unexpected variable value type");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Declaration::AddVariableDeclaration(const nsAString& aName,
|
|
CSSVariableDeclarations::Type aType,
|
|
const nsString& aValue,
|
|
bool aIsImportant,
|
|
bool aOverrideImportant)
|
|
{
|
|
MOZ_ASSERT(IsMutable());
|
|
|
|
nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
|
|
if (index == nsTArray<nsString>::NoIndex) {
|
|
index = mVariableOrder.Length();
|
|
mVariableOrder.AppendElement(aName);
|
|
}
|
|
|
|
if (!aIsImportant && !aOverrideImportant &&
|
|
mImportantVariables && mImportantVariables->Has(aName)) {
|
|
return;
|
|
}
|
|
|
|
CSSVariableDeclarations* variables;
|
|
if (aIsImportant) {
|
|
if (mVariables) {
|
|
mVariables->Remove(aName);
|
|
}
|
|
if (!mImportantVariables) {
|
|
mImportantVariables = new CSSVariableDeclarations;
|
|
}
|
|
variables = mImportantVariables;
|
|
} else {
|
|
if (mImportantVariables) {
|
|
mImportantVariables->Remove(aName);
|
|
}
|
|
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;
|
|
|
|
case CSSVariableDeclarations::eUnset:
|
|
MOZ_ASSERT(aValue.IsEmpty());
|
|
variables->PutUnset(aName);
|
|
break;
|
|
|
|
default:
|
|
MOZ_ASSERT(false, "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
|