Wed, 31 Dec 2014 07:16:47 +0100
Revert simplistic fix pending revisit of Mozilla integration attempt.
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)
24 {
25 MOZ_COUNT_CTOR(mozilla::css::Declaration);
26 }
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)
41 {
42 MOZ_COUNT_CTOR(mozilla::css::Declaration);
43 }
45 Declaration::~Declaration()
46 {
47 MOZ_COUNT_DTOR(mozilla::css::Declaration);
48 }
50 void
51 Declaration::ValueAppended(nsCSSProperty aProperty)
52 {
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));
60 }
62 void
63 Declaration::RemoveProperty(nsCSSProperty aProperty)
64 {
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));
75 }
76 } else {
77 data.ClearLonghandProperty(aProperty);
78 mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
79 }
81 CompressFrom(&data);
82 }
84 bool
85 Declaration::HasProperty(nsCSSProperty aProperty) const
86 {
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;
95 }
97 bool
98 Declaration::AppendValueToString(nsCSSProperty aProperty,
99 nsAString& aResult,
100 nsCSSValue::Serialization aSerialization) const
101 {
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;
111 }
113 val->AppendToString(aProperty, aResult, aSerialization);
114 return true;
115 }
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)
124 {
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);
144 }
145 }
146 }
147 }
149 void
150 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
151 {
152 GetValue(aProperty, aValue, nsCSSValue::eNormalized);
153 }
155 void
156 Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const
157 {
158 GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified);
159 }
161 void
162 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue,
163 nsCSSValue::Serialization aSerialization) const
164 {
165 aValue.Truncate(0);
167 // simple properties are easy.
168 if (!nsCSSProps::IsShorthand(aProperty)) {
169 AppendValueToString(aProperty, aValue, aSerialization);
170 return;
171 }
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.
194 //
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;
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;
208 }
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);
216 }
217 if (!val) {
218 // Case (1) above: some subproperties not specified.
219 return;
220 }
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 val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) {
229 tokenStream = val;
230 ++matchingTokenStreamCount;
231 }
232 }
233 if (importantCount != 0 && importantCount != totalCount) {
234 // Case (3), no consistent importance.
235 return;
236 }
237 if (initialCount == totalCount) {
238 // Simplify serialization below by serializing initial up-front.
239 nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue,
240 nsCSSValue::eNormalized);
241 return;
242 }
243 if (inheritCount == totalCount) {
244 // Simplify serialization below by serializing inherit up-front.
245 nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue,
246 nsCSSValue::eNormalized);
247 return;
248 }
249 if (unsetCount == totalCount) {
250 // Simplify serialization below by serializing unset up-front.
251 nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue,
252 nsCSSValue::eNormalized);
253 return;
254 }
255 if (initialCount != 0 || inheritCount != 0 || unsetCount != 0) {
256 // Case (2): partially initial, inherit or unset.
257 return;
258 }
259 if (tokenStream) {
260 if (matchingTokenStreamCount == totalCount) {
261 // Shorthand was specified using variable references and all of its
262 // longhand components were set by the shorthand.
263 aValue.Append(tokenStream->GetTokenStreamValue()->mTokenStream);
264 } else {
265 // In all other cases, serialize to the empty string.
266 }
267 return;
268 }
270 nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
271 switch (aProperty) {
272 case eCSSProperty_margin:
273 case eCSSProperty_padding:
274 case eCSSProperty_border_color:
275 case eCSSProperty_border_style:
276 case eCSSProperty_border_width: {
277 const nsCSSProperty* subprops =
278 nsCSSProps::SubpropertyEntryFor(aProperty);
279 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
280 kNotFound, "first subprop must be top");
281 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
282 kNotFound, "second subprop must be right");
283 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
284 kNotFound, "third subprop must be bottom");
285 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
286 kNotFound, "fourth subprop must be left");
287 const nsCSSValue* vals[4] = {
288 data->ValueFor(subprops[0]),
289 data->ValueFor(subprops[1]),
290 data->ValueFor(subprops[2]),
291 data->ValueFor(subprops[3])
292 };
293 AppendSidesShorthandToString(subprops, vals, aValue, aSerialization);
294 break;
295 }
296 case eCSSProperty_border_radius:
297 case eCSSProperty__moz_outline_radius: {
298 const nsCSSProperty* subprops =
299 nsCSSProps::SubpropertyEntryFor(aProperty);
300 const nsCSSValue* vals[4] = {
301 data->ValueFor(subprops[0]),
302 data->ValueFor(subprops[1]),
303 data->ValueFor(subprops[2]),
304 data->ValueFor(subprops[3])
305 };
307 // For compatibility, only write a slash and the y-values
308 // if they're not identical to the x-values.
309 bool needY = false;
310 const nsCSSValue* xVals[4];
311 const nsCSSValue* yVals[4];
312 for (int i = 0; i < 4; i++) {
313 if (vals[i]->GetUnit() == eCSSUnit_Pair) {
314 needY = true;
315 xVals[i] = &vals[i]->GetPairValue().mXValue;
316 yVals[i] = &vals[i]->GetPairValue().mYValue;
317 } else {
318 xVals[i] = yVals[i] = vals[i];
319 }
320 }
322 AppendSidesShorthandToString(subprops, xVals, aValue, aSerialization);
323 if (needY) {
324 aValue.AppendLiteral(" / ");
325 AppendSidesShorthandToString(subprops, yVals, aValue, aSerialization);
326 }
327 break;
328 }
329 case eCSSProperty_border_image: {
330 // Even though there are some cases where we could omit
331 // 'border-image-source' (when it's none), it's probably not a
332 // good idea since it's likely to be confusing. It would also
333 // require adding the extra check that we serialize *something*.
334 AppendValueToString(eCSSProperty_border_image_source, aValue,
335 aSerialization);
337 bool sliceDefault = data->HasDefaultBorderImageSlice();
338 bool widthDefault = data->HasDefaultBorderImageWidth();
339 bool outsetDefault = data->HasDefaultBorderImageOutset();
341 if (!sliceDefault || !widthDefault || !outsetDefault) {
342 aValue.Append(char16_t(' '));
343 AppendValueToString(eCSSProperty_border_image_slice, aValue,
344 aSerialization);
345 if (!widthDefault || !outsetDefault) {
346 aValue.Append(NS_LITERAL_STRING(" /"));
347 if (!widthDefault) {
348 aValue.Append(char16_t(' '));
349 AppendValueToString(eCSSProperty_border_image_width, aValue,
350 aSerialization);
351 }
352 if (!outsetDefault) {
353 aValue.Append(NS_LITERAL_STRING(" / "));
354 AppendValueToString(eCSSProperty_border_image_outset, aValue,
355 aSerialization);
356 }
357 }
358 }
360 bool repeatDefault = data->HasDefaultBorderImageRepeat();
361 if (!repeatDefault) {
362 aValue.Append(char16_t(' '));
363 AppendValueToString(eCSSProperty_border_image_repeat, aValue,
364 aSerialization);
365 }
366 break;
367 }
368 case eCSSProperty_border: {
369 // If we have a non-default value for any of the properties that
370 // this shorthand sets but cannot specify, we have to return the
371 // empty string.
372 if (data->ValueFor(eCSSProperty_border_image_source)->GetUnit() !=
373 eCSSUnit_None ||
374 !data->HasDefaultBorderImageSlice() ||
375 !data->HasDefaultBorderImageWidth() ||
376 !data->HasDefaultBorderImageOutset() ||
377 !data->HasDefaultBorderImageRepeat() ||
378 data->ValueFor(eCSSProperty_border_top_colors)->GetUnit() !=
379 eCSSUnit_None ||
380 data->ValueFor(eCSSProperty_border_right_colors)->GetUnit() !=
381 eCSSUnit_None ||
382 data->ValueFor(eCSSProperty_border_bottom_colors)->GetUnit() !=
383 eCSSUnit_None ||
384 data->ValueFor(eCSSProperty_border_left_colors)->GetUnit() !=
385 eCSSUnit_None) {
386 break;
387 }
389 const nsCSSProperty* subproptables[3] = {
390 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
391 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
392 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
393 };
394 bool match = true;
395 for (const nsCSSProperty** subprops = subproptables,
396 **subprops_end = ArrayEnd(subproptables);
397 subprops < subprops_end; ++subprops) {
398 // Check only the first four subprops in each table, since the
399 // others are extras for dimensional box properties.
400 const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
401 for (int32_t side = 1; side < 4; ++side) {
402 const nsCSSValue *otherSide =
403 data->ValueFor((*subprops)[side]);
404 if (*firstSide != *otherSide)
405 match = false;
406 }
407 }
408 if (!match) {
409 // We can't express what we have in the border shorthand
410 break;
411 }
412 // tweak aProperty and fall through
413 aProperty = eCSSProperty_border_top;
414 }
415 case eCSSProperty_border_top:
416 case eCSSProperty_border_right:
417 case eCSSProperty_border_bottom:
418 case eCSSProperty_border_left:
419 case eCSSProperty_border_start:
420 case eCSSProperty_border_end:
421 case eCSSProperty__moz_column_rule:
422 case eCSSProperty_outline: {
423 const nsCSSProperty* subprops =
424 nsCSSProps::SubpropertyEntryFor(aProperty);
425 NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
426 NS_LITERAL_CSTRING("-color")) ||
427 StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
428 NS_LITERAL_CSTRING("-color-value")),
429 "third subprop must be the color property");
430 const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
431 bool isMozUseTextColor =
432 colorValue->GetUnit() == eCSSUnit_Enumerated &&
433 colorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR;
434 if (!AppendValueToString(subprops[0], aValue, aSerialization) ||
435 !(aValue.Append(char16_t(' ')),
436 AppendValueToString(subprops[1], aValue, aSerialization)) ||
437 // Don't output a third value when it's -moz-use-text-color.
438 !(isMozUseTextColor ||
439 (aValue.Append(char16_t(' ')),
440 AppendValueToString(subprops[2], aValue, aSerialization)))) {
441 aValue.Truncate();
442 }
443 break;
444 }
445 case eCSSProperty_margin_left:
446 case eCSSProperty_margin_right:
447 case eCSSProperty_margin_start:
448 case eCSSProperty_margin_end:
449 case eCSSProperty_padding_left:
450 case eCSSProperty_padding_right:
451 case eCSSProperty_padding_start:
452 case eCSSProperty_padding_end:
453 case eCSSProperty_border_left_color:
454 case eCSSProperty_border_left_style:
455 case eCSSProperty_border_left_width:
456 case eCSSProperty_border_right_color:
457 case eCSSProperty_border_right_style:
458 case eCSSProperty_border_right_width:
459 case eCSSProperty_border_start_color:
460 case eCSSProperty_border_start_style:
461 case eCSSProperty_border_start_width:
462 case eCSSProperty_border_end_color:
463 case eCSSProperty_border_end_style:
464 case eCSSProperty_border_end_width: {
465 const nsCSSProperty* subprops =
466 nsCSSProps::SubpropertyEntryFor(aProperty);
467 NS_ABORT_IF_FALSE(subprops[3] == eCSSProperty_UNKNOWN,
468 "not box property with physical vs. logical cascading");
469 AppendValueToString(subprops[0], aValue, aSerialization);
470 break;
471 }
472 case eCSSProperty_background: {
473 // We know from above that all subproperties were specified.
474 // However, we still can't represent that in the shorthand unless
475 // they're all lists of the same length. So if they're different
476 // lengths, we need to bail out.
477 // We also need to bail out if an item has background-clip and
478 // background-origin that are different and not the default
479 // values. (We omit them if they're both default.)
480 const nsCSSValueList *image =
481 data->ValueFor(eCSSProperty_background_image)->
482 GetListValue();
483 const nsCSSValuePairList *repeat =
484 data->ValueFor(eCSSProperty_background_repeat)->
485 GetPairListValue();
486 const nsCSSValueList *attachment =
487 data->ValueFor(eCSSProperty_background_attachment)->
488 GetListValue();
489 const nsCSSValueList *position =
490 data->ValueFor(eCSSProperty_background_position)->
491 GetListValue();
492 const nsCSSValueList *clip =
493 data->ValueFor(eCSSProperty_background_clip)->
494 GetListValue();
495 const nsCSSValueList *origin =
496 data->ValueFor(eCSSProperty_background_origin)->
497 GetListValue();
498 const nsCSSValuePairList *size =
499 data->ValueFor(eCSSProperty_background_size)->
500 GetPairListValue();
501 for (;;) {
502 image->mValue.AppendToString(eCSSProperty_background_image, aValue,
503 aSerialization);
504 aValue.Append(char16_t(' '));
505 repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue,
506 aSerialization);
507 if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
508 repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue,
509 aSerialization);
510 }
511 aValue.Append(char16_t(' '));
512 attachment->mValue.AppendToString(eCSSProperty_background_attachment,
513 aValue, aSerialization);
514 aValue.Append(char16_t(' '));
515 position->mValue.AppendToString(eCSSProperty_background_position,
516 aValue, aSerialization);
518 if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
519 size->mYValue.GetUnit() != eCSSUnit_Auto) {
520 aValue.Append(char16_t(' '));
521 aValue.Append(char16_t('/'));
522 aValue.Append(char16_t(' '));
523 size->mXValue.AppendToString(eCSSProperty_background_size, aValue,
524 aSerialization);
525 aValue.Append(char16_t(' '));
526 size->mYValue.AppendToString(eCSSProperty_background_size, aValue,
527 aSerialization);
528 }
530 NS_ABORT_IF_FALSE(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
531 origin->mValue.GetUnit() == eCSSUnit_Enumerated,
532 "should not have inherit/initial within list");
534 if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
535 origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
536 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
537 eCSSProperty_background_origin] ==
538 nsCSSProps::kBackgroundOriginKTable);
539 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
540 eCSSProperty_background_clip] ==
541 nsCSSProps::kBackgroundOriginKTable);
542 static_assert(NS_STYLE_BG_CLIP_BORDER ==
543 NS_STYLE_BG_ORIGIN_BORDER &&
544 NS_STYLE_BG_CLIP_PADDING ==
545 NS_STYLE_BG_ORIGIN_PADDING &&
546 NS_STYLE_BG_CLIP_CONTENT ==
547 NS_STYLE_BG_ORIGIN_CONTENT,
548 "bg-clip and bg-origin style constants must agree");
549 aValue.Append(char16_t(' '));
550 origin->mValue.AppendToString(eCSSProperty_background_origin, aValue,
551 aSerialization);
553 if (clip->mValue != origin->mValue) {
554 aValue.Append(char16_t(' '));
555 clip->mValue.AppendToString(eCSSProperty_background_clip, aValue,
556 aSerialization);
557 }
558 }
560 image = image->mNext;
561 repeat = repeat->mNext;
562 attachment = attachment->mNext;
563 position = position->mNext;
564 clip = clip->mNext;
565 origin = origin->mNext;
566 size = size->mNext;
568 if (!image) {
569 if (repeat || attachment || position || clip || origin || size) {
570 // Uneven length lists, so can't be serialized as shorthand.
571 aValue.Truncate();
572 return;
573 }
574 break;
575 }
576 if (!repeat || !attachment || !position || !clip || !origin || !size) {
577 // Uneven length lists, so can't be serialized as shorthand.
578 aValue.Truncate();
579 return;
580 }
581 aValue.Append(char16_t(','));
582 aValue.Append(char16_t(' '));
583 }
585 aValue.Append(char16_t(' '));
586 AppendValueToString(eCSSProperty_background_color, aValue,
587 aSerialization);
588 break;
589 }
590 case eCSSProperty_font: {
591 // systemFont might not be present; other values are guaranteed to be
592 // available based on the shorthand check at the beginning of the
593 // function, as long as the prop is enabled
594 const nsCSSValue *systemFont =
595 data->ValueFor(eCSSProperty__x_system_font);
596 const nsCSSValue *style =
597 data->ValueFor(eCSSProperty_font_style);
598 const nsCSSValue *variant =
599 data->ValueFor(eCSSProperty_font_variant);
600 const nsCSSValue *weight =
601 data->ValueFor(eCSSProperty_font_weight);
602 const nsCSSValue *size =
603 data->ValueFor(eCSSProperty_font_size);
604 const nsCSSValue *lh =
605 data->ValueFor(eCSSProperty_line_height);
606 const nsCSSValue *family =
607 data->ValueFor(eCSSProperty_font_family);
608 const nsCSSValue *stretch =
609 data->ValueFor(eCSSProperty_font_stretch);
610 const nsCSSValue *sizeAdjust =
611 data->ValueFor(eCSSProperty_font_size_adjust);
612 const nsCSSValue *featureSettings =
613 data->ValueFor(eCSSProperty_font_feature_settings);
614 const nsCSSValue *languageOverride =
615 data->ValueFor(eCSSProperty_font_language_override);
616 const nsCSSValue *fontKerning =
617 data->ValueFor(eCSSProperty_font_kerning);
618 const nsCSSValue *fontSynthesis =
619 data->ValueFor(eCSSProperty_font_synthesis);
620 const nsCSSValue *fontVariantAlternates =
621 data->ValueFor(eCSSProperty_font_variant_alternates);
622 const nsCSSValue *fontVariantCaps =
623 data->ValueFor(eCSSProperty_font_variant_caps);
624 const nsCSSValue *fontVariantEastAsian =
625 data->ValueFor(eCSSProperty_font_variant_east_asian);
626 const nsCSSValue *fontVariantLigatures =
627 data->ValueFor(eCSSProperty_font_variant_ligatures);
628 const nsCSSValue *fontVariantNumeric =
629 data->ValueFor(eCSSProperty_font_variant_numeric);
630 const nsCSSValue *fontVariantPosition =
631 data->ValueFor(eCSSProperty_font_variant_position);
633 // if font features are not enabled, pointers for fontVariant
634 // values above may be null since the shorthand check ignores them
635 // font-variant-alternates enabled ==> layout.css.font-features.enabled is true
636 bool fontFeaturesEnabled =
637 nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
639 if (systemFont &&
640 systemFont->GetUnit() != eCSSUnit_None &&
641 systemFont->GetUnit() != eCSSUnit_Null) {
642 if (style->GetUnit() != eCSSUnit_System_Font ||
643 variant->GetUnit() != eCSSUnit_System_Font ||
644 weight->GetUnit() != eCSSUnit_System_Font ||
645 size->GetUnit() != eCSSUnit_System_Font ||
646 lh->GetUnit() != eCSSUnit_System_Font ||
647 family->GetUnit() != eCSSUnit_System_Font ||
648 stretch->GetUnit() != eCSSUnit_System_Font ||
649 sizeAdjust->GetUnit() != eCSSUnit_System_Font ||
650 featureSettings->GetUnit() != eCSSUnit_System_Font ||
651 languageOverride->GetUnit() != eCSSUnit_System_Font ||
652 (fontFeaturesEnabled &&
653 (fontKerning->GetUnit() != eCSSUnit_System_Font ||
654 fontSynthesis->GetUnit() != eCSSUnit_System_Font ||
655 fontVariantAlternates->GetUnit() != eCSSUnit_System_Font ||
656 fontVariantCaps->GetUnit() != eCSSUnit_System_Font ||
657 fontVariantEastAsian->GetUnit() != eCSSUnit_System_Font ||
658 fontVariantLigatures->GetUnit() != eCSSUnit_System_Font ||
659 fontVariantNumeric->GetUnit() != eCSSUnit_System_Font ||
660 fontVariantPosition->GetUnit() != eCSSUnit_System_Font))) {
661 // This can't be represented as a shorthand.
662 return;
663 }
664 systemFont->AppendToString(eCSSProperty__x_system_font, aValue,
665 aSerialization);
666 } else {
667 // properties reset by this shorthand property to their
668 // initial values but not represented in its syntax
669 if (stretch->GetUnit() != eCSSUnit_Enumerated ||
670 stretch->GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
671 sizeAdjust->GetUnit() != eCSSUnit_None ||
672 featureSettings->GetUnit() != eCSSUnit_Normal ||
673 languageOverride->GetUnit() != eCSSUnit_Normal ||
674 (fontFeaturesEnabled &&
675 (fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO ||
676 fontSynthesis->GetUnit() != eCSSUnit_Enumerated ||
677 fontSynthesis->GetIntValue() !=
678 (NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) ||
679 fontVariantAlternates->GetUnit() != eCSSUnit_Normal ||
680 fontVariantCaps->GetUnit() != eCSSUnit_Normal ||
681 fontVariantEastAsian->GetUnit() != eCSSUnit_Normal ||
682 fontVariantLigatures->GetUnit() != eCSSUnit_Normal ||
683 fontVariantNumeric->GetUnit() != eCSSUnit_Normal ||
684 fontVariantPosition->GetUnit() != eCSSUnit_Normal))) {
685 return;
686 }
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(' '));
693 }
694 if (variant->GetUnit() != eCSSUnit_Enumerated ||
695 variant->GetIntValue() != NS_FONT_VARIANT_NORMAL) {
696 variant->AppendToString(eCSSProperty_font_variant, aValue,
697 aSerialization);
698 aValue.Append(char16_t(' '));
699 }
700 if (weight->GetUnit() != eCSSUnit_Enumerated ||
701 weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
702 weight->AppendToString(eCSSProperty_font_weight, aValue,
703 aSerialization);
704 aValue.Append(char16_t(' '));
705 }
706 size->AppendToString(eCSSProperty_font_size, aValue, aSerialization);
707 if (lh->GetUnit() != eCSSUnit_Normal) {
708 aValue.Append(char16_t('/'));
709 lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization);
710 }
711 aValue.Append(char16_t(' '));
712 family->AppendToString(eCSSProperty_font_family, aValue,
713 aSerialization);
714 }
715 break;
716 }
717 case eCSSProperty_list_style:
718 if (AppendValueToString(eCSSProperty_list_style_type, aValue,
719 aSerialization)) {
720 aValue.Append(char16_t(' '));
721 }
722 if (AppendValueToString(eCSSProperty_list_style_position, aValue,
723 aSerialization)) {
724 aValue.Append(char16_t(' '));
725 }
726 AppendValueToString(eCSSProperty_list_style_image, aValue,
727 aSerialization);
728 break;
729 case eCSSProperty_overflow: {
730 const nsCSSValue &xValue =
731 *data->ValueFor(eCSSProperty_overflow_x);
732 const nsCSSValue &yValue =
733 *data->ValueFor(eCSSProperty_overflow_y);
734 if (xValue == yValue)
735 xValue.AppendToString(eCSSProperty_overflow_x, aValue, aSerialization);
736 break;
737 }
738 case eCSSProperty_text_decoration: {
739 // If text-decoration-color or text-decoration-style isn't initial value,
740 // we cannot serialize the text-decoration shorthand value.
741 const nsCSSValue *decorationColor =
742 data->ValueFor(eCSSProperty_text_decoration_color);
743 const nsCSSValue *decorationStyle =
744 data->ValueFor(eCSSProperty_text_decoration_style);
746 NS_ABORT_IF_FALSE(decorationStyle->GetUnit() == eCSSUnit_Enumerated,
747 nsPrintfCString("bad text-decoration-style unit %d",
748 decorationStyle->GetUnit()).get());
750 if (decorationColor->GetUnit() != eCSSUnit_Enumerated ||
751 decorationColor->GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR ||
752 decorationStyle->GetIntValue() !=
753 NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
754 return;
755 }
757 AppendValueToString(eCSSProperty_text_decoration_line, aValue,
758 aSerialization);
759 break;
760 }
761 case eCSSProperty_transition: {
762 const nsCSSValue *transProp =
763 data->ValueFor(eCSSProperty_transition_property);
764 const nsCSSValue *transDuration =
765 data->ValueFor(eCSSProperty_transition_duration);
766 const nsCSSValue *transTiming =
767 data->ValueFor(eCSSProperty_transition_timing_function);
768 const nsCSSValue *transDelay =
769 data->ValueFor(eCSSProperty_transition_delay);
771 NS_ABORT_IF_FALSE(transDuration->GetUnit() == eCSSUnit_List ||
772 transDuration->GetUnit() == eCSSUnit_ListDep,
773 nsPrintfCString("bad t-duration unit %d",
774 transDuration->GetUnit()).get());
775 NS_ABORT_IF_FALSE(transTiming->GetUnit() == eCSSUnit_List ||
776 transTiming->GetUnit() == eCSSUnit_ListDep,
777 nsPrintfCString("bad t-timing unit %d",
778 transTiming->GetUnit()).get());
779 NS_ABORT_IF_FALSE(transDelay->GetUnit() == eCSSUnit_List ||
780 transDelay->GetUnit() == eCSSUnit_ListDep,
781 nsPrintfCString("bad t-delay unit %d",
782 transDelay->GetUnit()).get());
784 const nsCSSValueList* dur = transDuration->GetListValue();
785 const nsCSSValueList* tim = transTiming->GetListValue();
786 const nsCSSValueList* del = transDelay->GetListValue();
788 if (transProp->GetUnit() == eCSSUnit_None ||
789 transProp->GetUnit() == eCSSUnit_All) {
790 // If any of the other three lists has more than one element,
791 // we can't use the shorthand.
792 if (!dur->mNext && !tim->mNext && !del->mNext) {
793 transProp->AppendToString(eCSSProperty_transition_property, aValue,
794 aSerialization);
795 aValue.Append(char16_t(' '));
796 dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue,
797 aSerialization);
798 aValue.Append(char16_t(' '));
799 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
800 aValue, aSerialization);
801 aValue.Append(char16_t(' '));
802 del->mValue.AppendToString(eCSSProperty_transition_delay, aValue,
803 aSerialization);
804 aValue.Append(char16_t(' '));
805 } else {
806 aValue.Truncate();
807 }
808 } else {
809 NS_ABORT_IF_FALSE(transProp->GetUnit() == eCSSUnit_List ||
810 transProp->GetUnit() == eCSSUnit_ListDep,
811 nsPrintfCString("bad t-prop unit %d",
812 transProp->GetUnit()).get());
813 const nsCSSValueList* pro = transProp->GetListValue();
814 for (;;) {
815 pro->mValue.AppendToString(eCSSProperty_transition_property,
816 aValue, aSerialization);
817 aValue.Append(char16_t(' '));
818 dur->mValue.AppendToString(eCSSProperty_transition_duration,
819 aValue, aSerialization);
820 aValue.Append(char16_t(' '));
821 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
822 aValue, aSerialization);
823 aValue.Append(char16_t(' '));
824 del->mValue.AppendToString(eCSSProperty_transition_delay,
825 aValue, aSerialization);
826 pro = pro->mNext;
827 dur = dur->mNext;
828 tim = tim->mNext;
829 del = del->mNext;
830 if (!pro || !dur || !tim || !del) {
831 break;
832 }
833 aValue.AppendLiteral(", ");
834 }
835 if (pro || dur || tim || del) {
836 // Lists not all the same length, can't use shorthand.
837 aValue.Truncate();
838 }
839 }
840 break;
841 }
842 case eCSSProperty_animation: {
843 const nsCSSProperty* subprops =
844 nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
845 static const size_t numProps = 7;
846 NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
847 "unexpected number of subproperties");
848 const nsCSSValue* values[numProps];
849 const nsCSSValueList* lists[numProps];
851 for (uint32_t i = 0; i < numProps; ++i) {
852 values[i] = data->ValueFor(subprops[i]);
853 NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
854 values[i]->GetUnit() == eCSSUnit_ListDep,
855 nsPrintfCString("bad a-duration unit %d",
856 values[i]->GetUnit()).get());
857 lists[i] = values[i]->GetListValue();
858 }
860 for (;;) {
861 // We must serialize 'animation-name' last in case it has
862 // a value that conflicts with one of the other keyword properties.
863 NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
864 eCSSProperty_animation_name,
865 "animation-name must be last");
866 bool done = false;
867 for (uint32_t i = 0;;) {
868 lists[i]->mValue.AppendToString(subprops[i], aValue, aSerialization);
869 lists[i] = lists[i]->mNext;
870 if (!lists[i]) {
871 done = true;
872 }
873 if (++i == numProps) {
874 break;
875 }
876 aValue.Append(char16_t(' '));
877 }
878 if (done) {
879 break;
880 }
881 aValue.AppendLiteral(", ");
882 }
883 for (uint32_t i = 0; i < numProps; ++i) {
884 if (lists[i]) {
885 // Lists not all the same length, can't use shorthand.
886 aValue.Truncate();
887 break;
888 }
889 }
890 break;
891 }
892 case eCSSProperty_marker: {
893 const nsCSSValue &endValue =
894 *data->ValueFor(eCSSProperty_marker_end);
895 const nsCSSValue &midValue =
896 *data->ValueFor(eCSSProperty_marker_mid);
897 const nsCSSValue &startValue =
898 *data->ValueFor(eCSSProperty_marker_start);
899 if (endValue == midValue && midValue == startValue)
900 AppendValueToString(eCSSProperty_marker_end, aValue, aSerialization);
901 break;
902 }
903 case eCSSProperty__moz_columns: {
904 // Two values, column-count and column-width, separated by a space.
905 const nsCSSProperty* subprops =
906 nsCSSProps::SubpropertyEntryFor(aProperty);
907 AppendValueToString(subprops[0], aValue, aSerialization);
908 aValue.Append(char16_t(' '));
909 AppendValueToString(subprops[1], aValue, aSerialization);
910 break;
911 }
912 case eCSSProperty_flex: {
913 // flex-grow, flex-shrink, flex-basis, separated by single space
914 const nsCSSProperty* subprops =
915 nsCSSProps::SubpropertyEntryFor(aProperty);
917 AppendValueToString(subprops[0], aValue, aSerialization);
918 aValue.Append(char16_t(' '));
919 AppendValueToString(subprops[1], aValue, aSerialization);
920 aValue.Append(char16_t(' '));
921 AppendValueToString(subprops[2], aValue, aSerialization);
922 break;
923 }
924 case eCSSProperty_flex_flow: {
925 // flex-direction, flex-wrap, separated by single space
926 const nsCSSProperty* subprops =
927 nsCSSProps::SubpropertyEntryFor(aProperty);
928 NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
929 "must have exactly two subproperties");
931 AppendValueToString(subprops[0], aValue, aSerialization);
932 aValue.Append(char16_t(' '));
933 AppendValueToString(subprops[1], aValue, aSerialization);
934 break;
935 }
936 case eCSSProperty_grid_row:
937 case eCSSProperty_grid_column: {
938 // grid-{row,column}-start, grid-{row,column}-end, separated by a slash
939 const nsCSSProperty* subprops =
940 nsCSSProps::SubpropertyEntryFor(aProperty);
941 NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
942 "must have exactly two subproperties");
944 // TODO: should we simplify when possible?
945 AppendValueToString(subprops[0], aValue, aSerialization);
946 aValue.AppendLiteral(" / ");
947 AppendValueToString(subprops[1], aValue, aSerialization);
948 break;
949 }
950 case eCSSProperty_grid_area: {
951 const nsCSSProperty* subprops =
952 nsCSSProps::SubpropertyEntryFor(aProperty);
953 NS_ABORT_IF_FALSE(subprops[4] == eCSSProperty_UNKNOWN,
954 "must have exactly four subproperties");
956 // TODO: should we simplify when possible?
957 AppendValueToString(subprops[0], aValue, aSerialization);
958 aValue.AppendLiteral(" / ");
959 AppendValueToString(subprops[1], aValue, aSerialization);
960 aValue.AppendLiteral(" / ");
961 AppendValueToString(subprops[2], aValue, aSerialization);
962 aValue.AppendLiteral(" / ");
963 AppendValueToString(subprops[3], aValue, aSerialization);
964 break;
965 }
967 // This can express either grid-template-{areas,columns,rows}
968 // or grid-auto-{flow,columns,rows}, but not both.
969 case eCSSProperty_grid: {
970 const nsCSSValue& areasValue =
971 *data->ValueFor(eCSSProperty_grid_template_areas);
972 const nsCSSValue& columnsValue =
973 *data->ValueFor(eCSSProperty_grid_template_columns);
974 const nsCSSValue& rowsValue =
975 *data->ValueFor(eCSSProperty_grid_template_rows);
977 const nsCSSValue& autoFlowValue =
978 *data->ValueFor(eCSSProperty_grid_auto_flow);
979 const nsCSSValue& autoColumnsValue =
980 *data->ValueFor(eCSSProperty_grid_auto_columns);
981 const nsCSSValue& autoRowsValue =
982 *data->ValueFor(eCSSProperty_grid_auto_rows);
984 if (areasValue.GetUnit() == eCSSUnit_None &&
985 columnsValue.GetUnit() == eCSSUnit_None &&
986 rowsValue.GetUnit() == eCSSUnit_None) {
987 AppendValueToString(eCSSProperty_grid_auto_flow,
988 aValue, aSerialization);
989 aValue.Append(char16_t(' '));
990 AppendValueToString(eCSSProperty_grid_auto_columns,
991 aValue, aSerialization);
992 aValue.AppendLiteral(" / ");
993 AppendValueToString(eCSSProperty_grid_auto_rows,
994 aValue, aSerialization);
995 break;
996 } else if (!(autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
997 autoFlowValue.GetIntValue() == NS_STYLE_GRID_AUTO_FLOW_NONE &&
998 autoColumnsValue.GetUnit() == eCSSUnit_Auto &&
999 autoRowsValue.GetUnit() == eCSSUnit_Auto)) {
1000 // Not serializable, bail.
1001 return;
1002 }
1003 // Fall through to eCSSProperty_grid_template
1004 }
1005 case eCSSProperty_grid_template: {
1006 const nsCSSValue& areasValue =
1007 *data->ValueFor(eCSSProperty_grid_template_areas);
1008 const nsCSSValue& columnsValue =
1009 *data->ValueFor(eCSSProperty_grid_template_columns);
1010 const nsCSSValue& rowsValue =
1011 *data->ValueFor(eCSSProperty_grid_template_rows);
1012 if (areasValue.GetUnit() == eCSSUnit_None) {
1013 AppendValueToString(eCSSProperty_grid_template_columns,
1014 aValue, aSerialization);
1015 aValue.AppendLiteral(" / ");
1016 AppendValueToString(eCSSProperty_grid_template_rows,
1017 aValue, aSerialization);
1018 break;
1019 }
1020 if (columnsValue.GetUnit() == eCSSUnit_List ||
1021 columnsValue.GetUnit() == eCSSUnit_ListDep) {
1022 const nsCSSValueList* columnsItem = columnsValue.GetListValue();
1023 if (columnsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
1024 columnsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
1025 // We have "grid-template-areas:[something]; grid-template-columns:subgrid"
1026 // which isn't a value that the shorthand can express. Bail.
1027 return;
1028 }
1029 }
1030 if (rowsValue.GetUnit() != eCSSUnit_List &&
1031 rowsValue.GetUnit() != eCSSUnit_ListDep) {
1032 // We have "grid-template-areas:[something]; grid-template-rows:none"
1033 // which isn't a value that the shorthand can express. Bail.
1034 return;
1035 }
1036 const nsCSSValueList* rowsItem = rowsValue.GetListValue();
1037 if (rowsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
1038 rowsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
1039 // We have "grid-template-areas:[something]; grid-template-rows:subgrid"
1040 // which isn't a value that the shorthand can express. Bail.
1041 return;
1042 }
1043 const GridTemplateAreasValue* areas = areasValue.GetGridTemplateAreas();
1044 uint32_t nRowItems = 0;
1045 while (rowsItem) {
1046 nRowItems++;
1047 rowsItem = rowsItem->mNext;
1048 }
1049 MOZ_ASSERT(nRowItems % 2 == 1, "expected an odd number of items");
1050 if ((nRowItems - 1) / 2 != areas->NRows()) {
1051 // Not serializable, bail.
1052 return;
1053 }
1054 if (columnsValue.GetUnit() != eCSSUnit_None) {
1055 AppendValueToString(eCSSProperty_grid_template_columns,
1056 aValue, aSerialization);
1057 aValue.AppendLiteral(" / ");
1058 }
1059 rowsItem = rowsValue.GetListValue();
1060 uint32_t row = 0;
1061 for (;;) {
1062 bool addSpaceSeparator = true;
1063 nsCSSUnit unit = rowsItem->mValue.GetUnit();
1065 if (unit == eCSSUnit_Null) {
1066 // Empty or omitted <line-names>. Serializes to nothing.
1067 addSpaceSeparator = false; // Avoid a double space.
1069 } else if (unit == eCSSUnit_List || unit == eCSSUnit_ListDep) {
1070 // Non-empty <line-names>
1071 aValue.AppendLiteral("(");
1072 rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
1073 aValue, aSerialization);
1074 aValue.AppendLiteral(")");
1076 } else {
1077 nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[row++], aValue);
1078 aValue.Append(char16_t(' '));
1080 // <track-size>
1081 rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
1082 aValue, aSerialization);
1083 if (rowsItem->mNext &&
1084 rowsItem->mNext->mValue.GetUnit() == eCSSUnit_Null &&
1085 !rowsItem->mNext->mNext) {
1086 // Break out of the loop early to avoid a trailing space.
1087 break;
1088 }
1089 }
1091 rowsItem = rowsItem->mNext;
1092 if (!rowsItem) {
1093 break;
1094 }
1096 if (addSpaceSeparator) {
1097 aValue.Append(char16_t(' '));
1098 }
1099 }
1100 break;
1101 }
1102 case eCSSProperty__moz_transform: {
1103 // shorthands that are just aliases with different parsing rules
1104 const nsCSSProperty* subprops =
1105 nsCSSProps::SubpropertyEntryFor(aProperty);
1106 NS_ABORT_IF_FALSE(subprops[1] == eCSSProperty_UNKNOWN,
1107 "must have exactly one subproperty");
1108 AppendValueToString(subprops[0], aValue, aSerialization);
1109 break;
1110 }
1111 case eCSSProperty_all:
1112 // If we got here, then we didn't have all "inherit" or "initial" or
1113 // "unset" values for all of the longhand property components of 'all'.
1114 // There is no other possible value that is valid for all properties,
1115 // so serialize as the empty string.
1116 break;
1117 default:
1118 NS_ABORT_IF_FALSE(false, "no other shorthands");
1119 break;
1120 }
1121 }
1123 bool
1124 Declaration::GetValueIsImportant(const nsAString& aProperty) const
1125 {
1126 nsCSSProperty propID =
1127 nsCSSProps::LookupProperty(aProperty, nsCSSProps::eIgnoreEnabledState);
1128 if (propID == eCSSProperty_UNKNOWN) {
1129 return false;
1130 }
1131 if (propID == eCSSPropertyExtra_variable) {
1132 const nsSubstring& variableName =
1133 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);
1134 return GetVariableValueIsImportant(variableName);
1135 }
1136 return GetValueIsImportant(propID);
1137 }
1139 bool
1140 Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
1141 {
1142 if (!mImportantData)
1143 return false;
1145 // Calling ValueFor is inefficient, but we can assume '!important' is rare.
1147 if (!nsCSSProps::IsShorthand(aProperty)) {
1148 return mImportantData->ValueFor(aProperty) != nullptr;
1149 }
1151 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
1152 if (*p == eCSSProperty__x_system_font) {
1153 // The system_font subproperty doesn't count.
1154 continue;
1155 }
1156 if (!mImportantData->ValueFor(*p)) {
1157 return false;
1158 }
1159 }
1160 return true;
1161 }
1163 void
1164 Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty,
1165 nsAutoString& aValue,
1166 nsAString& aResult) const
1167 {
1168 NS_ABORT_IF_FALSE(0 <= aProperty && aProperty < eCSSProperty_COUNT,
1169 "property enum out of range");
1170 NS_ABORT_IF_FALSE((aProperty < eCSSProperty_COUNT_no_shorthands) ==
1171 aValue.IsEmpty(),
1172 "aValue should be given for shorthands but not longhands");
1173 AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
1174 aResult.AppendLiteral(": ");
1175 if (aValue.IsEmpty())
1176 AppendValueToString(aProperty, aResult, nsCSSValue::eNormalized);
1177 else
1178 aResult.Append(aValue);
1179 if (GetValueIsImportant(aProperty)) {
1180 aResult.AppendLiteral(" ! important");
1181 }
1182 aResult.AppendLiteral("; ");
1183 }
1185 void
1186 Declaration::AppendVariableAndValueToString(const nsAString& aName,
1187 nsAString& aResult) const
1188 {
1189 aResult.AppendLiteral("--");
1190 aResult.Append(aName);
1191 CSSVariableDeclarations::Type type;
1192 nsString value;
1193 bool important;
1195 if (mImportantVariables && mImportantVariables->Get(aName, type, value)) {
1196 important = true;
1197 } else {
1198 MOZ_ASSERT(mVariables);
1199 MOZ_ASSERT(mVariables->Has(aName));
1200 mVariables->Get(aName, type, value);
1201 important = false;
1202 }
1204 switch (type) {
1205 case CSSVariableDeclarations::eTokenStream:
1206 if (value.IsEmpty()) {
1207 aResult.Append(':');
1208 } else {
1209 aResult.AppendLiteral(": ");
1210 aResult.Append(value);
1211 }
1212 break;
1214 case CSSVariableDeclarations::eInitial:
1215 aResult.AppendLiteral("initial");
1216 break;
1218 case CSSVariableDeclarations::eInherit:
1219 aResult.AppendLiteral("inherit");
1220 break;
1222 case CSSVariableDeclarations::eUnset:
1223 aResult.AppendLiteral("unset");
1224 break;
1226 default:
1227 MOZ_ASSERT(false, "unexpected variable value type");
1228 }
1230 if (important) {
1231 aResult.AppendLiteral("! important");
1232 }
1233 aResult.AppendLiteral("; ");
1234 }
1236 void
1237 Declaration::ToString(nsAString& aString) const
1238 {
1239 // Someone cares about this declaration's contents, so don't let it
1240 // change from under them. See e.g. bug 338679.
1241 SetImmutable();
1243 nsCSSCompressedDataBlock *systemFontData =
1244 GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData;
1245 const nsCSSValue *systemFont =
1246 systemFontData->ValueFor(eCSSProperty__x_system_font);
1247 const bool haveSystemFont = systemFont &&
1248 systemFont->GetUnit() != eCSSUnit_None &&
1249 systemFont->GetUnit() != eCSSUnit_Null;
1250 bool didSystemFont = false;
1252 int32_t count = mOrder.Length();
1253 int32_t index;
1254 nsAutoTArray<nsCSSProperty, 16> shorthandsUsed;
1255 for (index = 0; index < count; index++) {
1256 nsCSSProperty property = GetPropertyAt(index);
1258 if (property == eCSSPropertyExtra_variable) {
1259 uint32_t variableIndex = mOrder[index] - eCSSProperty_COUNT;
1260 AppendVariableAndValueToString(mVariableOrder[variableIndex], aString);
1261 continue;
1262 }
1264 if (!nsCSSProps::IsEnabled(property)) {
1265 continue;
1266 }
1267 bool doneProperty = false;
1269 // If we already used this property in a shorthand, skip it.
1270 if (shorthandsUsed.Length() > 0) {
1271 for (const nsCSSProperty *shorthands =
1272 nsCSSProps::ShorthandsContaining(property);
1273 *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
1274 if (shorthandsUsed.Contains(*shorthands)) {
1275 doneProperty = true;
1276 break;
1277 }
1278 }
1279 if (doneProperty)
1280 continue;
1281 }
1283 // Try to use this property in a shorthand.
1284 nsAutoString value;
1285 for (const nsCSSProperty *shorthands =
1286 nsCSSProps::ShorthandsContaining(property);
1287 *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
1288 // ShorthandsContaining returns the shorthands in order from those
1289 // that contain the most subproperties to those that contain the
1290 // least, which is exactly the order we want to test them.
1291 nsCSSProperty shorthand = *shorthands;
1293 // If GetValue gives us a non-empty string back, we can use that
1294 // value; otherwise it's not possible to use this shorthand.
1295 GetValue(shorthand, value);
1296 if (!value.IsEmpty()) {
1297 AppendPropertyAndValueToString(shorthand, value, aString);
1298 shorthandsUsed.AppendElement(shorthand);
1299 doneProperty = true;
1300 break;
1301 }
1303 NS_ABORT_IF_FALSE(shorthand != eCSSProperty_font ||
1304 *(shorthands + 1) == eCSSProperty_UNKNOWN,
1305 "font should always be the only containing shorthand");
1306 if (shorthand == eCSSProperty_font) {
1307 if (haveSystemFont && !didSystemFont) {
1308 // Output the shorthand font declaration that we will
1309 // partially override later. But don't add it to
1310 // |shorthandsUsed|, since we will have to override it.
1311 systemFont->AppendToString(eCSSProperty__x_system_font, value,
1312 nsCSSValue::eNormalized);
1313 AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
1314 value.Truncate();
1315 didSystemFont = true;
1316 }
1318 // That we output the system font is enough for this property if:
1319 // (1) it's the hidden system font subproperty (which either
1320 // means we output it or we don't have it), or
1321 // (2) its value is the hidden system font value and it matches
1322 // the hidden system font subproperty in importance, and
1323 // we output the system font subproperty.
1324 const nsCSSValue *val = systemFontData->ValueFor(property);
1325 if (property == eCSSProperty__x_system_font ||
1326 (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
1327 doneProperty = true;
1328 }
1329 }
1330 }
1331 if (doneProperty)
1332 continue;
1334 NS_ABORT_IF_FALSE(value.IsEmpty(), "value should be empty now");
1335 AppendPropertyAndValueToString(property, value, aString);
1336 }
1337 if (! aString.IsEmpty()) {
1338 // if the string is not empty, we have trailing whitespace we
1339 // should remove
1340 aString.Truncate(aString.Length() - 1);
1341 }
1342 }
1344 #ifdef DEBUG
1345 void
1346 Declaration::List(FILE* out, int32_t aIndent) const
1347 {
1348 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
1350 fputs("{ ", out);
1351 nsAutoString s;
1352 ToString(s);
1353 fputs(NS_ConvertUTF16toUTF8(s).get(), out);
1354 fputs("}", out);
1355 }
1356 #endif
1358 bool
1359 Declaration::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
1360 {
1361 aReturn.Truncate();
1362 if (aIndex < mOrder.Length()) {
1363 nsCSSProperty property = GetPropertyAt(aIndex);
1364 if (property == eCSSPropertyExtra_variable) {
1365 GetCustomPropertyNameAt(aIndex, aReturn);
1366 return true;
1367 }
1368 if (0 <= property) {
1369 AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
1370 return true;
1371 }
1372 }
1373 return false;
1374 }
1376 void
1377 Declaration::InitializeEmpty()
1378 {
1379 NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
1380 mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
1381 }
1383 Declaration*
1384 Declaration::EnsureMutable()
1385 {
1386 NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
1387 if (!IsMutable()) {
1388 return new Declaration(*this);
1389 } else {
1390 return this;
1391 }
1392 }
1394 size_t
1395 Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
1396 {
1397 size_t n = aMallocSizeOf(this);
1398 n += mOrder.SizeOfExcludingThis(aMallocSizeOf);
1399 n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0;
1400 n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
1401 if (mVariables) {
1402 n += mVariables->SizeOfIncludingThis(aMallocSizeOf);
1403 }
1404 if (mImportantVariables) {
1405 n += mImportantVariables->SizeOfIncludingThis(aMallocSizeOf);
1406 }
1407 return n;
1408 }
1410 bool
1411 Declaration::HasVariableDeclaration(const nsAString& aName) const
1412 {
1413 return (mVariables && mVariables->Has(aName)) ||
1414 (mImportantVariables && mImportantVariables->Has(aName));
1415 }
1417 void
1418 Declaration::GetVariableDeclaration(const nsAString& aName,
1419 nsAString& aValue) const
1420 {
1421 aValue.Truncate();
1423 CSSVariableDeclarations::Type type;
1424 nsString value;
1426 if ((mImportantVariables && mImportantVariables->Get(aName, type, value)) ||
1427 (mVariables && mVariables->Get(aName, type, value))) {
1428 switch (type) {
1429 case CSSVariableDeclarations::eTokenStream:
1430 aValue.Append(value);
1431 break;
1433 case CSSVariableDeclarations::eInitial:
1434 aValue.AppendLiteral("initial");
1435 break;
1437 case CSSVariableDeclarations::eInherit:
1438 aValue.AppendLiteral("inherit");
1439 break;
1441 case CSSVariableDeclarations::eUnset:
1442 aValue.AppendLiteral("unset");
1443 break;
1445 default:
1446 MOZ_ASSERT(false, "unexpected variable value type");
1447 }
1448 }
1449 }
1451 void
1452 Declaration::AddVariableDeclaration(const nsAString& aName,
1453 CSSVariableDeclarations::Type aType,
1454 const nsString& aValue,
1455 bool aIsImportant,
1456 bool aOverrideImportant)
1457 {
1458 MOZ_ASSERT(IsMutable());
1460 nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
1461 if (index == nsTArray<nsString>::NoIndex) {
1462 index = mVariableOrder.Length();
1463 mVariableOrder.AppendElement(aName);
1464 }
1466 if (!aIsImportant && !aOverrideImportant &&
1467 mImportantVariables && mImportantVariables->Has(aName)) {
1468 return;
1469 }
1471 CSSVariableDeclarations* variables;
1472 if (aIsImportant) {
1473 if (mVariables) {
1474 mVariables->Remove(aName);
1475 }
1476 if (!mImportantVariables) {
1477 mImportantVariables = new CSSVariableDeclarations;
1478 }
1479 variables = mImportantVariables;
1480 } else {
1481 if (mImportantVariables) {
1482 mImportantVariables->Remove(aName);
1483 }
1484 if (!mVariables) {
1485 mVariables = new CSSVariableDeclarations;
1486 }
1487 variables = mVariables;
1488 }
1490 switch (aType) {
1491 case CSSVariableDeclarations::eTokenStream:
1492 variables->PutTokenStream(aName, aValue);
1493 break;
1495 case CSSVariableDeclarations::eInitial:
1496 MOZ_ASSERT(aValue.IsEmpty());
1497 variables->PutInitial(aName);
1498 break;
1500 case CSSVariableDeclarations::eInherit:
1501 MOZ_ASSERT(aValue.IsEmpty());
1502 variables->PutInherit(aName);
1503 break;
1505 case CSSVariableDeclarations::eUnset:
1506 MOZ_ASSERT(aValue.IsEmpty());
1507 variables->PutUnset(aName);
1508 break;
1510 default:
1511 MOZ_ASSERT(false, "unexpected aType value");
1512 }
1514 uint32_t propertyIndex = index + eCSSProperty_COUNT;
1515 mOrder.RemoveElement(propertyIndex);
1516 mOrder.AppendElement(propertyIndex);
1517 }
1519 void
1520 Declaration::RemoveVariableDeclaration(const nsAString& aName)
1521 {
1522 if (mVariables) {
1523 mVariables->Remove(aName);
1524 }
1525 if (mImportantVariables) {
1526 mImportantVariables->Remove(aName);
1527 }
1528 nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
1529 if (index != nsTArray<nsString>::NoIndex) {
1530 mOrder.RemoveElement(index + eCSSProperty_COUNT);
1531 }
1532 }
1534 bool
1535 Declaration::GetVariableValueIsImportant(const nsAString& aName) const
1536 {
1537 return mImportantVariables && mImportantVariables->Has(aName);
1538 }
1540 } // namespace mozilla::css
1541 } // namespace mozilla