Bumping manifests a=b2g-bump
[gecko.git] / layout / style / Declaration.cpp
blob3685b7a8da0ebfb04fbeb4c5ea35af86a3ca269a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * representation of a declaration block (or style attribute) in a CSS
8 * stylesheet
9 */
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/css/Declaration.h"
15 #include "nsPrintfCString.h"
16 #include "gfxFontConstants.h"
17 #include "nsStyleUtil.h"
19 namespace mozilla {
20 namespace css {
22 Declaration::Declaration()
23 : mImmutable(false)
25 MOZ_COUNT_CTOR(mozilla::css::Declaration);
28 Declaration::Declaration(const Declaration& aCopy)
29 : mOrder(aCopy.mOrder),
30 mVariableOrder(aCopy.mVariableOrder),
31 mData(aCopy.mData ? aCopy.mData->Clone() : nullptr),
32 mImportantData(aCopy.mImportantData ?
33 aCopy.mImportantData->Clone() : nullptr),
34 mVariables(aCopy.mVariables ?
35 new CSSVariableDeclarations(*aCopy.mVariables) :
36 nullptr),
37 mImportantVariables(aCopy.mImportantVariables ?
38 new CSSVariableDeclarations(*aCopy.mImportantVariables) :
39 nullptr),
40 mImmutable(false)
42 MOZ_COUNT_CTOR(mozilla::css::Declaration);
45 Declaration::~Declaration()
47 MOZ_COUNT_DTOR(mozilla::css::Declaration);
50 void
51 Declaration::ValueAppended(nsCSSProperty aProperty)
53 NS_ABORT_IF_FALSE(!mData && !mImportantData,
54 "should only be called while expanded");
55 NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty),
56 "shorthands forbidden");
57 // order IS important for CSS, so remove and add to the end
58 mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
59 mOrder.AppendElement(static_cast<uint32_t>(aProperty));
62 void
63 Declaration::RemoveProperty(nsCSSProperty aProperty)
65 MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT);
67 nsCSSExpandedDataBlock data;
68 ExpandTo(&data);
69 NS_ABORT_IF_FALSE(!mData && !mImportantData, "Expand didn't null things out");
71 if (nsCSSProps::IsShorthand(aProperty)) {
72 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
73 data.ClearLonghandProperty(*p);
74 mOrder.RemoveElement(static_cast<uint32_t>(*p));
76 } else {
77 data.ClearLonghandProperty(aProperty);
78 mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
81 CompressFrom(&data);
84 bool
85 Declaration::HasProperty(nsCSSProperty aProperty) const
87 NS_ABORT_IF_FALSE(0 <= aProperty &&
88 aProperty < eCSSProperty_COUNT_no_shorthands,
89 "property ID out of range");
91 nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
92 ? mImportantData : mData;
93 const nsCSSValue *val = data->ValueFor(aProperty);
94 return !!val;
97 bool
98 Declaration::AppendValueToString(nsCSSProperty aProperty,
99 nsAString& aResult,
100 nsCSSValue::Serialization aSerialization) const
102 NS_ABORT_IF_FALSE(0 <= aProperty &&
103 aProperty < eCSSProperty_COUNT_no_shorthands,
104 "property ID out of range");
106 nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
107 ? mImportantData : mData;
108 const nsCSSValue *val = data->ValueFor(aProperty);
109 if (!val) {
110 return false;
113 val->AppendToString(aProperty, aResult, aSerialization);
114 return true;
117 void
118 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
120 GetValue(aProperty, aValue, nsCSSValue::eNormalized);
123 void
124 Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const
126 GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified);
129 void
130 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue,
131 nsCSSValue::Serialization aSerialization) const
133 aValue.Truncate(0);
135 // simple properties are easy.
136 if (!nsCSSProps::IsShorthand(aProperty)) {
137 AppendValueToString(aProperty, aValue, aSerialization);
138 return;
141 // DOM Level 2 Style says (when describing CSS2Properties, although
142 // not CSSStyleDeclaration.getPropertyValue):
143 // However, if there is no shorthand declaration that could be added
144 // to the ruleset without changing in any way the rules already
145 // declared in the ruleset (i.e., by adding longhand rules that were
146 // previously not declared in the ruleset), then the empty string
147 // should be returned for the shorthand property.
148 // This means we need to check a number of cases:
149 // (1) Since a shorthand sets all sub-properties, if some of its
150 // subproperties were not specified, we must return the empty
151 // string.
152 // (2) Since 'inherit', 'initial' and 'unset' can only be specified
153 // as the values for entire properties, we need to return the
154 // empty string if some but not all of the subproperties have one
155 // of those values.
156 // (3) Since a single value only makes sense with or without
157 // !important, we return the empty string if some values are
158 // !important and some are not.
159 // Since we're doing this check for 'inherit' and 'initial' up front,
160 // we can also simplify the property serialization code by serializing
161 // those values up front as well.
163 // Additionally, if a shorthand property was set using a value with a
164 // variable reference and none of its component longhand properties were
165 // then overridden on the declaration, we return the token stream
166 // assigned to the shorthand.
167 const nsCSSValue* tokenStream = nullptr;
168 uint32_t totalCount = 0, importantCount = 0,
169 initialCount = 0, inheritCount = 0, unsetCount = 0,
170 matchingTokenStreamCount = 0, nonMatchingTokenStreamCount = 0;
171 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
172 if (*p == eCSSProperty__x_system_font ||
173 nsCSSProps::PropHasFlags(*p, CSS_PROPERTY_DIRECTIONAL_SOURCE)) {
174 // The system-font subproperty and the *-source properties don't count.
175 continue;
177 ++totalCount;
178 const nsCSSValue *val = mData->ValueFor(*p);
179 NS_ABORT_IF_FALSE(!val || !mImportantData || !mImportantData->ValueFor(*p),
180 "can't be in both blocks");
181 if (!val && mImportantData) {
182 ++importantCount;
183 val = mImportantData->ValueFor(*p);
185 if (!val) {
186 // Case (1) above: some subproperties not specified.
187 return;
189 if (val->GetUnit() == eCSSUnit_Inherit) {
190 ++inheritCount;
191 } else if (val->GetUnit() == eCSSUnit_Initial) {
192 ++initialCount;
193 } else if (val->GetUnit() == eCSSUnit_Unset) {
194 ++unsetCount;
195 } else if (val->GetUnit() == eCSSUnit_TokenStream) {
196 if (val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) {
197 tokenStream = val;
198 ++matchingTokenStreamCount;
199 } else {
200 ++nonMatchingTokenStreamCount;
204 if (importantCount != 0 && importantCount != totalCount) {
205 // Case (3), no consistent importance.
206 return;
208 if (initialCount == totalCount) {
209 // Simplify serialization below by serializing initial up-front.
210 nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue,
211 nsCSSValue::eNormalized);
212 return;
214 if (inheritCount == totalCount) {
215 // Simplify serialization below by serializing inherit up-front.
216 nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue,
217 nsCSSValue::eNormalized);
218 return;
220 if (unsetCount == totalCount) {
221 // Simplify serialization below by serializing unset up-front.
222 nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue,
223 nsCSSValue::eNormalized);
224 return;
226 if (initialCount != 0 || inheritCount != 0 ||
227 unsetCount != 0 || nonMatchingTokenStreamCount != 0) {
228 // Case (2): partially initial, inherit, unset or token stream.
229 return;
231 if (tokenStream) {
232 if (matchingTokenStreamCount == totalCount) {
233 // Shorthand was specified using variable references and all of its
234 // longhand components were set by the shorthand.
235 aValue.Append(tokenStream->GetTokenStreamValue()->mTokenStream);
236 } else {
237 // In all other cases, serialize to the empty string.
239 return;
242 nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
243 switch (aProperty) {
244 case eCSSProperty_margin:
245 case eCSSProperty_padding:
246 case eCSSProperty_border_color:
247 case eCSSProperty_border_style:
248 case eCSSProperty_border_width: {
249 const nsCSSProperty* subprops =
250 nsCSSProps::SubpropertyEntryFor(aProperty);
251 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
252 kNotFound, "first subprop must be top");
253 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
254 kNotFound, "second subprop must be right");
255 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
256 kNotFound, "third subprop must be bottom");
257 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
258 kNotFound, "fourth subprop must be left");
259 const nsCSSValue* vals[4] = {
260 data->ValueFor(subprops[0]),
261 data->ValueFor(subprops[1]),
262 data->ValueFor(subprops[2]),
263 data->ValueFor(subprops[3])
265 nsCSSValue::AppendSidesShorthandToString(subprops, vals, aValue,
266 aSerialization);
267 break;
269 case eCSSProperty_border_radius:
270 case eCSSProperty__moz_outline_radius: {
271 const nsCSSProperty* subprops =
272 nsCSSProps::SubpropertyEntryFor(aProperty);
273 const nsCSSValue* vals[4] = {
274 data->ValueFor(subprops[0]),
275 data->ValueFor(subprops[1]),
276 data->ValueFor(subprops[2]),
277 data->ValueFor(subprops[3])
279 nsCSSValue::AppendBasicShapeRadiusToString(subprops, vals, aValue,
280 aSerialization);
281 break;
283 case eCSSProperty_border_image: {
284 // Even though there are some cases where we could omit
285 // 'border-image-source' (when it's none), it's probably not a
286 // good idea since it's likely to be confusing. It would also
287 // require adding the extra check that we serialize *something*.
288 AppendValueToString(eCSSProperty_border_image_source, aValue,
289 aSerialization);
291 bool sliceDefault = data->HasDefaultBorderImageSlice();
292 bool widthDefault = data->HasDefaultBorderImageWidth();
293 bool outsetDefault = data->HasDefaultBorderImageOutset();
295 if (!sliceDefault || !widthDefault || !outsetDefault) {
296 aValue.Append(char16_t(' '));
297 AppendValueToString(eCSSProperty_border_image_slice, aValue,
298 aSerialization);
299 if (!widthDefault || !outsetDefault) {
300 aValue.AppendLiteral(" /");
301 if (!widthDefault) {
302 aValue.Append(char16_t(' '));
303 AppendValueToString(eCSSProperty_border_image_width, aValue,
304 aSerialization);
306 if (!outsetDefault) {
307 aValue.AppendLiteral(" / ");
308 AppendValueToString(eCSSProperty_border_image_outset, aValue,
309 aSerialization);
314 bool repeatDefault = data->HasDefaultBorderImageRepeat();
315 if (!repeatDefault) {
316 aValue.Append(char16_t(' '));
317 AppendValueToString(eCSSProperty_border_image_repeat, aValue,
318 aSerialization);
320 break;
322 case eCSSProperty_border: {
323 // If we have a non-default value for any of the properties that
324 // this shorthand sets but cannot specify, we have to return the
325 // empty string.
326 if (data->ValueFor(eCSSProperty_border_image_source)->GetUnit() !=
327 eCSSUnit_None ||
328 !data->HasDefaultBorderImageSlice() ||
329 !data->HasDefaultBorderImageWidth() ||
330 !data->HasDefaultBorderImageOutset() ||
331 !data->HasDefaultBorderImageRepeat() ||
332 data->ValueFor(eCSSProperty_border_top_colors)->GetUnit() !=
333 eCSSUnit_None ||
334 data->ValueFor(eCSSProperty_border_right_colors)->GetUnit() !=
335 eCSSUnit_None ||
336 data->ValueFor(eCSSProperty_border_bottom_colors)->GetUnit() !=
337 eCSSUnit_None ||
338 data->ValueFor(eCSSProperty_border_left_colors)->GetUnit() !=
339 eCSSUnit_None) {
340 break;
343 const nsCSSProperty* subproptables[3] = {
344 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
345 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
346 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
348 bool match = true;
349 for (const nsCSSProperty** subprops = subproptables,
350 **subprops_end = ArrayEnd(subproptables);
351 subprops < subprops_end; ++subprops) {
352 // Check only the first four subprops in each table, since the
353 // others are extras for dimensional box properties.
354 const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
355 for (int32_t side = 1; side < 4; ++side) {
356 const nsCSSValue *otherSide =
357 data->ValueFor((*subprops)[side]);
358 if (*firstSide != *otherSide)
359 match = false;
362 if (!match) {
363 // We can't express what we have in the border shorthand
364 break;
366 // tweak aProperty and fall through
367 aProperty = eCSSProperty_border_top;
369 case eCSSProperty_border_top:
370 case eCSSProperty_border_right:
371 case eCSSProperty_border_bottom:
372 case eCSSProperty_border_left:
373 case eCSSProperty_border_start:
374 case eCSSProperty_border_end:
375 case eCSSProperty__moz_column_rule:
376 case eCSSProperty_outline: {
377 const nsCSSProperty* subprops =
378 nsCSSProps::SubpropertyEntryFor(aProperty);
379 NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
380 NS_LITERAL_CSTRING("-color")) ||
381 StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
382 NS_LITERAL_CSTRING("-color-value")),
383 "third subprop must be the color property");
384 const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
385 bool isMozUseTextColor =
386 colorValue->GetUnit() == eCSSUnit_Enumerated &&
387 colorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR;
388 if (!AppendValueToString(subprops[0], aValue, aSerialization) ||
389 !(aValue.Append(char16_t(' ')),
390 AppendValueToString(subprops[1], aValue, aSerialization)) ||
391 // Don't output a third value when it's -moz-use-text-color.
392 !(isMozUseTextColor ||
393 (aValue.Append(char16_t(' ')),
394 AppendValueToString(subprops[2], aValue, aSerialization)))) {
395 aValue.Truncate();
397 break;
399 case eCSSProperty_margin_left:
400 case eCSSProperty_margin_right:
401 case eCSSProperty_margin_start:
402 case eCSSProperty_margin_end:
403 case eCSSProperty_padding_left:
404 case eCSSProperty_padding_right:
405 case eCSSProperty_padding_start:
406 case eCSSProperty_padding_end:
407 case eCSSProperty_border_left_color:
408 case eCSSProperty_border_left_style:
409 case eCSSProperty_border_left_width:
410 case eCSSProperty_border_right_color:
411 case eCSSProperty_border_right_style:
412 case eCSSProperty_border_right_width:
413 case eCSSProperty_border_start_color:
414 case eCSSProperty_border_start_style:
415 case eCSSProperty_border_start_width:
416 case eCSSProperty_border_end_color:
417 case eCSSProperty_border_end_style:
418 case eCSSProperty_border_end_width: {
419 const nsCSSProperty* subprops =
420 nsCSSProps::SubpropertyEntryFor(aProperty);
421 NS_ABORT_IF_FALSE(subprops[3] == eCSSProperty_UNKNOWN,
422 "not box property with physical vs. logical cascading");
423 AppendValueToString(subprops[0], aValue, aSerialization);
424 break;
426 case eCSSProperty_background: {
427 // We know from above that all subproperties were specified.
428 // However, we still can't represent that in the shorthand unless
429 // they're all lists of the same length. So if they're different
430 // lengths, we need to bail out.
431 // We also need to bail out if an item has background-clip and
432 // background-origin that are different and not the default
433 // values. (We omit them if they're both default.)
434 const nsCSSValueList *image =
435 data->ValueFor(eCSSProperty_background_image)->
436 GetListValue();
437 const nsCSSValuePairList *repeat =
438 data->ValueFor(eCSSProperty_background_repeat)->
439 GetPairListValue();
440 const nsCSSValueList *attachment =
441 data->ValueFor(eCSSProperty_background_attachment)->
442 GetListValue();
443 const nsCSSValueList *position =
444 data->ValueFor(eCSSProperty_background_position)->
445 GetListValue();
446 const nsCSSValueList *clip =
447 data->ValueFor(eCSSProperty_background_clip)->
448 GetListValue();
449 const nsCSSValueList *origin =
450 data->ValueFor(eCSSProperty_background_origin)->
451 GetListValue();
452 const nsCSSValuePairList *size =
453 data->ValueFor(eCSSProperty_background_size)->
454 GetPairListValue();
455 for (;;) {
456 image->mValue.AppendToString(eCSSProperty_background_image, aValue,
457 aSerialization);
458 aValue.Append(char16_t(' '));
459 repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue,
460 aSerialization);
461 if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
462 repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue,
463 aSerialization);
465 aValue.Append(char16_t(' '));
466 attachment->mValue.AppendToString(eCSSProperty_background_attachment,
467 aValue, aSerialization);
468 aValue.Append(char16_t(' '));
469 position->mValue.AppendToString(eCSSProperty_background_position,
470 aValue, aSerialization);
472 if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
473 size->mYValue.GetUnit() != eCSSUnit_Auto) {
474 aValue.Append(char16_t(' '));
475 aValue.Append(char16_t('/'));
476 aValue.Append(char16_t(' '));
477 size->mXValue.AppendToString(eCSSProperty_background_size, aValue,
478 aSerialization);
479 aValue.Append(char16_t(' '));
480 size->mYValue.AppendToString(eCSSProperty_background_size, aValue,
481 aSerialization);
484 NS_ABORT_IF_FALSE(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
485 origin->mValue.GetUnit() == eCSSUnit_Enumerated,
486 "should not have inherit/initial within list");
488 if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
489 origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
490 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
491 eCSSProperty_background_origin] ==
492 nsCSSProps::kBackgroundOriginKTable);
493 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
494 eCSSProperty_background_clip] ==
495 nsCSSProps::kBackgroundOriginKTable);
496 static_assert(NS_STYLE_BG_CLIP_BORDER ==
497 NS_STYLE_BG_ORIGIN_BORDER &&
498 NS_STYLE_BG_CLIP_PADDING ==
499 NS_STYLE_BG_ORIGIN_PADDING &&
500 NS_STYLE_BG_CLIP_CONTENT ==
501 NS_STYLE_BG_ORIGIN_CONTENT,
502 "bg-clip and bg-origin style constants must agree");
503 aValue.Append(char16_t(' '));
504 origin->mValue.AppendToString(eCSSProperty_background_origin, aValue,
505 aSerialization);
507 if (clip->mValue != origin->mValue) {
508 aValue.Append(char16_t(' '));
509 clip->mValue.AppendToString(eCSSProperty_background_clip, aValue,
510 aSerialization);
514 image = image->mNext;
515 repeat = repeat->mNext;
516 attachment = attachment->mNext;
517 position = position->mNext;
518 clip = clip->mNext;
519 origin = origin->mNext;
520 size = size->mNext;
522 if (!image) {
523 if (repeat || attachment || position || clip || origin || size) {
524 // Uneven length lists, so can't be serialized as shorthand.
525 aValue.Truncate();
526 return;
528 break;
530 if (!repeat || !attachment || !position || !clip || !origin || !size) {
531 // Uneven length lists, so can't be serialized as shorthand.
532 aValue.Truncate();
533 return;
535 aValue.Append(char16_t(','));
536 aValue.Append(char16_t(' '));
539 aValue.Append(char16_t(' '));
540 AppendValueToString(eCSSProperty_background_color, aValue,
541 aSerialization);
542 break;
544 case eCSSProperty_font: {
545 // systemFont might not be present; other values are guaranteed to be
546 // available based on the shorthand check at the beginning of the
547 // function, as long as the prop is enabled
548 const nsCSSValue *systemFont =
549 data->ValueFor(eCSSProperty__x_system_font);
550 const nsCSSValue *style =
551 data->ValueFor(eCSSProperty_font_style);
552 const nsCSSValue *weight =
553 data->ValueFor(eCSSProperty_font_weight);
554 const nsCSSValue *size =
555 data->ValueFor(eCSSProperty_font_size);
556 const nsCSSValue *lh =
557 data->ValueFor(eCSSProperty_line_height);
558 const nsCSSValue *family =
559 data->ValueFor(eCSSProperty_font_family);
560 const nsCSSValue *stretch =
561 data->ValueFor(eCSSProperty_font_stretch);
562 const nsCSSValue *sizeAdjust =
563 data->ValueFor(eCSSProperty_font_size_adjust);
564 const nsCSSValue *featureSettings =
565 data->ValueFor(eCSSProperty_font_feature_settings);
566 const nsCSSValue *languageOverride =
567 data->ValueFor(eCSSProperty_font_language_override);
568 const nsCSSValue *fontKerning =
569 data->ValueFor(eCSSProperty_font_kerning);
570 const nsCSSValue *fontSynthesis =
571 data->ValueFor(eCSSProperty_font_synthesis);
572 const nsCSSValue *fontVariantAlternates =
573 data->ValueFor(eCSSProperty_font_variant_alternates);
574 const nsCSSValue *fontVariantCaps =
575 data->ValueFor(eCSSProperty_font_variant_caps);
576 const nsCSSValue *fontVariantEastAsian =
577 data->ValueFor(eCSSProperty_font_variant_east_asian);
578 const nsCSSValue *fontVariantLigatures =
579 data->ValueFor(eCSSProperty_font_variant_ligatures);
580 const nsCSSValue *fontVariantNumeric =
581 data->ValueFor(eCSSProperty_font_variant_numeric);
582 const nsCSSValue *fontVariantPosition =
583 data->ValueFor(eCSSProperty_font_variant_position);
585 if (systemFont &&
586 systemFont->GetUnit() != eCSSUnit_None &&
587 systemFont->GetUnit() != eCSSUnit_Null) {
588 if (style->GetUnit() != eCSSUnit_System_Font ||
589 weight->GetUnit() != eCSSUnit_System_Font ||
590 size->GetUnit() != eCSSUnit_System_Font ||
591 lh->GetUnit() != eCSSUnit_System_Font ||
592 family->GetUnit() != eCSSUnit_System_Font ||
593 stretch->GetUnit() != eCSSUnit_System_Font ||
594 sizeAdjust->GetUnit() != eCSSUnit_System_Font ||
595 featureSettings->GetUnit() != eCSSUnit_System_Font ||
596 languageOverride->GetUnit() != eCSSUnit_System_Font ||
597 fontKerning->GetUnit() != eCSSUnit_System_Font ||
598 fontSynthesis->GetUnit() != eCSSUnit_System_Font ||
599 fontVariantAlternates->GetUnit() != eCSSUnit_System_Font ||
600 fontVariantCaps->GetUnit() != eCSSUnit_System_Font ||
601 fontVariantEastAsian->GetUnit() != eCSSUnit_System_Font ||
602 fontVariantLigatures->GetUnit() != eCSSUnit_System_Font ||
603 fontVariantNumeric->GetUnit() != eCSSUnit_System_Font ||
604 fontVariantPosition->GetUnit() != eCSSUnit_System_Font) {
605 // This can't be represented as a shorthand.
606 return;
608 systemFont->AppendToString(eCSSProperty__x_system_font, aValue,
609 aSerialization);
610 } else {
611 // properties reset by this shorthand property to their
612 // initial values but not represented in its syntax
613 if (stretch->GetUnit() != eCSSUnit_Enumerated ||
614 stretch->GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
615 sizeAdjust->GetUnit() != eCSSUnit_None ||
616 featureSettings->GetUnit() != eCSSUnit_Normal ||
617 languageOverride->GetUnit() != eCSSUnit_Normal ||
618 fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO ||
619 fontSynthesis->GetUnit() != eCSSUnit_Enumerated ||
620 fontSynthesis->GetIntValue() !=
621 (NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) ||
622 fontVariantAlternates->GetUnit() != eCSSUnit_Normal ||
623 fontVariantEastAsian->GetUnit() != eCSSUnit_Normal ||
624 fontVariantLigatures->GetUnit() != eCSSUnit_Normal ||
625 fontVariantNumeric->GetUnit() != eCSSUnit_Normal ||
626 fontVariantPosition->GetUnit() != eCSSUnit_Normal) {
627 return;
630 // only a normal or small-caps values of font-variant-caps can
631 // be represented in the font shorthand
632 if (fontVariantCaps->GetUnit() != eCSSUnit_Normal &&
633 (fontVariantCaps->GetUnit() != eCSSUnit_Enumerated ||
634 fontVariantCaps->GetIntValue() != NS_FONT_VARIANT_CAPS_SMALLCAPS)) {
635 return;
638 if (style->GetUnit() != eCSSUnit_Enumerated ||
639 style->GetIntValue() != NS_FONT_STYLE_NORMAL) {
640 style->AppendToString(eCSSProperty_font_style, aValue,
641 aSerialization);
642 aValue.Append(char16_t(' '));
644 if (fontVariantCaps->GetUnit() != eCSSUnit_Normal) {
645 fontVariantCaps->AppendToString(eCSSProperty_font_variant_caps, aValue,
646 aSerialization);
647 aValue.Append(char16_t(' '));
649 if (weight->GetUnit() != eCSSUnit_Enumerated ||
650 weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
651 weight->AppendToString(eCSSProperty_font_weight, aValue,
652 aSerialization);
653 aValue.Append(char16_t(' '));
655 size->AppendToString(eCSSProperty_font_size, aValue, aSerialization);
656 if (lh->GetUnit() != eCSSUnit_Normal) {
657 aValue.Append(char16_t('/'));
658 lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization);
660 aValue.Append(char16_t(' '));
661 family->AppendToString(eCSSProperty_font_family, aValue,
662 aSerialization);
664 break;
666 case eCSSProperty_font_variant: {
667 const nsCSSProperty *subprops =
668 nsCSSProps::SubpropertyEntryFor(aProperty);
669 const nsCSSValue *fontVariantLigatures =
670 data->ValueFor(eCSSProperty_font_variant_ligatures);
672 // all subproperty values normal? system font?
673 bool normalLigs = true, normalNonLigs = true, systemFont = true,
674 hasSystem = false;
675 for (const nsCSSProperty *sp = subprops; *sp != eCSSProperty_UNKNOWN; sp++) {
676 const nsCSSValue *spVal = data->ValueFor(*sp);
677 bool isNormal = (spVal->GetUnit() == eCSSUnit_Normal);
678 if (*sp == eCSSProperty_font_variant_ligatures) {
679 normalLigs = normalLigs && isNormal;
680 } else {
681 normalNonLigs = normalNonLigs && isNormal;
683 bool isSystem = (spVal->GetUnit() == eCSSUnit_System_Font);
684 systemFont = systemFont && isSystem;
685 hasSystem = hasSystem || isSystem;
688 bool ligsNone =
689 fontVariantLigatures->GetUnit() == eCSSUnit_None;
691 // normal, none, or system font ==> single value
692 if ((normalLigs && normalNonLigs) ||
693 (normalNonLigs && ligsNone) ||
694 systemFont) {
695 fontVariantLigatures->AppendToString(eCSSProperty_font_variant_ligatures,
696 aValue,
697 aSerialization);
698 } else if (ligsNone || hasSystem) {
699 // ligatures none but other values are non-normal ==> empty
700 // at least one but not all values are system font ==> empty
701 return;
702 } else {
703 // iterate over and append non-normal values
704 bool appendSpace = false;
705 for (const nsCSSProperty *sp = subprops;
706 *sp != eCSSProperty_UNKNOWN; sp++) {
707 const nsCSSValue *spVal = data->ValueFor(*sp);
708 if (spVal && spVal->GetUnit() != eCSSUnit_Normal) {
709 if (appendSpace) {
710 aValue.Append(char16_t(' '));
711 } else {
712 appendSpace = true;
714 spVal->AppendToString(*sp, aValue, aSerialization);
718 break;
720 case eCSSProperty_list_style:
721 if (AppendValueToString(eCSSProperty_list_style_position, aValue,
722 aSerialization)) {
723 aValue.Append(char16_t(' '));
725 if (AppendValueToString(eCSSProperty_list_style_image, aValue,
726 aSerialization)) {
727 aValue.Append(char16_t(' '));
729 AppendValueToString(eCSSProperty_list_style_type, aValue,
730 aSerialization);
731 break;
732 case eCSSProperty_overflow: {
733 const nsCSSValue &xValue =
734 *data->ValueFor(eCSSProperty_overflow_x);
735 const nsCSSValue &yValue =
736 *data->ValueFor(eCSSProperty_overflow_y);
737 if (xValue == yValue)
738 xValue.AppendToString(eCSSProperty_overflow_x, aValue, aSerialization);
739 break;
741 case eCSSProperty_text_decoration: {
742 const nsCSSValue *decorationColor =
743 data->ValueFor(eCSSProperty_text_decoration_color);
744 const nsCSSValue *decorationStyle =
745 data->ValueFor(eCSSProperty_text_decoration_style);
747 NS_ABORT_IF_FALSE(decorationStyle->GetUnit() == eCSSUnit_Enumerated,
748 nsPrintfCString("bad text-decoration-style unit %d",
749 decorationStyle->GetUnit()).get());
751 AppendValueToString(eCSSProperty_text_decoration_line, aValue,
752 aSerialization);
753 if (decorationStyle->GetIntValue() !=
754 NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
755 aValue.Append(char16_t(' '));
756 AppendValueToString(eCSSProperty_text_decoration_style, aValue,
757 aSerialization);
759 if (decorationColor->GetUnit() != eCSSUnit_Enumerated ||
760 decorationColor->GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR) {
761 aValue.Append(char16_t(' '));
762 AppendValueToString(eCSSProperty_text_decoration_color, aValue,
763 aSerialization);
765 break;
767 case eCSSProperty_transition: {
768 const nsCSSValue *transProp =
769 data->ValueFor(eCSSProperty_transition_property);
770 const nsCSSValue *transDuration =
771 data->ValueFor(eCSSProperty_transition_duration);
772 const nsCSSValue *transTiming =
773 data->ValueFor(eCSSProperty_transition_timing_function);
774 const nsCSSValue *transDelay =
775 data->ValueFor(eCSSProperty_transition_delay);
777 NS_ABORT_IF_FALSE(transDuration->GetUnit() == eCSSUnit_List ||
778 transDuration->GetUnit() == eCSSUnit_ListDep,
779 nsPrintfCString("bad t-duration unit %d",
780 transDuration->GetUnit()).get());
781 NS_ABORT_IF_FALSE(transTiming->GetUnit() == eCSSUnit_List ||
782 transTiming->GetUnit() == eCSSUnit_ListDep,
783 nsPrintfCString("bad t-timing unit %d",
784 transTiming->GetUnit()).get());
785 NS_ABORT_IF_FALSE(transDelay->GetUnit() == eCSSUnit_List ||
786 transDelay->GetUnit() == eCSSUnit_ListDep,
787 nsPrintfCString("bad t-delay unit %d",
788 transDelay->GetUnit()).get());
790 const nsCSSValueList* dur = transDuration->GetListValue();
791 const nsCSSValueList* tim = transTiming->GetListValue();
792 const nsCSSValueList* del = transDelay->GetListValue();
794 if (transProp->GetUnit() == eCSSUnit_None ||
795 transProp->GetUnit() == eCSSUnit_All) {
796 // If any of the other three lists has more than one element,
797 // we can't use the shorthand.
798 if (!dur->mNext && !tim->mNext && !del->mNext) {
799 transProp->AppendToString(eCSSProperty_transition_property, aValue,
800 aSerialization);
801 aValue.Append(char16_t(' '));
802 dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue,
803 aSerialization);
804 aValue.Append(char16_t(' '));
805 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
806 aValue, aSerialization);
807 aValue.Append(char16_t(' '));
808 del->mValue.AppendToString(eCSSProperty_transition_delay, aValue,
809 aSerialization);
810 aValue.Append(char16_t(' '));
811 } else {
812 aValue.Truncate();
814 } else {
815 NS_ABORT_IF_FALSE(transProp->GetUnit() == eCSSUnit_List ||
816 transProp->GetUnit() == eCSSUnit_ListDep,
817 nsPrintfCString("bad t-prop unit %d",
818 transProp->GetUnit()).get());
819 const nsCSSValueList* pro = transProp->GetListValue();
820 for (;;) {
821 pro->mValue.AppendToString(eCSSProperty_transition_property,
822 aValue, aSerialization);
823 aValue.Append(char16_t(' '));
824 dur->mValue.AppendToString(eCSSProperty_transition_duration,
825 aValue, aSerialization);
826 aValue.Append(char16_t(' '));
827 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
828 aValue, aSerialization);
829 aValue.Append(char16_t(' '));
830 del->mValue.AppendToString(eCSSProperty_transition_delay,
831 aValue, aSerialization);
832 pro = pro->mNext;
833 dur = dur->mNext;
834 tim = tim->mNext;
835 del = del->mNext;
836 if (!pro || !dur || !tim || !del) {
837 break;
839 aValue.AppendLiteral(", ");
841 if (pro || dur || tim || del) {
842 // Lists not all the same length, can't use shorthand.
843 aValue.Truncate();
846 break;
848 case eCSSProperty_animation: {
849 const nsCSSProperty* subprops =
850 nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
851 static const size_t numProps = 8;
852 NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
853 "unexpected number of subproperties");
854 const nsCSSValue* values[numProps];
855 const nsCSSValueList* lists[numProps];
857 for (uint32_t i = 0; i < numProps; ++i) {
858 values[i] = data->ValueFor(subprops[i]);
859 NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
860 values[i]->GetUnit() == eCSSUnit_ListDep,
861 nsPrintfCString("bad a-duration unit %d",
862 values[i]->GetUnit()).get());
863 lists[i] = values[i]->GetListValue();
866 for (;;) {
867 // We must serialize 'animation-name' last in case it has
868 // a value that conflicts with one of the other keyword properties.
869 NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
870 eCSSProperty_animation_name,
871 "animation-name must be last");
872 bool done = false;
873 for (uint32_t i = 0;;) {
874 lists[i]->mValue.AppendToString(subprops[i], aValue, aSerialization);
875 lists[i] = lists[i]->mNext;
876 if (!lists[i]) {
877 done = true;
879 if (++i == numProps) {
880 break;
882 aValue.Append(char16_t(' '));
884 if (done) {
885 break;
887 aValue.AppendLiteral(", ");
889 for (uint32_t i = 0; i < numProps; ++i) {
890 if (lists[i]) {
891 // Lists not all the same length, can't use shorthand.
892 aValue.Truncate();
893 break;
896 break;
898 case eCSSProperty_marker: {
899 const nsCSSValue &endValue =
900 *data->ValueFor(eCSSProperty_marker_end);
901 const nsCSSValue &midValue =
902 *data->ValueFor(eCSSProperty_marker_mid);
903 const nsCSSValue &startValue =
904 *data->ValueFor(eCSSProperty_marker_start);
905 if (endValue == midValue && midValue == startValue)
906 AppendValueToString(eCSSProperty_marker_end, aValue, aSerialization);
907 break;
909 case eCSSProperty__moz_columns: {
910 // Two values, column-count and column-width, separated by a space.
911 const nsCSSProperty* subprops =
912 nsCSSProps::SubpropertyEntryFor(aProperty);
913 AppendValueToString(subprops[0], aValue, aSerialization);
914 aValue.Append(char16_t(' '));
915 AppendValueToString(subprops[1], aValue, aSerialization);
916 break;
918 case eCSSProperty_flex: {
919 // flex-grow, flex-shrink, flex-basis, separated by single space
920 const nsCSSProperty* subprops =
921 nsCSSProps::SubpropertyEntryFor(aProperty);
923 AppendValueToString(subprops[0], aValue, aSerialization);
924 aValue.Append(char16_t(' '));
925 AppendValueToString(subprops[1], aValue, aSerialization);
926 aValue.Append(char16_t(' '));
927 AppendValueToString(subprops[2], aValue, aSerialization);
928 break;
930 case eCSSProperty_flex_flow: {
931 // flex-direction, flex-wrap, separated by single space
932 const nsCSSProperty* subprops =
933 nsCSSProps::SubpropertyEntryFor(aProperty);
934 NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
935 "must have exactly two subproperties");
937 AppendValueToString(subprops[0], aValue, aSerialization);
938 aValue.Append(char16_t(' '));
939 AppendValueToString(subprops[1], aValue, aSerialization);
940 break;
942 case eCSSProperty_grid_row:
943 case eCSSProperty_grid_column: {
944 // grid-{row,column}-start, grid-{row,column}-end, separated by a slash
945 const nsCSSProperty* subprops =
946 nsCSSProps::SubpropertyEntryFor(aProperty);
947 NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
948 "must have exactly two subproperties");
950 // TODO: should we simplify when possible?
951 AppendValueToString(subprops[0], aValue, aSerialization);
952 aValue.AppendLiteral(" / ");
953 AppendValueToString(subprops[1], aValue, aSerialization);
954 break;
956 case eCSSProperty_grid_area: {
957 const nsCSSProperty* subprops =
958 nsCSSProps::SubpropertyEntryFor(aProperty);
959 NS_ABORT_IF_FALSE(subprops[4] == eCSSProperty_UNKNOWN,
960 "must have exactly four subproperties");
962 // TODO: should we simplify when possible?
963 AppendValueToString(subprops[0], aValue, aSerialization);
964 aValue.AppendLiteral(" / ");
965 AppendValueToString(subprops[1], aValue, aSerialization);
966 aValue.AppendLiteral(" / ");
967 AppendValueToString(subprops[2], aValue, aSerialization);
968 aValue.AppendLiteral(" / ");
969 AppendValueToString(subprops[3], aValue, aSerialization);
970 break;
973 // This can express either grid-template-{areas,columns,rows}
974 // or grid-auto-{flow,columns,rows}, but not both.
975 case eCSSProperty_grid: {
976 const nsCSSValue& areasValue =
977 *data->ValueFor(eCSSProperty_grid_template_areas);
978 const nsCSSValue& columnsValue =
979 *data->ValueFor(eCSSProperty_grid_template_columns);
980 const nsCSSValue& rowsValue =
981 *data->ValueFor(eCSSProperty_grid_template_rows);
983 const nsCSSValue& autoFlowValue =
984 *data->ValueFor(eCSSProperty_grid_auto_flow);
985 const nsCSSValue& autoColumnsValue =
986 *data->ValueFor(eCSSProperty_grid_auto_columns);
987 const nsCSSValue& autoRowsValue =
988 *data->ValueFor(eCSSProperty_grid_auto_rows);
990 if (areasValue.GetUnit() == eCSSUnit_None &&
991 columnsValue.GetUnit() == eCSSUnit_None &&
992 rowsValue.GetUnit() == eCSSUnit_None) {
993 AppendValueToString(eCSSProperty_grid_auto_flow,
994 aValue, aSerialization);
995 aValue.Append(char16_t(' '));
996 AppendValueToString(eCSSProperty_grid_auto_columns,
997 aValue, aSerialization);
998 aValue.AppendLiteral(" / ");
999 AppendValueToString(eCSSProperty_grid_auto_rows,
1000 aValue, aSerialization);
1001 break;
1002 } else if (!(autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
1003 autoFlowValue.GetIntValue() == NS_STYLE_GRID_AUTO_FLOW_ROW &&
1004 autoColumnsValue.GetUnit() == eCSSUnit_Auto &&
1005 autoRowsValue.GetUnit() == eCSSUnit_Auto)) {
1006 // Not serializable, bail.
1007 return;
1009 // Fall through to eCSSProperty_grid_template
1011 case eCSSProperty_grid_template: {
1012 const nsCSSValue& areasValue =
1013 *data->ValueFor(eCSSProperty_grid_template_areas);
1014 const nsCSSValue& columnsValue =
1015 *data->ValueFor(eCSSProperty_grid_template_columns);
1016 const nsCSSValue& rowsValue =
1017 *data->ValueFor(eCSSProperty_grid_template_rows);
1018 if (areasValue.GetUnit() == eCSSUnit_None) {
1019 AppendValueToString(eCSSProperty_grid_template_columns,
1020 aValue, aSerialization);
1021 aValue.AppendLiteral(" / ");
1022 AppendValueToString(eCSSProperty_grid_template_rows,
1023 aValue, aSerialization);
1024 break;
1026 if (columnsValue.GetUnit() == eCSSUnit_List ||
1027 columnsValue.GetUnit() == eCSSUnit_ListDep) {
1028 const nsCSSValueList* columnsItem = columnsValue.GetListValue();
1029 if (columnsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
1030 columnsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
1031 // We have "grid-template-areas:[something]; grid-template-columns:subgrid"
1032 // which isn't a value that the shorthand can express. Bail.
1033 return;
1036 if (rowsValue.GetUnit() != eCSSUnit_List &&
1037 rowsValue.GetUnit() != eCSSUnit_ListDep) {
1038 // We have "grid-template-areas:[something]; grid-template-rows:none"
1039 // which isn't a value that the shorthand can express. Bail.
1040 return;
1042 const nsCSSValueList* rowsItem = rowsValue.GetListValue();
1043 if (rowsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
1044 rowsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
1045 // We have "grid-template-areas:[something]; grid-template-rows:subgrid"
1046 // which isn't a value that the shorthand can express. Bail.
1047 return;
1049 const GridTemplateAreasValue* areas = areasValue.GetGridTemplateAreas();
1050 uint32_t nRowItems = 0;
1051 while (rowsItem) {
1052 nRowItems++;
1053 rowsItem = rowsItem->mNext;
1055 MOZ_ASSERT(nRowItems % 2 == 1, "expected an odd number of items");
1056 if ((nRowItems - 1) / 2 != areas->NRows()) {
1057 // Not serializable, bail.
1058 return;
1060 if (columnsValue.GetUnit() != eCSSUnit_None) {
1061 AppendValueToString(eCSSProperty_grid_template_columns,
1062 aValue, aSerialization);
1063 aValue.AppendLiteral(" / ");
1065 rowsItem = rowsValue.GetListValue();
1066 uint32_t row = 0;
1067 for (;;) {
1068 bool addSpaceSeparator = true;
1069 nsCSSUnit unit = rowsItem->mValue.GetUnit();
1071 if (unit == eCSSUnit_Null) {
1072 // Empty or omitted <line-names>. Serializes to nothing.
1073 addSpaceSeparator = false; // Avoid a double space.
1075 } else if (unit == eCSSUnit_List || unit == eCSSUnit_ListDep) {
1076 // Non-empty <line-names>
1077 aValue.Append('(');
1078 rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
1079 aValue, aSerialization);
1080 aValue.Append(')');
1082 } else {
1083 nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[row++], aValue);
1084 aValue.Append(char16_t(' '));
1086 // <track-size>
1087 rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
1088 aValue, aSerialization);
1089 if (rowsItem->mNext &&
1090 rowsItem->mNext->mValue.GetUnit() == eCSSUnit_Null &&
1091 !rowsItem->mNext->mNext) {
1092 // Break out of the loop early to avoid a trailing space.
1093 break;
1097 rowsItem = rowsItem->mNext;
1098 if (!rowsItem) {
1099 break;
1102 if (addSpaceSeparator) {
1103 aValue.Append(char16_t(' '));
1106 break;
1108 case eCSSProperty__moz_transform: {
1109 // shorthands that are just aliases with different parsing rules
1110 const nsCSSProperty* subprops =
1111 nsCSSProps::SubpropertyEntryFor(aProperty);
1112 NS_ABORT_IF_FALSE(subprops[1] == eCSSProperty_UNKNOWN,
1113 "must have exactly one subproperty");
1114 AppendValueToString(subprops[0], aValue, aSerialization);
1115 break;
1117 case eCSSProperty_all:
1118 // If we got here, then we didn't have all "inherit" or "initial" or
1119 // "unset" values for all of the longhand property components of 'all'.
1120 // There is no other possible value that is valid for all properties,
1121 // so serialize as the empty string.
1122 break;
1123 default:
1124 NS_ABORT_IF_FALSE(false, "no other shorthands");
1125 break;
1129 bool
1130 Declaration::GetValueIsImportant(const nsAString& aProperty) const
1132 nsCSSProperty propID =
1133 nsCSSProps::LookupProperty(aProperty, nsCSSProps::eIgnoreEnabledState);
1134 if (propID == eCSSProperty_UNKNOWN) {
1135 return false;
1137 if (propID == eCSSPropertyExtra_variable) {
1138 const nsSubstring& variableName =
1139 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);
1140 return GetVariableValueIsImportant(variableName);
1142 return GetValueIsImportant(propID);
1145 bool
1146 Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
1148 if (!mImportantData)
1149 return false;
1151 // Calling ValueFor is inefficient, but we can assume '!important' is rare.
1153 if (!nsCSSProps::IsShorthand(aProperty)) {
1154 return mImportantData->ValueFor(aProperty) != nullptr;
1157 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
1158 if (*p == eCSSProperty__x_system_font) {
1159 // The system_font subproperty doesn't count.
1160 continue;
1162 if (!mImportantData->ValueFor(*p)) {
1163 return false;
1166 return true;
1169 void
1170 Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty,
1171 nsAutoString& aValue,
1172 nsAString& aResult) const
1174 NS_ABORT_IF_FALSE(0 <= aProperty && aProperty < eCSSProperty_COUNT,
1175 "property enum out of range");
1176 NS_ABORT_IF_FALSE((aProperty < eCSSProperty_COUNT_no_shorthands) ==
1177 aValue.IsEmpty(),
1178 "aValue should be given for shorthands but not longhands");
1179 AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
1180 aResult.AppendLiteral(": ");
1181 if (aValue.IsEmpty())
1182 AppendValueToString(aProperty, aResult, nsCSSValue::eNormalized);
1183 else
1184 aResult.Append(aValue);
1185 if (GetValueIsImportant(aProperty)) {
1186 aResult.AppendLiteral(" ! important");
1188 aResult.AppendLiteral("; ");
1191 void
1192 Declaration::AppendVariableAndValueToString(const nsAString& aName,
1193 nsAString& aResult) const
1195 aResult.AppendLiteral("--");
1196 aResult.Append(aName);
1197 CSSVariableDeclarations::Type type;
1198 nsString value;
1199 bool important;
1201 if (mImportantVariables && mImportantVariables->Get(aName, type, value)) {
1202 important = true;
1203 } else {
1204 MOZ_ASSERT(mVariables);
1205 MOZ_ASSERT(mVariables->Has(aName));
1206 mVariables->Get(aName, type, value);
1207 important = false;
1210 switch (type) {
1211 case CSSVariableDeclarations::eTokenStream:
1212 if (value.IsEmpty()) {
1213 aResult.Append(':');
1214 } else {
1215 aResult.AppendLiteral(": ");
1216 aResult.Append(value);
1218 break;
1220 case CSSVariableDeclarations::eInitial:
1221 aResult.AppendLiteral("initial");
1222 break;
1224 case CSSVariableDeclarations::eInherit:
1225 aResult.AppendLiteral("inherit");
1226 break;
1228 case CSSVariableDeclarations::eUnset:
1229 aResult.AppendLiteral("unset");
1230 break;
1232 default:
1233 MOZ_ASSERT(false, "unexpected variable value type");
1236 if (important) {
1237 aResult.AppendLiteral("! important");
1239 aResult.AppendLiteral("; ");
1242 void
1243 Declaration::ToString(nsAString& aString) const
1245 // Someone cares about this declaration's contents, so don't let it
1246 // change from under them. See e.g. bug 338679.
1247 SetImmutable();
1249 nsCSSCompressedDataBlock *systemFontData =
1250 GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData;
1251 const nsCSSValue *systemFont =
1252 systemFontData->ValueFor(eCSSProperty__x_system_font);
1253 const bool haveSystemFont = systemFont &&
1254 systemFont->GetUnit() != eCSSUnit_None &&
1255 systemFont->GetUnit() != eCSSUnit_Null;
1256 bool didSystemFont = false;
1258 int32_t count = mOrder.Length();
1259 int32_t index;
1260 nsAutoTArray<nsCSSProperty, 16> shorthandsUsed;
1261 for (index = 0; index < count; index++) {
1262 nsCSSProperty property = GetPropertyAt(index);
1264 if (property == eCSSPropertyExtra_variable) {
1265 uint32_t variableIndex = mOrder[index] - eCSSProperty_COUNT;
1266 AppendVariableAndValueToString(mVariableOrder[variableIndex], aString);
1267 continue;
1270 if (!nsCSSProps::IsEnabled(property)) {
1271 continue;
1273 bool doneProperty = false;
1275 // If we already used this property in a shorthand, skip it.
1276 if (shorthandsUsed.Length() > 0) {
1277 for (const nsCSSProperty *shorthands =
1278 nsCSSProps::ShorthandsContaining(property);
1279 *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
1280 if (shorthandsUsed.Contains(*shorthands)) {
1281 doneProperty = true;
1282 break;
1285 if (doneProperty)
1286 continue;
1289 // Try to use this property in a shorthand.
1290 nsAutoString value;
1291 for (const nsCSSProperty *shorthands =
1292 nsCSSProps::ShorthandsContaining(property);
1293 *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
1294 // ShorthandsContaining returns the shorthands in order from those
1295 // that contain the most subproperties to those that contain the
1296 // least, which is exactly the order we want to test them.
1297 nsCSSProperty shorthand = *shorthands;
1299 GetValue(shorthand, value);
1301 // in the system font case, skip over font-variant shorthand, since all
1302 // subproperties are already dealt with via the font shorthand
1303 if (shorthand == eCSSProperty_font_variant &&
1304 value.EqualsLiteral("-moz-use-system-font")) {
1305 continue;
1308 // If GetValue gives us a non-empty string back, we can use that
1309 // value; otherwise it's not possible to use this shorthand.
1310 if (!value.IsEmpty()) {
1311 AppendPropertyAndValueToString(shorthand, value, aString);
1312 shorthandsUsed.AppendElement(shorthand);
1313 doneProperty = true;
1314 break;
1317 if (shorthand == eCSSProperty_font) {
1318 if (haveSystemFont && !didSystemFont) {
1319 // Output the shorthand font declaration that we will
1320 // partially override later. But don't add it to
1321 // |shorthandsUsed|, since we will have to override it.
1322 systemFont->AppendToString(eCSSProperty__x_system_font, value,
1323 nsCSSValue::eNormalized);
1324 AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
1325 value.Truncate();
1326 didSystemFont = true;
1329 // That we output the system font is enough for this property if:
1330 // (1) it's the hidden system font subproperty (which either
1331 // means we output it or we don't have it), or
1332 // (2) its value is the hidden system font value and it matches
1333 // the hidden system font subproperty in importance, and
1334 // we output the system font subproperty.
1335 const nsCSSValue *val = systemFontData->ValueFor(property);
1336 if (property == eCSSProperty__x_system_font ||
1337 (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
1338 doneProperty = true;
1339 break;
1343 if (doneProperty)
1344 continue;
1346 NS_ABORT_IF_FALSE(value.IsEmpty(), "value should be empty now");
1347 AppendPropertyAndValueToString(property, value, aString);
1349 if (! aString.IsEmpty()) {
1350 // if the string is not empty, we have trailing whitespace we
1351 // should remove
1352 aString.Truncate(aString.Length() - 1);
1356 #ifdef DEBUG
1357 void
1358 Declaration::List(FILE* out, int32_t aIndent) const
1360 nsAutoCString str;
1361 for (int32_t index = aIndent; --index >= 0; ) {
1362 str.AppendLiteral(" ");
1365 str.AppendLiteral("{ ");
1366 nsAutoString s;
1367 ToString(s);
1368 AppendUTF16toUTF8(s, str);
1369 str.AppendLiteral("}\n");
1370 fprintf_stderr(out, "%s", str.get());
1372 #endif
1374 bool
1375 Declaration::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
1377 aReturn.Truncate();
1378 if (aIndex < mOrder.Length()) {
1379 nsCSSProperty property = GetPropertyAt(aIndex);
1380 if (property == eCSSPropertyExtra_variable) {
1381 GetCustomPropertyNameAt(aIndex, aReturn);
1382 return true;
1384 if (0 <= property) {
1385 AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
1386 return true;
1389 return false;
1392 void
1393 Declaration::InitializeEmpty()
1395 NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
1396 mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
1399 Declaration*
1400 Declaration::EnsureMutable()
1402 NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
1403 if (!IsMutable()) {
1404 return new Declaration(*this);
1405 } else {
1406 return this;
1410 size_t
1411 Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
1413 size_t n = aMallocSizeOf(this);
1414 n += mOrder.SizeOfExcludingThis(aMallocSizeOf);
1415 n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0;
1416 n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
1417 if (mVariables) {
1418 n += mVariables->SizeOfIncludingThis(aMallocSizeOf);
1420 if (mImportantVariables) {
1421 n += mImportantVariables->SizeOfIncludingThis(aMallocSizeOf);
1423 return n;
1426 bool
1427 Declaration::HasVariableDeclaration(const nsAString& aName) const
1429 return (mVariables && mVariables->Has(aName)) ||
1430 (mImportantVariables && mImportantVariables->Has(aName));
1433 void
1434 Declaration::GetVariableDeclaration(const nsAString& aName,
1435 nsAString& aValue) const
1437 aValue.Truncate();
1439 CSSVariableDeclarations::Type type;
1440 nsString value;
1442 if ((mImportantVariables && mImportantVariables->Get(aName, type, value)) ||
1443 (mVariables && mVariables->Get(aName, type, value))) {
1444 switch (type) {
1445 case CSSVariableDeclarations::eTokenStream:
1446 aValue.Append(value);
1447 break;
1449 case CSSVariableDeclarations::eInitial:
1450 aValue.AppendLiteral("initial");
1451 break;
1453 case CSSVariableDeclarations::eInherit:
1454 aValue.AppendLiteral("inherit");
1455 break;
1457 case CSSVariableDeclarations::eUnset:
1458 aValue.AppendLiteral("unset");
1459 break;
1461 default:
1462 MOZ_ASSERT(false, "unexpected variable value type");
1467 void
1468 Declaration::AddVariableDeclaration(const nsAString& aName,
1469 CSSVariableDeclarations::Type aType,
1470 const nsString& aValue,
1471 bool aIsImportant,
1472 bool aOverrideImportant)
1474 MOZ_ASSERT(IsMutable());
1476 nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
1477 if (index == nsTArray<nsString>::NoIndex) {
1478 index = mVariableOrder.Length();
1479 mVariableOrder.AppendElement(aName);
1482 if (!aIsImportant && !aOverrideImportant &&
1483 mImportantVariables && mImportantVariables->Has(aName)) {
1484 return;
1487 CSSVariableDeclarations* variables;
1488 if (aIsImportant) {
1489 if (mVariables) {
1490 mVariables->Remove(aName);
1492 if (!mImportantVariables) {
1493 mImportantVariables = new CSSVariableDeclarations;
1495 variables = mImportantVariables;
1496 } else {
1497 if (mImportantVariables) {
1498 mImportantVariables->Remove(aName);
1500 if (!mVariables) {
1501 mVariables = new CSSVariableDeclarations;
1503 variables = mVariables;
1506 switch (aType) {
1507 case CSSVariableDeclarations::eTokenStream:
1508 variables->PutTokenStream(aName, aValue);
1509 break;
1511 case CSSVariableDeclarations::eInitial:
1512 MOZ_ASSERT(aValue.IsEmpty());
1513 variables->PutInitial(aName);
1514 break;
1516 case CSSVariableDeclarations::eInherit:
1517 MOZ_ASSERT(aValue.IsEmpty());
1518 variables->PutInherit(aName);
1519 break;
1521 case CSSVariableDeclarations::eUnset:
1522 MOZ_ASSERT(aValue.IsEmpty());
1523 variables->PutUnset(aName);
1524 break;
1526 default:
1527 MOZ_ASSERT(false, "unexpected aType value");
1530 uint32_t propertyIndex = index + eCSSProperty_COUNT;
1531 mOrder.RemoveElement(propertyIndex);
1532 mOrder.AppendElement(propertyIndex);
1535 void
1536 Declaration::RemoveVariableDeclaration(const nsAString& aName)
1538 if (mVariables) {
1539 mVariables->Remove(aName);
1541 if (mImportantVariables) {
1542 mImportantVariables->Remove(aName);
1544 nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
1545 if (index != nsTArray<nsString>::NoIndex) {
1546 mOrder.RemoveElement(index + eCSSProperty_COUNT);
1550 bool
1551 Declaration::GetVariableValueIsImportant(const nsAString& aName) const
1553 return mImportantVariables && mImportantVariables->Has(aName);
1556 } // namespace mozilla::css
1557 } // namespace mozilla