Bumping manifests a=b2g-bump
[gecko.git] / layout / style / Declaration.cpp
blob3888862d185e45dcf0199718bac58c42f0ae9b9f
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 // Helper to append |aString| with the shorthand sides notation used in e.g.
118 // 'padding'. |aProperties| and |aValues| are expected to have 4 elements.
119 static void
120 AppendSidesShorthandToString(const nsCSSProperty aProperties[],
121 const nsCSSValue* aValues[],
122 nsAString& aString,
123 nsCSSValue::Serialization aSerialization)
125 const nsCSSValue& value1 = *aValues[0];
126 const nsCSSValue& value2 = *aValues[1];
127 const nsCSSValue& value3 = *aValues[2];
128 const nsCSSValue& value4 = *aValues[3];
130 NS_ABORT_IF_FALSE(value1.GetUnit() != eCSSUnit_Null, "null value 1");
131 value1.AppendToString(aProperties[0], aString, aSerialization);
132 if (value1 != value2 || value1 != value3 || value1 != value4) {
133 aString.Append(char16_t(' '));
134 NS_ABORT_IF_FALSE(value2.GetUnit() != eCSSUnit_Null, "null value 2");
135 value2.AppendToString(aProperties[1], aString, aSerialization);
136 if (value1 != value3 || value2 != value4) {
137 aString.Append(char16_t(' '));
138 NS_ABORT_IF_FALSE(value3.GetUnit() != eCSSUnit_Null, "null value 3");
139 value3.AppendToString(aProperties[2], aString, aSerialization);
140 if (value2 != value4) {
141 aString.Append(char16_t(' '));
142 NS_ABORT_IF_FALSE(value4.GetUnit() != eCSSUnit_Null, "null value 4");
143 value4.AppendToString(aProperties[3], aString, aSerialization);
149 void
150 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
152 GetValue(aProperty, aValue, nsCSSValue::eNormalized);
155 void
156 Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const
158 GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified);
161 void
162 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue,
163 nsCSSValue::Serialization aSerialization) const
165 aValue.Truncate(0);
167 // simple properties are easy.
168 if (!nsCSSProps::IsShorthand(aProperty)) {
169 AppendValueToString(aProperty, aValue, aSerialization);
170 return;
173 // DOM Level 2 Style says (when describing CSS2Properties, although
174 // not CSSStyleDeclaration.getPropertyValue):
175 // However, if there is no shorthand declaration that could be added
176 // to the ruleset without changing in any way the rules already
177 // declared in the ruleset (i.e., by adding longhand rules that were
178 // previously not declared in the ruleset), then the empty string
179 // should be returned for the shorthand property.
180 // This means we need to check a number of cases:
181 // (1) Since a shorthand sets all sub-properties, if some of its
182 // subproperties were not specified, we must return the empty
183 // string.
184 // (2) Since 'inherit', 'initial' and 'unset' can only be specified
185 // as the values for entire properties, we need to return the
186 // empty string if some but not all of the subproperties have one
187 // of those values.
188 // (3) Since a single value only makes sense with or without
189 // !important, we return the empty string if some values are
190 // !important and some are not.
191 // Since we're doing this check for 'inherit' and 'initial' up front,
192 // we can also simplify the property serialization code by serializing
193 // those values up front as well.
195 // Additionally, if a shorthand property was set using a value with a
196 // variable reference and none of its component longhand properties were
197 // then overridden on the declaration, we return the token stream
198 // assigned to the shorthand.
199 const nsCSSValue* tokenStream = nullptr;
200 uint32_t totalCount = 0, importantCount = 0,
201 initialCount = 0, inheritCount = 0, unsetCount = 0,
202 matchingTokenStreamCount = 0, nonMatchingTokenStreamCount = 0;
203 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
204 if (*p == eCSSProperty__x_system_font ||
205 nsCSSProps::PropHasFlags(*p, CSS_PROPERTY_DIRECTIONAL_SOURCE)) {
206 // The system-font subproperty and the *-source properties don't count.
207 continue;
209 ++totalCount;
210 const nsCSSValue *val = mData->ValueFor(*p);
211 NS_ABORT_IF_FALSE(!val || !mImportantData || !mImportantData->ValueFor(*p),
212 "can't be in both blocks");
213 if (!val && mImportantData) {
214 ++importantCount;
215 val = mImportantData->ValueFor(*p);
217 if (!val) {
218 // Case (1) above: some subproperties not specified.
219 return;
221 if (val->GetUnit() == eCSSUnit_Inherit) {
222 ++inheritCount;
223 } else if (val->GetUnit() == eCSSUnit_Initial) {
224 ++initialCount;
225 } else if (val->GetUnit() == eCSSUnit_Unset) {
226 ++unsetCount;
227 } else if (val->GetUnit() == eCSSUnit_TokenStream) {
228 if (val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) {
229 tokenStream = val;
230 ++matchingTokenStreamCount;
231 } else {
232 ++nonMatchingTokenStreamCount;
236 if (importantCount != 0 && importantCount != totalCount) {
237 // Case (3), no consistent importance.
238 return;
240 if (initialCount == totalCount) {
241 // Simplify serialization below by serializing initial up-front.
242 nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue,
243 nsCSSValue::eNormalized);
244 return;
246 if (inheritCount == totalCount) {
247 // Simplify serialization below by serializing inherit up-front.
248 nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue,
249 nsCSSValue::eNormalized);
250 return;
252 if (unsetCount == totalCount) {
253 // Simplify serialization below by serializing unset up-front.
254 nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue,
255 nsCSSValue::eNormalized);
256 return;
258 if (initialCount != 0 || inheritCount != 0 ||
259 unsetCount != 0 || nonMatchingTokenStreamCount != 0) {
260 // Case (2): partially initial, inherit, unset or token stream.
261 return;
263 if (tokenStream) {
264 if (matchingTokenStreamCount == totalCount) {
265 // Shorthand was specified using variable references and all of its
266 // longhand components were set by the shorthand.
267 aValue.Append(tokenStream->GetTokenStreamValue()->mTokenStream);
268 } else {
269 // In all other cases, serialize to the empty string.
271 return;
274 nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
275 switch (aProperty) {
276 case eCSSProperty_margin:
277 case eCSSProperty_padding:
278 case eCSSProperty_border_color:
279 case eCSSProperty_border_style:
280 case eCSSProperty_border_width: {
281 const nsCSSProperty* subprops =
282 nsCSSProps::SubpropertyEntryFor(aProperty);
283 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
284 kNotFound, "first subprop must be top");
285 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
286 kNotFound, "second subprop must be right");
287 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
288 kNotFound, "third subprop must be bottom");
289 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
290 kNotFound, "fourth subprop must be left");
291 const nsCSSValue* vals[4] = {
292 data->ValueFor(subprops[0]),
293 data->ValueFor(subprops[1]),
294 data->ValueFor(subprops[2]),
295 data->ValueFor(subprops[3])
297 AppendSidesShorthandToString(subprops, vals, aValue, aSerialization);
298 break;
300 case eCSSProperty_border_radius:
301 case eCSSProperty__moz_outline_radius: {
302 const nsCSSProperty* subprops =
303 nsCSSProps::SubpropertyEntryFor(aProperty);
304 const nsCSSValue* vals[4] = {
305 data->ValueFor(subprops[0]),
306 data->ValueFor(subprops[1]),
307 data->ValueFor(subprops[2]),
308 data->ValueFor(subprops[3])
311 // For compatibility, only write a slash and the y-values
312 // if they're not identical to the x-values.
313 bool needY = false;
314 const nsCSSValue* xVals[4];
315 const nsCSSValue* yVals[4];
316 for (int i = 0; i < 4; i++) {
317 if (vals[i]->GetUnit() == eCSSUnit_Pair) {
318 needY = true;
319 xVals[i] = &vals[i]->GetPairValue().mXValue;
320 yVals[i] = &vals[i]->GetPairValue().mYValue;
321 } else {
322 xVals[i] = yVals[i] = vals[i];
326 AppendSidesShorthandToString(subprops, xVals, aValue, aSerialization);
327 if (needY) {
328 aValue.AppendLiteral(" / ");
329 AppendSidesShorthandToString(subprops, yVals, aValue, aSerialization);
331 break;
333 case eCSSProperty_border_image: {
334 // Even though there are some cases where we could omit
335 // 'border-image-source' (when it's none), it's probably not a
336 // good idea since it's likely to be confusing. It would also
337 // require adding the extra check that we serialize *something*.
338 AppendValueToString(eCSSProperty_border_image_source, aValue,
339 aSerialization);
341 bool sliceDefault = data->HasDefaultBorderImageSlice();
342 bool widthDefault = data->HasDefaultBorderImageWidth();
343 bool outsetDefault = data->HasDefaultBorderImageOutset();
345 if (!sliceDefault || !widthDefault || !outsetDefault) {
346 aValue.Append(char16_t(' '));
347 AppendValueToString(eCSSProperty_border_image_slice, aValue,
348 aSerialization);
349 if (!widthDefault || !outsetDefault) {
350 aValue.AppendLiteral(" /");
351 if (!widthDefault) {
352 aValue.Append(char16_t(' '));
353 AppendValueToString(eCSSProperty_border_image_width, aValue,
354 aSerialization);
356 if (!outsetDefault) {
357 aValue.AppendLiteral(" / ");
358 AppendValueToString(eCSSProperty_border_image_outset, aValue,
359 aSerialization);
364 bool repeatDefault = data->HasDefaultBorderImageRepeat();
365 if (!repeatDefault) {
366 aValue.Append(char16_t(' '));
367 AppendValueToString(eCSSProperty_border_image_repeat, aValue,
368 aSerialization);
370 break;
372 case eCSSProperty_border: {
373 // If we have a non-default value for any of the properties that
374 // this shorthand sets but cannot specify, we have to return the
375 // empty string.
376 if (data->ValueFor(eCSSProperty_border_image_source)->GetUnit() !=
377 eCSSUnit_None ||
378 !data->HasDefaultBorderImageSlice() ||
379 !data->HasDefaultBorderImageWidth() ||
380 !data->HasDefaultBorderImageOutset() ||
381 !data->HasDefaultBorderImageRepeat() ||
382 data->ValueFor(eCSSProperty_border_top_colors)->GetUnit() !=
383 eCSSUnit_None ||
384 data->ValueFor(eCSSProperty_border_right_colors)->GetUnit() !=
385 eCSSUnit_None ||
386 data->ValueFor(eCSSProperty_border_bottom_colors)->GetUnit() !=
387 eCSSUnit_None ||
388 data->ValueFor(eCSSProperty_border_left_colors)->GetUnit() !=
389 eCSSUnit_None) {
390 break;
393 const nsCSSProperty* subproptables[3] = {
394 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
395 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
396 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
398 bool match = true;
399 for (const nsCSSProperty** subprops = subproptables,
400 **subprops_end = ArrayEnd(subproptables);
401 subprops < subprops_end; ++subprops) {
402 // Check only the first four subprops in each table, since the
403 // others are extras for dimensional box properties.
404 const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
405 for (int32_t side = 1; side < 4; ++side) {
406 const nsCSSValue *otherSide =
407 data->ValueFor((*subprops)[side]);
408 if (*firstSide != *otherSide)
409 match = false;
412 if (!match) {
413 // We can't express what we have in the border shorthand
414 break;
416 // tweak aProperty and fall through
417 aProperty = eCSSProperty_border_top;
419 case eCSSProperty_border_top:
420 case eCSSProperty_border_right:
421 case eCSSProperty_border_bottom:
422 case eCSSProperty_border_left:
423 case eCSSProperty_border_start:
424 case eCSSProperty_border_end:
425 case eCSSProperty__moz_column_rule:
426 case eCSSProperty_outline: {
427 const nsCSSProperty* subprops =
428 nsCSSProps::SubpropertyEntryFor(aProperty);
429 NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
430 NS_LITERAL_CSTRING("-color")) ||
431 StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
432 NS_LITERAL_CSTRING("-color-value")),
433 "third subprop must be the color property");
434 const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
435 bool isMozUseTextColor =
436 colorValue->GetUnit() == eCSSUnit_Enumerated &&
437 colorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR;
438 if (!AppendValueToString(subprops[0], aValue, aSerialization) ||
439 !(aValue.Append(char16_t(' ')),
440 AppendValueToString(subprops[1], aValue, aSerialization)) ||
441 // Don't output a third value when it's -moz-use-text-color.
442 !(isMozUseTextColor ||
443 (aValue.Append(char16_t(' ')),
444 AppendValueToString(subprops[2], aValue, aSerialization)))) {
445 aValue.Truncate();
447 break;
449 case eCSSProperty_margin_left:
450 case eCSSProperty_margin_right:
451 case eCSSProperty_margin_start:
452 case eCSSProperty_margin_end:
453 case eCSSProperty_padding_left:
454 case eCSSProperty_padding_right:
455 case eCSSProperty_padding_start:
456 case eCSSProperty_padding_end:
457 case eCSSProperty_border_left_color:
458 case eCSSProperty_border_left_style:
459 case eCSSProperty_border_left_width:
460 case eCSSProperty_border_right_color:
461 case eCSSProperty_border_right_style:
462 case eCSSProperty_border_right_width:
463 case eCSSProperty_border_start_color:
464 case eCSSProperty_border_start_style:
465 case eCSSProperty_border_start_width:
466 case eCSSProperty_border_end_color:
467 case eCSSProperty_border_end_style:
468 case eCSSProperty_border_end_width: {
469 const nsCSSProperty* subprops =
470 nsCSSProps::SubpropertyEntryFor(aProperty);
471 NS_ABORT_IF_FALSE(subprops[3] == eCSSProperty_UNKNOWN,
472 "not box property with physical vs. logical cascading");
473 AppendValueToString(subprops[0], aValue, aSerialization);
474 break;
476 case eCSSProperty_background: {
477 // We know from above that all subproperties were specified.
478 // However, we still can't represent that in the shorthand unless
479 // they're all lists of the same length. So if they're different
480 // lengths, we need to bail out.
481 // We also need to bail out if an item has background-clip and
482 // background-origin that are different and not the default
483 // values. (We omit them if they're both default.)
484 const nsCSSValueList *image =
485 data->ValueFor(eCSSProperty_background_image)->
486 GetListValue();
487 const nsCSSValuePairList *repeat =
488 data->ValueFor(eCSSProperty_background_repeat)->
489 GetPairListValue();
490 const nsCSSValueList *attachment =
491 data->ValueFor(eCSSProperty_background_attachment)->
492 GetListValue();
493 const nsCSSValueList *position =
494 data->ValueFor(eCSSProperty_background_position)->
495 GetListValue();
496 const nsCSSValueList *clip =
497 data->ValueFor(eCSSProperty_background_clip)->
498 GetListValue();
499 const nsCSSValueList *origin =
500 data->ValueFor(eCSSProperty_background_origin)->
501 GetListValue();
502 const nsCSSValuePairList *size =
503 data->ValueFor(eCSSProperty_background_size)->
504 GetPairListValue();
505 for (;;) {
506 image->mValue.AppendToString(eCSSProperty_background_image, aValue,
507 aSerialization);
508 aValue.Append(char16_t(' '));
509 repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue,
510 aSerialization);
511 if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
512 repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue,
513 aSerialization);
515 aValue.Append(char16_t(' '));
516 attachment->mValue.AppendToString(eCSSProperty_background_attachment,
517 aValue, aSerialization);
518 aValue.Append(char16_t(' '));
519 position->mValue.AppendToString(eCSSProperty_background_position,
520 aValue, aSerialization);
522 if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
523 size->mYValue.GetUnit() != eCSSUnit_Auto) {
524 aValue.Append(char16_t(' '));
525 aValue.Append(char16_t('/'));
526 aValue.Append(char16_t(' '));
527 size->mXValue.AppendToString(eCSSProperty_background_size, aValue,
528 aSerialization);
529 aValue.Append(char16_t(' '));
530 size->mYValue.AppendToString(eCSSProperty_background_size, aValue,
531 aSerialization);
534 NS_ABORT_IF_FALSE(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
535 origin->mValue.GetUnit() == eCSSUnit_Enumerated,
536 "should not have inherit/initial within list");
538 if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
539 origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
540 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
541 eCSSProperty_background_origin] ==
542 nsCSSProps::kBackgroundOriginKTable);
543 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
544 eCSSProperty_background_clip] ==
545 nsCSSProps::kBackgroundOriginKTable);
546 static_assert(NS_STYLE_BG_CLIP_BORDER ==
547 NS_STYLE_BG_ORIGIN_BORDER &&
548 NS_STYLE_BG_CLIP_PADDING ==
549 NS_STYLE_BG_ORIGIN_PADDING &&
550 NS_STYLE_BG_CLIP_CONTENT ==
551 NS_STYLE_BG_ORIGIN_CONTENT,
552 "bg-clip and bg-origin style constants must agree");
553 aValue.Append(char16_t(' '));
554 origin->mValue.AppendToString(eCSSProperty_background_origin, aValue,
555 aSerialization);
557 if (clip->mValue != origin->mValue) {
558 aValue.Append(char16_t(' '));
559 clip->mValue.AppendToString(eCSSProperty_background_clip, aValue,
560 aSerialization);
564 image = image->mNext;
565 repeat = repeat->mNext;
566 attachment = attachment->mNext;
567 position = position->mNext;
568 clip = clip->mNext;
569 origin = origin->mNext;
570 size = size->mNext;
572 if (!image) {
573 if (repeat || attachment || position || clip || origin || size) {
574 // Uneven length lists, so can't be serialized as shorthand.
575 aValue.Truncate();
576 return;
578 break;
580 if (!repeat || !attachment || !position || !clip || !origin || !size) {
581 // Uneven length lists, so can't be serialized as shorthand.
582 aValue.Truncate();
583 return;
585 aValue.Append(char16_t(','));
586 aValue.Append(char16_t(' '));
589 aValue.Append(char16_t(' '));
590 AppendValueToString(eCSSProperty_background_color, aValue,
591 aSerialization);
592 break;
594 case eCSSProperty_font: {
595 // systemFont might not be present; other values are guaranteed to be
596 // available based on the shorthand check at the beginning of the
597 // function, as long as the prop is enabled
598 const nsCSSValue *systemFont =
599 data->ValueFor(eCSSProperty__x_system_font);
600 const nsCSSValue *style =
601 data->ValueFor(eCSSProperty_font_style);
602 const nsCSSValue *weight =
603 data->ValueFor(eCSSProperty_font_weight);
604 const nsCSSValue *size =
605 data->ValueFor(eCSSProperty_font_size);
606 const nsCSSValue *lh =
607 data->ValueFor(eCSSProperty_line_height);
608 const nsCSSValue *family =
609 data->ValueFor(eCSSProperty_font_family);
610 const nsCSSValue *stretch =
611 data->ValueFor(eCSSProperty_font_stretch);
612 const nsCSSValue *sizeAdjust =
613 data->ValueFor(eCSSProperty_font_size_adjust);
614 const nsCSSValue *featureSettings =
615 data->ValueFor(eCSSProperty_font_feature_settings);
616 const nsCSSValue *languageOverride =
617 data->ValueFor(eCSSProperty_font_language_override);
618 const nsCSSValue *fontKerning =
619 data->ValueFor(eCSSProperty_font_kerning);
620 const nsCSSValue *fontSynthesis =
621 data->ValueFor(eCSSProperty_font_synthesis);
622 const nsCSSValue *fontVariantAlternates =
623 data->ValueFor(eCSSProperty_font_variant_alternates);
624 const nsCSSValue *fontVariantCaps =
625 data->ValueFor(eCSSProperty_font_variant_caps);
626 const nsCSSValue *fontVariantEastAsian =
627 data->ValueFor(eCSSProperty_font_variant_east_asian);
628 const nsCSSValue *fontVariantLigatures =
629 data->ValueFor(eCSSProperty_font_variant_ligatures);
630 const nsCSSValue *fontVariantNumeric =
631 data->ValueFor(eCSSProperty_font_variant_numeric);
632 const nsCSSValue *fontVariantPosition =
633 data->ValueFor(eCSSProperty_font_variant_position);
635 if (systemFont &&
636 systemFont->GetUnit() != eCSSUnit_None &&
637 systemFont->GetUnit() != eCSSUnit_Null) {
638 if (style->GetUnit() != eCSSUnit_System_Font ||
639 weight->GetUnit() != eCSSUnit_System_Font ||
640 size->GetUnit() != eCSSUnit_System_Font ||
641 lh->GetUnit() != eCSSUnit_System_Font ||
642 family->GetUnit() != eCSSUnit_System_Font ||
643 stretch->GetUnit() != eCSSUnit_System_Font ||
644 sizeAdjust->GetUnit() != eCSSUnit_System_Font ||
645 featureSettings->GetUnit() != eCSSUnit_System_Font ||
646 languageOverride->GetUnit() != eCSSUnit_System_Font ||
647 fontKerning->GetUnit() != eCSSUnit_System_Font ||
648 fontSynthesis->GetUnit() != eCSSUnit_System_Font ||
649 fontVariantAlternates->GetUnit() != eCSSUnit_System_Font ||
650 fontVariantCaps->GetUnit() != eCSSUnit_System_Font ||
651 fontVariantEastAsian->GetUnit() != eCSSUnit_System_Font ||
652 fontVariantLigatures->GetUnit() != eCSSUnit_System_Font ||
653 fontVariantNumeric->GetUnit() != eCSSUnit_System_Font ||
654 fontVariantPosition->GetUnit() != eCSSUnit_System_Font) {
655 // This can't be represented as a shorthand.
656 return;
658 systemFont->AppendToString(eCSSProperty__x_system_font, aValue,
659 aSerialization);
660 } else {
661 // properties reset by this shorthand property to their
662 // initial values but not represented in its syntax
663 if (stretch->GetUnit() != eCSSUnit_Enumerated ||
664 stretch->GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
665 sizeAdjust->GetUnit() != eCSSUnit_None ||
666 featureSettings->GetUnit() != eCSSUnit_Normal ||
667 languageOverride->GetUnit() != eCSSUnit_Normal ||
668 fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO ||
669 fontSynthesis->GetUnit() != eCSSUnit_Enumerated ||
670 fontSynthesis->GetIntValue() !=
671 (NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) ||
672 fontVariantAlternates->GetUnit() != eCSSUnit_Normal ||
673 fontVariantEastAsian->GetUnit() != eCSSUnit_Normal ||
674 fontVariantLigatures->GetUnit() != eCSSUnit_Normal ||
675 fontVariantNumeric->GetUnit() != eCSSUnit_Normal ||
676 fontVariantPosition->GetUnit() != eCSSUnit_Normal) {
677 return;
680 // only a normal or small-caps values of font-variant-caps can
681 // be represented in the font shorthand
682 if (fontVariantCaps->GetUnit() != eCSSUnit_Normal &&
683 (fontVariantCaps->GetUnit() != eCSSUnit_Enumerated ||
684 fontVariantCaps->GetIntValue() != NS_FONT_VARIANT_CAPS_SMALLCAPS)) {
685 return;
688 if (style->GetUnit() != eCSSUnit_Enumerated ||
689 style->GetIntValue() != NS_FONT_STYLE_NORMAL) {
690 style->AppendToString(eCSSProperty_font_style, aValue,
691 aSerialization);
692 aValue.Append(char16_t(' '));
694 if (fontVariantCaps->GetUnit() != eCSSUnit_Normal) {
695 fontVariantCaps->AppendToString(eCSSProperty_font_variant_caps, aValue,
696 aSerialization);
697 aValue.Append(char16_t(' '));
699 if (weight->GetUnit() != eCSSUnit_Enumerated ||
700 weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
701 weight->AppendToString(eCSSProperty_font_weight, aValue,
702 aSerialization);
703 aValue.Append(char16_t(' '));
705 size->AppendToString(eCSSProperty_font_size, aValue, aSerialization);
706 if (lh->GetUnit() != eCSSUnit_Normal) {
707 aValue.Append(char16_t('/'));
708 lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization);
710 aValue.Append(char16_t(' '));
711 family->AppendToString(eCSSProperty_font_family, aValue,
712 aSerialization);
714 break;
716 case eCSSProperty_font_variant: {
717 const nsCSSProperty *subprops =
718 nsCSSProps::SubpropertyEntryFor(aProperty);
719 const nsCSSValue *fontVariantLigatures =
720 data->ValueFor(eCSSProperty_font_variant_ligatures);
722 // all subproperty values normal? system font?
723 bool normalLigs = true, normalNonLigs = true, systemFont = true,
724 hasSystem = false;
725 for (const nsCSSProperty *sp = subprops; *sp != eCSSProperty_UNKNOWN; sp++) {
726 const nsCSSValue *spVal = data->ValueFor(*sp);
727 bool isNormal = (spVal->GetUnit() == eCSSUnit_Normal);
728 if (*sp == eCSSProperty_font_variant_ligatures) {
729 normalLigs = normalLigs && isNormal;
730 } else {
731 normalNonLigs = normalNonLigs && isNormal;
733 bool isSystem = (spVal->GetUnit() == eCSSUnit_System_Font);
734 systemFont = systemFont && isSystem;
735 hasSystem = hasSystem || isSystem;
738 bool ligsNone =
739 fontVariantLigatures->GetUnit() == eCSSUnit_None;
741 // normal, none, or system font ==> single value
742 if ((normalLigs && normalNonLigs) ||
743 (normalNonLigs && ligsNone) ||
744 systemFont) {
745 fontVariantLigatures->AppendToString(eCSSProperty_font_variant_ligatures,
746 aValue,
747 aSerialization);
748 } else if (ligsNone || hasSystem) {
749 // ligatures none but other values are non-normal ==> empty
750 // at least one but not all values are system font ==> empty
751 return;
752 } else {
753 // iterate over and append non-normal values
754 bool appendSpace = false;
755 for (const nsCSSProperty *sp = subprops;
756 *sp != eCSSProperty_UNKNOWN; sp++) {
757 const nsCSSValue *spVal = data->ValueFor(*sp);
758 if (spVal && spVal->GetUnit() != eCSSUnit_Normal) {
759 if (appendSpace) {
760 aValue.Append(char16_t(' '));
761 } else {
762 appendSpace = true;
764 spVal->AppendToString(*sp, aValue, aSerialization);
768 break;
770 case eCSSProperty_list_style:
771 if (AppendValueToString(eCSSProperty_list_style_position, aValue,
772 aSerialization)) {
773 aValue.Append(char16_t(' '));
775 if (AppendValueToString(eCSSProperty_list_style_image, aValue,
776 aSerialization)) {
777 aValue.Append(char16_t(' '));
779 AppendValueToString(eCSSProperty_list_style_type, aValue,
780 aSerialization);
781 break;
782 case eCSSProperty_overflow: {
783 const nsCSSValue &xValue =
784 *data->ValueFor(eCSSProperty_overflow_x);
785 const nsCSSValue &yValue =
786 *data->ValueFor(eCSSProperty_overflow_y);
787 if (xValue == yValue)
788 xValue.AppendToString(eCSSProperty_overflow_x, aValue, aSerialization);
789 break;
791 case eCSSProperty_text_decoration: {
792 // If text-decoration-color or text-decoration-style isn't initial value,
793 // we cannot serialize the text-decoration shorthand value.
794 const nsCSSValue *decorationColor =
795 data->ValueFor(eCSSProperty_text_decoration_color);
796 const nsCSSValue *decorationStyle =
797 data->ValueFor(eCSSProperty_text_decoration_style);
799 NS_ABORT_IF_FALSE(decorationStyle->GetUnit() == eCSSUnit_Enumerated,
800 nsPrintfCString("bad text-decoration-style unit %d",
801 decorationStyle->GetUnit()).get());
803 if (decorationColor->GetUnit() != eCSSUnit_Enumerated ||
804 decorationColor->GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR ||
805 decorationStyle->GetIntValue() !=
806 NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
807 return;
810 AppendValueToString(eCSSProperty_text_decoration_line, aValue,
811 aSerialization);
812 break;
814 case eCSSProperty_transition: {
815 const nsCSSValue *transProp =
816 data->ValueFor(eCSSProperty_transition_property);
817 const nsCSSValue *transDuration =
818 data->ValueFor(eCSSProperty_transition_duration);
819 const nsCSSValue *transTiming =
820 data->ValueFor(eCSSProperty_transition_timing_function);
821 const nsCSSValue *transDelay =
822 data->ValueFor(eCSSProperty_transition_delay);
824 NS_ABORT_IF_FALSE(transDuration->GetUnit() == eCSSUnit_List ||
825 transDuration->GetUnit() == eCSSUnit_ListDep,
826 nsPrintfCString("bad t-duration unit %d",
827 transDuration->GetUnit()).get());
828 NS_ABORT_IF_FALSE(transTiming->GetUnit() == eCSSUnit_List ||
829 transTiming->GetUnit() == eCSSUnit_ListDep,
830 nsPrintfCString("bad t-timing unit %d",
831 transTiming->GetUnit()).get());
832 NS_ABORT_IF_FALSE(transDelay->GetUnit() == eCSSUnit_List ||
833 transDelay->GetUnit() == eCSSUnit_ListDep,
834 nsPrintfCString("bad t-delay unit %d",
835 transDelay->GetUnit()).get());
837 const nsCSSValueList* dur = transDuration->GetListValue();
838 const nsCSSValueList* tim = transTiming->GetListValue();
839 const nsCSSValueList* del = transDelay->GetListValue();
841 if (transProp->GetUnit() == eCSSUnit_None ||
842 transProp->GetUnit() == eCSSUnit_All) {
843 // If any of the other three lists has more than one element,
844 // we can't use the shorthand.
845 if (!dur->mNext && !tim->mNext && !del->mNext) {
846 transProp->AppendToString(eCSSProperty_transition_property, aValue,
847 aSerialization);
848 aValue.Append(char16_t(' '));
849 dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue,
850 aSerialization);
851 aValue.Append(char16_t(' '));
852 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
853 aValue, aSerialization);
854 aValue.Append(char16_t(' '));
855 del->mValue.AppendToString(eCSSProperty_transition_delay, aValue,
856 aSerialization);
857 aValue.Append(char16_t(' '));
858 } else {
859 aValue.Truncate();
861 } else {
862 NS_ABORT_IF_FALSE(transProp->GetUnit() == eCSSUnit_List ||
863 transProp->GetUnit() == eCSSUnit_ListDep,
864 nsPrintfCString("bad t-prop unit %d",
865 transProp->GetUnit()).get());
866 const nsCSSValueList* pro = transProp->GetListValue();
867 for (;;) {
868 pro->mValue.AppendToString(eCSSProperty_transition_property,
869 aValue, aSerialization);
870 aValue.Append(char16_t(' '));
871 dur->mValue.AppendToString(eCSSProperty_transition_duration,
872 aValue, aSerialization);
873 aValue.Append(char16_t(' '));
874 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
875 aValue, aSerialization);
876 aValue.Append(char16_t(' '));
877 del->mValue.AppendToString(eCSSProperty_transition_delay,
878 aValue, aSerialization);
879 pro = pro->mNext;
880 dur = dur->mNext;
881 tim = tim->mNext;
882 del = del->mNext;
883 if (!pro || !dur || !tim || !del) {
884 break;
886 aValue.AppendLiteral(", ");
888 if (pro || dur || tim || del) {
889 // Lists not all the same length, can't use shorthand.
890 aValue.Truncate();
893 break;
895 case eCSSProperty_animation: {
896 const nsCSSProperty* subprops =
897 nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
898 static const size_t numProps = 8;
899 NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
900 "unexpected number of subproperties");
901 const nsCSSValue* values[numProps];
902 const nsCSSValueList* lists[numProps];
904 for (uint32_t i = 0; i < numProps; ++i) {
905 values[i] = data->ValueFor(subprops[i]);
906 NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
907 values[i]->GetUnit() == eCSSUnit_ListDep,
908 nsPrintfCString("bad a-duration unit %d",
909 values[i]->GetUnit()).get());
910 lists[i] = values[i]->GetListValue();
913 for (;;) {
914 // We must serialize 'animation-name' last in case it has
915 // a value that conflicts with one of the other keyword properties.
916 NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
917 eCSSProperty_animation_name,
918 "animation-name must be last");
919 bool done = false;
920 for (uint32_t i = 0;;) {
921 lists[i]->mValue.AppendToString(subprops[i], aValue, aSerialization);
922 lists[i] = lists[i]->mNext;
923 if (!lists[i]) {
924 done = true;
926 if (++i == numProps) {
927 break;
929 aValue.Append(char16_t(' '));
931 if (done) {
932 break;
934 aValue.AppendLiteral(", ");
936 for (uint32_t i = 0; i < numProps; ++i) {
937 if (lists[i]) {
938 // Lists not all the same length, can't use shorthand.
939 aValue.Truncate();
940 break;
943 break;
945 case eCSSProperty_marker: {
946 const nsCSSValue &endValue =
947 *data->ValueFor(eCSSProperty_marker_end);
948 const nsCSSValue &midValue =
949 *data->ValueFor(eCSSProperty_marker_mid);
950 const nsCSSValue &startValue =
951 *data->ValueFor(eCSSProperty_marker_start);
952 if (endValue == midValue && midValue == startValue)
953 AppendValueToString(eCSSProperty_marker_end, aValue, aSerialization);
954 break;
956 case eCSSProperty__moz_columns: {
957 // Two values, column-count and column-width, separated by a space.
958 const nsCSSProperty* subprops =
959 nsCSSProps::SubpropertyEntryFor(aProperty);
960 AppendValueToString(subprops[0], aValue, aSerialization);
961 aValue.Append(char16_t(' '));
962 AppendValueToString(subprops[1], aValue, aSerialization);
963 break;
965 case eCSSProperty_flex: {
966 // flex-grow, flex-shrink, flex-basis, separated by single space
967 const nsCSSProperty* subprops =
968 nsCSSProps::SubpropertyEntryFor(aProperty);
970 AppendValueToString(subprops[0], aValue, aSerialization);
971 aValue.Append(char16_t(' '));
972 AppendValueToString(subprops[1], aValue, aSerialization);
973 aValue.Append(char16_t(' '));
974 AppendValueToString(subprops[2], aValue, aSerialization);
975 break;
977 case eCSSProperty_flex_flow: {
978 // flex-direction, flex-wrap, separated by single space
979 const nsCSSProperty* subprops =
980 nsCSSProps::SubpropertyEntryFor(aProperty);
981 NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
982 "must have exactly two subproperties");
984 AppendValueToString(subprops[0], aValue, aSerialization);
985 aValue.Append(char16_t(' '));
986 AppendValueToString(subprops[1], aValue, aSerialization);
987 break;
989 case eCSSProperty_grid_row:
990 case eCSSProperty_grid_column: {
991 // grid-{row,column}-start, grid-{row,column}-end, separated by a slash
992 const nsCSSProperty* subprops =
993 nsCSSProps::SubpropertyEntryFor(aProperty);
994 NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
995 "must have exactly two subproperties");
997 // TODO: should we simplify when possible?
998 AppendValueToString(subprops[0], aValue, aSerialization);
999 aValue.AppendLiteral(" / ");
1000 AppendValueToString(subprops[1], aValue, aSerialization);
1001 break;
1003 case eCSSProperty_grid_area: {
1004 const nsCSSProperty* subprops =
1005 nsCSSProps::SubpropertyEntryFor(aProperty);
1006 NS_ABORT_IF_FALSE(subprops[4] == eCSSProperty_UNKNOWN,
1007 "must have exactly four subproperties");
1009 // TODO: should we simplify when possible?
1010 AppendValueToString(subprops[0], aValue, aSerialization);
1011 aValue.AppendLiteral(" / ");
1012 AppendValueToString(subprops[1], aValue, aSerialization);
1013 aValue.AppendLiteral(" / ");
1014 AppendValueToString(subprops[2], aValue, aSerialization);
1015 aValue.AppendLiteral(" / ");
1016 AppendValueToString(subprops[3], aValue, aSerialization);
1017 break;
1020 // This can express either grid-template-{areas,columns,rows}
1021 // or grid-auto-{flow,columns,rows}, but not both.
1022 case eCSSProperty_grid: {
1023 const nsCSSValue& areasValue =
1024 *data->ValueFor(eCSSProperty_grid_template_areas);
1025 const nsCSSValue& columnsValue =
1026 *data->ValueFor(eCSSProperty_grid_template_columns);
1027 const nsCSSValue& rowsValue =
1028 *data->ValueFor(eCSSProperty_grid_template_rows);
1030 const nsCSSValue& autoFlowValue =
1031 *data->ValueFor(eCSSProperty_grid_auto_flow);
1032 const nsCSSValue& autoColumnsValue =
1033 *data->ValueFor(eCSSProperty_grid_auto_columns);
1034 const nsCSSValue& autoRowsValue =
1035 *data->ValueFor(eCSSProperty_grid_auto_rows);
1037 if (areasValue.GetUnit() == eCSSUnit_None &&
1038 columnsValue.GetUnit() == eCSSUnit_None &&
1039 rowsValue.GetUnit() == eCSSUnit_None) {
1040 AppendValueToString(eCSSProperty_grid_auto_flow,
1041 aValue, aSerialization);
1042 aValue.Append(char16_t(' '));
1043 AppendValueToString(eCSSProperty_grid_auto_columns,
1044 aValue, aSerialization);
1045 aValue.AppendLiteral(" / ");
1046 AppendValueToString(eCSSProperty_grid_auto_rows,
1047 aValue, aSerialization);
1048 break;
1049 } else if (!(autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
1050 autoFlowValue.GetIntValue() == NS_STYLE_GRID_AUTO_FLOW_ROW &&
1051 autoColumnsValue.GetUnit() == eCSSUnit_Auto &&
1052 autoRowsValue.GetUnit() == eCSSUnit_Auto)) {
1053 // Not serializable, bail.
1054 return;
1056 // Fall through to eCSSProperty_grid_template
1058 case eCSSProperty_grid_template: {
1059 const nsCSSValue& areasValue =
1060 *data->ValueFor(eCSSProperty_grid_template_areas);
1061 const nsCSSValue& columnsValue =
1062 *data->ValueFor(eCSSProperty_grid_template_columns);
1063 const nsCSSValue& rowsValue =
1064 *data->ValueFor(eCSSProperty_grid_template_rows);
1065 if (areasValue.GetUnit() == eCSSUnit_None) {
1066 AppendValueToString(eCSSProperty_grid_template_columns,
1067 aValue, aSerialization);
1068 aValue.AppendLiteral(" / ");
1069 AppendValueToString(eCSSProperty_grid_template_rows,
1070 aValue, aSerialization);
1071 break;
1073 if (columnsValue.GetUnit() == eCSSUnit_List ||
1074 columnsValue.GetUnit() == eCSSUnit_ListDep) {
1075 const nsCSSValueList* columnsItem = columnsValue.GetListValue();
1076 if (columnsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
1077 columnsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
1078 // We have "grid-template-areas:[something]; grid-template-columns:subgrid"
1079 // which isn't a value that the shorthand can express. Bail.
1080 return;
1083 if (rowsValue.GetUnit() != eCSSUnit_List &&
1084 rowsValue.GetUnit() != eCSSUnit_ListDep) {
1085 // We have "grid-template-areas:[something]; grid-template-rows:none"
1086 // which isn't a value that the shorthand can express. Bail.
1087 return;
1089 const nsCSSValueList* rowsItem = rowsValue.GetListValue();
1090 if (rowsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
1091 rowsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
1092 // We have "grid-template-areas:[something]; grid-template-rows:subgrid"
1093 // which isn't a value that the shorthand can express. Bail.
1094 return;
1096 const GridTemplateAreasValue* areas = areasValue.GetGridTemplateAreas();
1097 uint32_t nRowItems = 0;
1098 while (rowsItem) {
1099 nRowItems++;
1100 rowsItem = rowsItem->mNext;
1102 MOZ_ASSERT(nRowItems % 2 == 1, "expected an odd number of items");
1103 if ((nRowItems - 1) / 2 != areas->NRows()) {
1104 // Not serializable, bail.
1105 return;
1107 if (columnsValue.GetUnit() != eCSSUnit_None) {
1108 AppendValueToString(eCSSProperty_grid_template_columns,
1109 aValue, aSerialization);
1110 aValue.AppendLiteral(" / ");
1112 rowsItem = rowsValue.GetListValue();
1113 uint32_t row = 0;
1114 for (;;) {
1115 bool addSpaceSeparator = true;
1116 nsCSSUnit unit = rowsItem->mValue.GetUnit();
1118 if (unit == eCSSUnit_Null) {
1119 // Empty or omitted <line-names>. Serializes to nothing.
1120 addSpaceSeparator = false; // Avoid a double space.
1122 } else if (unit == eCSSUnit_List || unit == eCSSUnit_ListDep) {
1123 // Non-empty <line-names>
1124 aValue.Append('(');
1125 rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
1126 aValue, aSerialization);
1127 aValue.Append(')');
1129 } else {
1130 nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[row++], aValue);
1131 aValue.Append(char16_t(' '));
1133 // <track-size>
1134 rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
1135 aValue, aSerialization);
1136 if (rowsItem->mNext &&
1137 rowsItem->mNext->mValue.GetUnit() == eCSSUnit_Null &&
1138 !rowsItem->mNext->mNext) {
1139 // Break out of the loop early to avoid a trailing space.
1140 break;
1144 rowsItem = rowsItem->mNext;
1145 if (!rowsItem) {
1146 break;
1149 if (addSpaceSeparator) {
1150 aValue.Append(char16_t(' '));
1153 break;
1155 case eCSSProperty__moz_transform: {
1156 // shorthands that are just aliases with different parsing rules
1157 const nsCSSProperty* subprops =
1158 nsCSSProps::SubpropertyEntryFor(aProperty);
1159 NS_ABORT_IF_FALSE(subprops[1] == eCSSProperty_UNKNOWN,
1160 "must have exactly one subproperty");
1161 AppendValueToString(subprops[0], aValue, aSerialization);
1162 break;
1164 case eCSSProperty_all:
1165 // If we got here, then we didn't have all "inherit" or "initial" or
1166 // "unset" values for all of the longhand property components of 'all'.
1167 // There is no other possible value that is valid for all properties,
1168 // so serialize as the empty string.
1169 break;
1170 default:
1171 NS_ABORT_IF_FALSE(false, "no other shorthands");
1172 break;
1176 bool
1177 Declaration::GetValueIsImportant(const nsAString& aProperty) const
1179 nsCSSProperty propID =
1180 nsCSSProps::LookupProperty(aProperty, nsCSSProps::eIgnoreEnabledState);
1181 if (propID == eCSSProperty_UNKNOWN) {
1182 return false;
1184 if (propID == eCSSPropertyExtra_variable) {
1185 const nsSubstring& variableName =
1186 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);
1187 return GetVariableValueIsImportant(variableName);
1189 return GetValueIsImportant(propID);
1192 bool
1193 Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
1195 if (!mImportantData)
1196 return false;
1198 // Calling ValueFor is inefficient, but we can assume '!important' is rare.
1200 if (!nsCSSProps::IsShorthand(aProperty)) {
1201 return mImportantData->ValueFor(aProperty) != nullptr;
1204 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
1205 if (*p == eCSSProperty__x_system_font) {
1206 // The system_font subproperty doesn't count.
1207 continue;
1209 if (!mImportantData->ValueFor(*p)) {
1210 return false;
1213 return true;
1216 void
1217 Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty,
1218 nsAutoString& aValue,
1219 nsAString& aResult) const
1221 NS_ABORT_IF_FALSE(0 <= aProperty && aProperty < eCSSProperty_COUNT,
1222 "property enum out of range");
1223 NS_ABORT_IF_FALSE((aProperty < eCSSProperty_COUNT_no_shorthands) ==
1224 aValue.IsEmpty(),
1225 "aValue should be given for shorthands but not longhands");
1226 AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
1227 aResult.AppendLiteral(": ");
1228 if (aValue.IsEmpty())
1229 AppendValueToString(aProperty, aResult, nsCSSValue::eNormalized);
1230 else
1231 aResult.Append(aValue);
1232 if (GetValueIsImportant(aProperty)) {
1233 aResult.AppendLiteral(" ! important");
1235 aResult.AppendLiteral("; ");
1238 void
1239 Declaration::AppendVariableAndValueToString(const nsAString& aName,
1240 nsAString& aResult) const
1242 aResult.AppendLiteral("--");
1243 aResult.Append(aName);
1244 CSSVariableDeclarations::Type type;
1245 nsString value;
1246 bool important;
1248 if (mImportantVariables && mImportantVariables->Get(aName, type, value)) {
1249 important = true;
1250 } else {
1251 MOZ_ASSERT(mVariables);
1252 MOZ_ASSERT(mVariables->Has(aName));
1253 mVariables->Get(aName, type, value);
1254 important = false;
1257 switch (type) {
1258 case CSSVariableDeclarations::eTokenStream:
1259 if (value.IsEmpty()) {
1260 aResult.Append(':');
1261 } else {
1262 aResult.AppendLiteral(": ");
1263 aResult.Append(value);
1265 break;
1267 case CSSVariableDeclarations::eInitial:
1268 aResult.AppendLiteral("initial");
1269 break;
1271 case CSSVariableDeclarations::eInherit:
1272 aResult.AppendLiteral("inherit");
1273 break;
1275 case CSSVariableDeclarations::eUnset:
1276 aResult.AppendLiteral("unset");
1277 break;
1279 default:
1280 MOZ_ASSERT(false, "unexpected variable value type");
1283 if (important) {
1284 aResult.AppendLiteral("! important");
1286 aResult.AppendLiteral("; ");
1289 void
1290 Declaration::ToString(nsAString& aString) const
1292 // Someone cares about this declaration's contents, so don't let it
1293 // change from under them. See e.g. bug 338679.
1294 SetImmutable();
1296 nsCSSCompressedDataBlock *systemFontData =
1297 GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData;
1298 const nsCSSValue *systemFont =
1299 systemFontData->ValueFor(eCSSProperty__x_system_font);
1300 const bool haveSystemFont = systemFont &&
1301 systemFont->GetUnit() != eCSSUnit_None &&
1302 systemFont->GetUnit() != eCSSUnit_Null;
1303 bool didSystemFont = false;
1305 int32_t count = mOrder.Length();
1306 int32_t index;
1307 nsAutoTArray<nsCSSProperty, 16> shorthandsUsed;
1308 for (index = 0; index < count; index++) {
1309 nsCSSProperty property = GetPropertyAt(index);
1311 if (property == eCSSPropertyExtra_variable) {
1312 uint32_t variableIndex = mOrder[index] - eCSSProperty_COUNT;
1313 AppendVariableAndValueToString(mVariableOrder[variableIndex], aString);
1314 continue;
1317 if (!nsCSSProps::IsEnabled(property)) {
1318 continue;
1320 bool doneProperty = false;
1322 // If we already used this property in a shorthand, skip it.
1323 if (shorthandsUsed.Length() > 0) {
1324 for (const nsCSSProperty *shorthands =
1325 nsCSSProps::ShorthandsContaining(property);
1326 *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
1327 if (shorthandsUsed.Contains(*shorthands)) {
1328 doneProperty = true;
1329 break;
1332 if (doneProperty)
1333 continue;
1336 // Try to use this property in a shorthand.
1337 nsAutoString value;
1338 for (const nsCSSProperty *shorthands =
1339 nsCSSProps::ShorthandsContaining(property);
1340 *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
1341 // ShorthandsContaining returns the shorthands in order from those
1342 // that contain the most subproperties to those that contain the
1343 // least, which is exactly the order we want to test them.
1344 nsCSSProperty shorthand = *shorthands;
1346 GetValue(shorthand, value);
1348 // in the system font case, skip over font-variant shorthand, since all
1349 // subproperties are already dealt with via the font shorthand
1350 if (shorthand == eCSSProperty_font_variant &&
1351 value.EqualsLiteral("-moz-use-system-font")) {
1352 continue;
1355 // If GetValue gives us a non-empty string back, we can use that
1356 // value; otherwise it's not possible to use this shorthand.
1357 if (!value.IsEmpty()) {
1358 AppendPropertyAndValueToString(shorthand, value, aString);
1359 shorthandsUsed.AppendElement(shorthand);
1360 doneProperty = true;
1361 break;
1364 if (shorthand == eCSSProperty_font) {
1365 if (haveSystemFont && !didSystemFont) {
1366 // Output the shorthand font declaration that we will
1367 // partially override later. But don't add it to
1368 // |shorthandsUsed|, since we will have to override it.
1369 systemFont->AppendToString(eCSSProperty__x_system_font, value,
1370 nsCSSValue::eNormalized);
1371 AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
1372 value.Truncate();
1373 didSystemFont = true;
1376 // That we output the system font is enough for this property if:
1377 // (1) it's the hidden system font subproperty (which either
1378 // means we output it or we don't have it), or
1379 // (2) its value is the hidden system font value and it matches
1380 // the hidden system font subproperty in importance, and
1381 // we output the system font subproperty.
1382 const nsCSSValue *val = systemFontData->ValueFor(property);
1383 if (property == eCSSProperty__x_system_font ||
1384 (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
1385 doneProperty = true;
1386 break;
1390 if (doneProperty)
1391 continue;
1393 NS_ABORT_IF_FALSE(value.IsEmpty(), "value should be empty now");
1394 AppendPropertyAndValueToString(property, value, aString);
1396 if (! aString.IsEmpty()) {
1397 // if the string is not empty, we have trailing whitespace we
1398 // should remove
1399 aString.Truncate(aString.Length() - 1);
1403 #ifdef DEBUG
1404 void
1405 Declaration::List(FILE* out, int32_t aIndent) const
1407 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
1409 fputs("{ ", out);
1410 nsAutoString s;
1411 ToString(s);
1412 fputs(NS_ConvertUTF16toUTF8(s).get(), out);
1413 fputs("}", out);
1415 #endif
1417 bool
1418 Declaration::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
1420 aReturn.Truncate();
1421 if (aIndex < mOrder.Length()) {
1422 nsCSSProperty property = GetPropertyAt(aIndex);
1423 if (property == eCSSPropertyExtra_variable) {
1424 GetCustomPropertyNameAt(aIndex, aReturn);
1425 return true;
1427 if (0 <= property) {
1428 AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
1429 return true;
1432 return false;
1435 void
1436 Declaration::InitializeEmpty()
1438 NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
1439 mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
1442 Declaration*
1443 Declaration::EnsureMutable()
1445 NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
1446 if (!IsMutable()) {
1447 return new Declaration(*this);
1448 } else {
1449 return this;
1453 size_t
1454 Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
1456 size_t n = aMallocSizeOf(this);
1457 n += mOrder.SizeOfExcludingThis(aMallocSizeOf);
1458 n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0;
1459 n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
1460 if (mVariables) {
1461 n += mVariables->SizeOfIncludingThis(aMallocSizeOf);
1463 if (mImportantVariables) {
1464 n += mImportantVariables->SizeOfIncludingThis(aMallocSizeOf);
1466 return n;
1469 bool
1470 Declaration::HasVariableDeclaration(const nsAString& aName) const
1472 return (mVariables && mVariables->Has(aName)) ||
1473 (mImportantVariables && mImportantVariables->Has(aName));
1476 void
1477 Declaration::GetVariableDeclaration(const nsAString& aName,
1478 nsAString& aValue) const
1480 aValue.Truncate();
1482 CSSVariableDeclarations::Type type;
1483 nsString value;
1485 if ((mImportantVariables && mImportantVariables->Get(aName, type, value)) ||
1486 (mVariables && mVariables->Get(aName, type, value))) {
1487 switch (type) {
1488 case CSSVariableDeclarations::eTokenStream:
1489 aValue.Append(value);
1490 break;
1492 case CSSVariableDeclarations::eInitial:
1493 aValue.AppendLiteral("initial");
1494 break;
1496 case CSSVariableDeclarations::eInherit:
1497 aValue.AppendLiteral("inherit");
1498 break;
1500 case CSSVariableDeclarations::eUnset:
1501 aValue.AppendLiteral("unset");
1502 break;
1504 default:
1505 MOZ_ASSERT(false, "unexpected variable value type");
1510 void
1511 Declaration::AddVariableDeclaration(const nsAString& aName,
1512 CSSVariableDeclarations::Type aType,
1513 const nsString& aValue,
1514 bool aIsImportant,
1515 bool aOverrideImportant)
1517 MOZ_ASSERT(IsMutable());
1519 nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
1520 if (index == nsTArray<nsString>::NoIndex) {
1521 index = mVariableOrder.Length();
1522 mVariableOrder.AppendElement(aName);
1525 if (!aIsImportant && !aOverrideImportant &&
1526 mImportantVariables && mImportantVariables->Has(aName)) {
1527 return;
1530 CSSVariableDeclarations* variables;
1531 if (aIsImportant) {
1532 if (mVariables) {
1533 mVariables->Remove(aName);
1535 if (!mImportantVariables) {
1536 mImportantVariables = new CSSVariableDeclarations;
1538 variables = mImportantVariables;
1539 } else {
1540 if (mImportantVariables) {
1541 mImportantVariables->Remove(aName);
1543 if (!mVariables) {
1544 mVariables = new CSSVariableDeclarations;
1546 variables = mVariables;
1549 switch (aType) {
1550 case CSSVariableDeclarations::eTokenStream:
1551 variables->PutTokenStream(aName, aValue);
1552 break;
1554 case CSSVariableDeclarations::eInitial:
1555 MOZ_ASSERT(aValue.IsEmpty());
1556 variables->PutInitial(aName);
1557 break;
1559 case CSSVariableDeclarations::eInherit:
1560 MOZ_ASSERT(aValue.IsEmpty());
1561 variables->PutInherit(aName);
1562 break;
1564 case CSSVariableDeclarations::eUnset:
1565 MOZ_ASSERT(aValue.IsEmpty());
1566 variables->PutUnset(aName);
1567 break;
1569 default:
1570 MOZ_ASSERT(false, "unexpected aType value");
1573 uint32_t propertyIndex = index + eCSSProperty_COUNT;
1574 mOrder.RemoveElement(propertyIndex);
1575 mOrder.AppendElement(propertyIndex);
1578 void
1579 Declaration::RemoveVariableDeclaration(const nsAString& aName)
1581 if (mVariables) {
1582 mVariables->Remove(aName);
1584 if (mImportantVariables) {
1585 mImportantVariables->Remove(aName);
1587 nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
1588 if (index != nsTArray<nsString>::NoIndex) {
1589 mOrder.RemoveElement(index + eCSSProperty_COUNT);
1593 bool
1594 Declaration::GetVariableValueIsImportant(const nsAString& aName) const
1596 return mImportantVariables && mImportantVariables->Has(aName);
1599 } // namespace mozilla::css
1600 } // namespace mozilla