Line data Source code
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/. */
5 :
6 : /*
7 : * representation of a declaration block (or style attribute) in a CSS
8 : * stylesheet
9 : */
10 :
11 : #include "mozilla/css/Declaration.h"
12 :
13 : #include "mozilla/ArrayUtils.h"
14 : #include "mozilla/MemoryReporting.h"
15 : #include "mozilla/ServoStyleSet.h"
16 :
17 : #include "mozilla/css/Rule.h"
18 : #include "nsPrintfCString.h"
19 : #include "gfxFontConstants.h"
20 : #include "nsStyleUtil.h"
21 :
22 : namespace mozilla {
23 : namespace css {
24 :
25 1210 : NS_IMPL_QUERY_INTERFACE(ImportantStyleData, nsIStyleRule)
26 310 : NS_IMPL_ADDREF_USING_AGGREGATOR(ImportantStyleData, Declaration())
27 217 : NS_IMPL_RELEASE_USING_AGGREGATOR(ImportantStyleData, Declaration())
28 :
29 : /* virtual */ void
30 2460 : ImportantStyleData::MapRuleInfoInto(nsRuleData* aRuleData)
31 : {
32 2460 : Declaration()->MapImportantRuleInfoInto(aRuleData);
33 2460 : }
34 :
35 : /* virtual */ bool
36 35 : ImportantStyleData::MightMapInheritedStyleData()
37 : {
38 35 : return Declaration()->MapsImportantInheritedStyleData();
39 : }
40 :
41 : /* virtual */ bool
42 0 : ImportantStyleData::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
43 : nsCSSValue* aValue)
44 : {
45 0 : return Declaration()->GetDiscretelyAnimatedCSSValue(aProperty, aValue);
46 : }
47 :
48 :
49 : #ifdef DEBUG
50 : /* virtual */ void
51 0 : ImportantStyleData::List(FILE* out, int32_t aIndent) const
52 : {
53 : // Indent
54 0 : nsAutoCString str;
55 0 : for (int32_t index = aIndent; --index >= 0; ) {
56 0 : str.AppendLiteral(" ");
57 : }
58 :
59 0 : str.AppendLiteral("! important rule\n");
60 0 : fprintf_stderr(out, "%s", str.get());
61 0 : }
62 : #endif
63 :
64 15 : Declaration::Declaration(const Declaration& aCopy)
65 : : DeclarationBlock(aCopy),
66 : mOrder(aCopy.mOrder),
67 : mVariableOrder(aCopy.mVariableOrder),
68 30 : mData(aCopy.mData ? aCopy.mData->Clone() : nullptr),
69 : mImportantData(aCopy.mImportantData ?
70 15 : aCopy.mImportantData->Clone() : nullptr),
71 : mVariables(aCopy.mVariables ?
72 0 : new CSSVariableDeclarations(*aCopy.mVariables) :
73 15 : nullptr),
74 : mImportantVariables(aCopy.mImportantVariables ?
75 0 : new CSSVariableDeclarations(*aCopy.mImportantVariables) :
76 75 : nullptr)
77 : {
78 15 : }
79 :
80 25 : Declaration::~Declaration()
81 : {
82 25 : }
83 :
84 23588 : NS_INTERFACE_MAP_BEGIN(Declaration)
85 23588 : if (aIID.Equals(NS_GET_IID(mozilla::css::Declaration))) {
86 21808 : *aInstancePtr = this;
87 21808 : NS_ADDREF_THIS();
88 21808 : return NS_OK;
89 : }
90 : else
91 1780 : NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
92 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
93 0 : NS_INTERFACE_MAP_END
94 :
95 31741 : NS_IMPL_ADDREF(Declaration)
96 27681 : NS_IMPL_RELEASE(Declaration)
97 :
98 : /* virtual */ void
99 29444 : Declaration::MapRuleInfoInto(nsRuleData* aRuleData)
100 : {
101 29444 : MOZ_ASSERT(mData, "must call only while compressed");
102 29444 : mData->MapRuleInfoInto(aRuleData);
103 29444 : if (mVariables) {
104 735 : mVariables->MapRuleInfoInto(aRuleData);
105 : }
106 29444 : }
107 :
108 : /* virtual */ bool
109 282 : Declaration::MightMapInheritedStyleData()
110 : {
111 282 : MOZ_ASSERT(mData, "must call only while compressed");
112 282 : if (mVariables && mVariables->Count() != 0) {
113 1 : return true;
114 : }
115 281 : return mData->HasInheritedStyleData();
116 : }
117 :
118 : /* virtual */ bool
119 0 : Declaration::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
120 : nsCSSValue* aValue)
121 : {
122 0 : nsCSSCompressedDataBlock* data = GetPropertyIsImportantByID(aProperty)
123 0 : ? mImportantData : mData;
124 0 : const nsCSSValue* value = data->ValueFor(aProperty);
125 0 : if (!value) {
126 0 : return false;
127 : }
128 0 : *aValue = *value;
129 0 : return true;
130 : }
131 :
132 :
133 : bool
134 35 : Declaration::MapsImportantInheritedStyleData() const
135 : {
136 35 : MOZ_ASSERT(mData, "must call only while compressed");
137 35 : MOZ_ASSERT(HasImportantData(), "must only be called for Declarations with "
138 : "important data");
139 35 : if (mImportantVariables && mImportantVariables->Count() != 0) {
140 0 : return true;
141 : }
142 35 : return mImportantData ? mImportantData->HasInheritedStyleData() : false;
143 : }
144 :
145 : void
146 13008 : Declaration::ValueAppended(nsCSSPropertyID aProperty)
147 : {
148 13008 : MOZ_ASSERT(!mData && !mImportantData,
149 : "should only be called while expanded");
150 13008 : MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
151 : "shorthands forbidden");
152 : // order IS important for CSS, so remove and add to the end
153 13008 : mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
154 13008 : mOrder.AppendElement(static_cast<uint32_t>(aProperty));
155 13008 : }
156 :
157 : template<typename PropFunc, typename CustomPropFunc>
158 : inline void
159 0 : DispatchPropertyOperation(const nsAString& aProperty,
160 : PropFunc aPropFunc, CustomPropFunc aCustomPropFunc)
161 : {
162 : nsCSSPropertyID propID =
163 0 : nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent);
164 0 : if (propID != eCSSProperty_UNKNOWN) {
165 0 : if (propID != eCSSPropertyExtra_variable) {
166 0 : aPropFunc(propID);
167 : } else {
168 0 : aCustomPropFunc(Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH));
169 : }
170 : }
171 0 : }
172 :
173 : void
174 0 : Declaration::GetPropertyValue(const nsAString& aProperty,
175 : nsAString& aValue) const
176 : {
177 : DispatchPropertyOperation(aProperty,
178 0 : [&](nsCSSPropertyID propID) { GetPropertyValueByID(propID, aValue); },
179 0 : [&](const nsAString& name) { GetVariableValue(name, aValue); });
180 0 : }
181 :
182 : void
183 0 : Declaration::GetPropertyValueByID(nsCSSPropertyID aPropID,
184 : nsAString& aValue) const
185 : {
186 0 : GetPropertyValueInternal(aPropID, aValue, nsCSSValue::eNormalized);
187 0 : }
188 :
189 : void
190 0 : Declaration::GetAuthoredPropertyValue(const nsAString& aProperty,
191 : nsAString& aValue) const
192 : {
193 : DispatchPropertyOperation(aProperty,
194 0 : [&](nsCSSPropertyID propID) {
195 0 : GetPropertyValueInternal(propID, aValue, nsCSSValue::eAuthorSpecified);
196 0 : },
197 0 : [&](const nsAString& name) { GetVariableValue(name, aValue); });
198 0 : }
199 :
200 : bool
201 0 : Declaration::GetPropertyIsImportant(const nsAString& aProperty) const
202 : {
203 0 : bool r = false;
204 : DispatchPropertyOperation(aProperty,
205 0 : [&](nsCSSPropertyID propID) { r = GetPropertyIsImportantByID(propID); },
206 0 : [&](const nsAString& name) { r = GetVariableIsImportant(name); });
207 0 : return r;
208 : }
209 :
210 : void
211 0 : Declaration::RemoveProperty(const nsAString& aProperty)
212 : {
213 : DispatchPropertyOperation(aProperty,
214 0 : [&](nsCSSPropertyID propID) { RemovePropertyByID(propID); },
215 0 : [&](const nsAString& name) { RemoveVariable(name); });
216 0 : }
217 :
218 : void
219 1 : Declaration::RemovePropertyByID(nsCSSPropertyID aProperty)
220 : {
221 1 : MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT);
222 :
223 2 : nsCSSExpandedDataBlock data;
224 1 : ExpandTo(&data);
225 1 : MOZ_ASSERT(!mData && !mImportantData, "Expand didn't null things out");
226 :
227 1 : if (nsCSSProps::IsShorthand(aProperty)) {
228 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty,
229 : CSSEnabledState::eForAllContent) {
230 0 : data.ClearLonghandProperty(*p);
231 0 : mOrder.RemoveElement(static_cast<uint32_t>(*p));
232 : }
233 : } else {
234 1 : data.ClearLonghandProperty(aProperty);
235 1 : mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
236 : }
237 :
238 1 : CompressFrom(&data);
239 1 : }
240 :
241 : bool
242 0 : Declaration::HasProperty(nsCSSPropertyID aProperty) const
243 : {
244 0 : MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands,
245 : "property ID out of range");
246 :
247 0 : nsCSSCompressedDataBlock *data = GetPropertyIsImportantByID(aProperty)
248 0 : ? mImportantData : mData;
249 0 : const nsCSSValue *val = data->ValueFor(aProperty);
250 0 : return !!val;
251 : }
252 :
253 : bool
254 0 : Declaration::AppendValueToString(nsCSSPropertyID aProperty,
255 : nsAString& aResult,
256 : nsCSSValue::Serialization aSerialization,
257 : bool* aIsTokenStream) const
258 : {
259 0 : MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands,
260 : "property ID out of range");
261 :
262 0 : nsCSSCompressedDataBlock *data = GetPropertyIsImportantByID(aProperty)
263 0 : ? mImportantData : mData;
264 0 : const nsCSSValue *val = data->ValueFor(aProperty);
265 0 : if (!val) {
266 0 : return false;
267 : }
268 :
269 0 : if (aIsTokenStream) {
270 0 : *aIsTokenStream = val->GetUnit() == eCSSUnit_TokenStream;
271 : }
272 0 : val->AppendToString(aProperty, aResult, aSerialization);
273 0 : return true;
274 : }
275 :
276 : static void
277 0 : AppendSingleImageLayerPositionValue(const nsCSSValue& aPositionX,
278 : const nsCSSValue& aPositionY,
279 : const nsCSSPropertyID aTable[],
280 : nsAString& aValue,
281 : nsCSSValue::Serialization aSerialization)
282 : {
283 : // We need to make sure that we don't serialize to an invalid 3-value form.
284 : // The 3-value form is only valid if both edges are present.
285 0 : const nsCSSValue &xEdge = aPositionX.GetArrayValue()->Item(0);
286 0 : const nsCSSValue &xOffset = aPositionX.GetArrayValue()->Item(1);
287 0 : const nsCSSValue &yEdge = aPositionY.GetArrayValue()->Item(0);
288 0 : const nsCSSValue &yOffset = aPositionY.GetArrayValue()->Item(1);
289 0 : bool xHasEdge = (eCSSUnit_Enumerated == xEdge.GetUnit());
290 0 : bool xHasBoth = xHasEdge && (eCSSUnit_Null != xOffset.GetUnit());
291 0 : bool yHasEdge = (eCSSUnit_Enumerated == yEdge.GetUnit());
292 0 : bool yHasBoth = yHasEdge && (eCSSUnit_Null != yOffset.GetUnit());
293 :
294 0 : if (yHasBoth && !xHasEdge) {
295 : // Output 4-value form by adding the x edge.
296 0 : aValue.AppendLiteral("left ");
297 : }
298 0 : aPositionX.AppendToString(aTable[nsStyleImageLayers::positionX],
299 0 : aValue, aSerialization);
300 :
301 0 : aValue.Append(char16_t(' '));
302 :
303 0 : if (xHasBoth && !yHasEdge) {
304 : // Output 4-value form by adding the y edge.
305 0 : aValue.AppendLiteral("top ");
306 : }
307 0 : aPositionY.AppendToString(aTable[nsStyleImageLayers::positionY],
308 0 : aValue, aSerialization);
309 0 : }
310 :
311 : void
312 0 : Declaration::GetImageLayerValue(
313 : nsCSSCompressedDataBlock *data,
314 : nsAString& aValue,
315 : nsCSSValue::Serialization aSerialization,
316 : const nsCSSPropertyID aTable[]) const
317 : {
318 : // We know from our caller that all subproperties were specified.
319 : // However, we still can't represent that in the shorthand unless
320 : // they're all lists of the same length. So if they're different
321 : // lengths, we need to bail out.
322 : // We also need to bail out if an item has background-clip and
323 : // background-origin that are different and not the default
324 : // values. (We omit them if they're both default.)
325 :
326 : // Common CSS properties for both background & mask layer.
327 : const nsCSSValueList *image =
328 0 : data->ValueFor(aTable[nsStyleImageLayers::image])->GetListValue();
329 : const nsCSSValuePairList *repeat =
330 0 : data->ValueFor(aTable[nsStyleImageLayers::repeat])->GetPairListValue();
331 : const nsCSSValueList *positionX =
332 0 : data->ValueFor(aTable[nsStyleImageLayers::positionX])->GetListValue();
333 : const nsCSSValueList *positionY =
334 0 : data->ValueFor(aTable[nsStyleImageLayers::positionY])->GetListValue();
335 : const nsCSSValueList *clip =
336 0 : data->ValueFor(aTable[nsStyleImageLayers::clip])->GetListValue();
337 : const nsCSSValueList *origin =
338 0 : data->ValueFor(aTable[nsStyleImageLayers::origin])->GetListValue();
339 : const nsCSSValuePairList *size =
340 0 : data->ValueFor(aTable[nsStyleImageLayers::size])->GetPairListValue();
341 :
342 : // Background layer property.
343 : const nsCSSValueList *attachment =
344 0 : (aTable[nsStyleImageLayers::attachment] == eCSSProperty_UNKNOWN)?
345 : nullptr :
346 0 : data->ValueFor(aTable[nsStyleImageLayers::attachment])->GetListValue();
347 :
348 : // Mask layer properties.
349 : const nsCSSValueList *composite =
350 0 : (aTable[nsStyleImageLayers::composite] == eCSSProperty_UNKNOWN)?
351 : nullptr :
352 0 : data->ValueFor(aTable[nsStyleImageLayers::composite])->GetListValue();
353 : const nsCSSValueList *mode =
354 0 : (aTable[nsStyleImageLayers::maskMode] == eCSSProperty_UNKNOWN)?
355 : nullptr :
356 0 : data->ValueFor(aTable[nsStyleImageLayers::maskMode])->GetListValue();
357 :
358 : for (;;) {
359 : // Serialize background-color at the beginning of the last item.
360 0 : if (!image->mNext) {
361 0 : if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
362 0 : AppendValueToString(aTable[nsStyleImageLayers::color], aValue,
363 0 : aSerialization);
364 0 : aValue.Append(char16_t(' '));
365 : }
366 : }
367 :
368 0 : image->mValue.AppendToString(aTable[nsStyleImageLayers::image], aValue,
369 0 : aSerialization);
370 :
371 0 : aValue.Append(char16_t(' '));
372 0 : repeat->mXValue.AppendToString(aTable[nsStyleImageLayers::repeat], aValue,
373 0 : aSerialization);
374 0 : if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
375 0 : repeat->mYValue.AppendToString(aTable[nsStyleImageLayers::repeat], aValue,
376 0 : aSerialization);
377 : }
378 :
379 0 : if (attachment) {
380 0 : aValue.Append(char16_t(' '));
381 0 : attachment->mValue.AppendToString(aTable[nsStyleImageLayers::attachment],
382 0 : aValue, aSerialization);
383 : }
384 :
385 0 : aValue.Append(char16_t(' '));
386 0 : AppendSingleImageLayerPositionValue(positionX->mValue, positionY->mValue,
387 0 : aTable, aValue, aSerialization);
388 :
389 0 : if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
390 0 : size->mYValue.GetUnit() != eCSSUnit_Auto) {
391 0 : aValue.Append(char16_t(' '));
392 0 : aValue.Append(char16_t('/'));
393 0 : aValue.Append(char16_t(' '));
394 0 : size->mXValue.AppendToString(aTable[nsStyleImageLayers::size], aValue,
395 0 : aSerialization);
396 0 : aValue.Append(char16_t(' '));
397 0 : size->mYValue.AppendToString(aTable[nsStyleImageLayers::size], aValue,
398 0 : aSerialization);
399 : }
400 :
401 0 : MOZ_ASSERT(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
402 : origin->mValue.GetUnit() == eCSSUnit_Enumerated,
403 : "should not have inherit/initial within list");
404 :
405 : StyleGeometryBox originDefaultValue =
406 : (aTable == nsStyleImageLayers::kBackgroundLayerTable)
407 0 : ? StyleGeometryBox::PaddingBox : StyleGeometryBox::BorderBox;
408 0 : if (static_cast<StyleGeometryBox>(clip->mValue.GetIntValue()) !=
409 0 : StyleGeometryBox::BorderBox ||
410 0 : static_cast<StyleGeometryBox>(origin->mValue.GetIntValue()) !=
411 : originDefaultValue) {
412 : #ifdef DEBUG
413 0 : const nsCSSProps::KTableEntry* originTable = nsCSSProps::kKeywordTableTable[aTable[nsStyleImageLayers::origin]];
414 0 : const nsCSSProps::KTableEntry* clipTable = nsCSSProps::kKeywordTableTable[aTable[nsStyleImageLayers::clip]];
415 0 : for (size_t i = 0; originTable[i].mValue != -1; i++) {
416 : // For each keyword & value in kOriginKTable, ensure that
417 : // kBackgroundKTable has a matching entry at the same position.
418 0 : MOZ_ASSERT(originTable[i].mKeyword == clipTable[i].mKeyword);
419 0 : MOZ_ASSERT(originTable[i].mValue == clipTable[i].mValue);
420 : }
421 : #endif
422 0 : aValue.Append(char16_t(' '));
423 0 : origin->mValue.AppendToString(aTable[nsStyleImageLayers::origin], aValue,
424 0 : aSerialization);
425 :
426 0 : if (clip->mValue != origin->mValue) {
427 0 : aValue.Append(char16_t(' '));
428 0 : clip->mValue.AppendToString(aTable[nsStyleImageLayers::clip], aValue,
429 0 : aSerialization);
430 : }
431 : }
432 :
433 0 : if (composite) {
434 0 : aValue.Append(char16_t(' '));
435 0 : composite->mValue.AppendToString(aTable[nsStyleImageLayers::composite],
436 0 : aValue, aSerialization);
437 : }
438 :
439 0 : if (mode) {
440 0 : aValue.Append(char16_t(' '));
441 0 : mode->mValue.AppendToString(aTable[nsStyleImageLayers::maskMode],
442 0 : aValue, aSerialization);
443 : }
444 :
445 0 : image = image->mNext;
446 0 : repeat = repeat->mNext;
447 0 : positionX = positionX->mNext;
448 0 : positionY = positionY->mNext;
449 0 : clip = clip->mNext;
450 0 : origin = origin->mNext;
451 0 : size = size->mNext;
452 0 : attachment = attachment ? attachment->mNext : nullptr;
453 0 : composite = composite ? composite->mNext : nullptr;
454 0 : mode = mode ? mode->mNext : nullptr;
455 :
456 0 : if (!image) {
457 : // This layer is an background layer
458 0 : if (aTable == nsStyleImageLayers::kBackgroundLayerTable) {
459 0 : if (repeat || positionX || positionY || clip || origin || size ||
460 : attachment) {
461 : // Uneven length lists, so can't be serialized as shorthand.
462 0 : aValue.Truncate();
463 0 : return;
464 : }
465 : // This layer is an mask layer
466 : } else {
467 : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
468 0 : MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable);
469 : #else
470 : MOZ_ASSERT_UNREACHABLE("Should never get here when mask-as-shorthand is disable");
471 : #endif
472 0 : if (repeat || positionX || positionY || clip || origin || size ||
473 0 : composite || mode) {
474 : // Uneven length lists, so can't be serialized as shorthand.
475 0 : aValue.Truncate();
476 0 : return;
477 : }
478 : }
479 0 : break;
480 : }
481 :
482 : // This layer is an background layer
483 0 : if (aTable == nsStyleImageLayers::kBackgroundLayerTable) {
484 0 : if (!repeat || !positionX || !positionY || !clip || !origin || !size ||
485 : !attachment) {
486 : // Uneven length lists, so can't be serialized as shorthand.
487 0 : aValue.Truncate();
488 0 : return;
489 : }
490 : // This layer is an mask layer
491 : } else {
492 : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
493 0 : MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable);
494 : #else
495 : MOZ_ASSERT_UNREACHABLE("Should never get here when mask-as-shorthand is disable");
496 : #endif
497 0 : if (!repeat || !positionX || !positionY || !clip || !origin || !size ||
498 0 : !composite || !mode) {
499 : // Uneven length lists, so can't be serialized as shorthand.
500 0 : aValue.Truncate();
501 0 : return;
502 : }
503 : }
504 0 : aValue.Append(char16_t(','));
505 0 : aValue.Append(char16_t(' '));
506 0 : }
507 : }
508 :
509 : void
510 0 : Declaration::GetImageLayerPositionValue(
511 : nsCSSCompressedDataBlock *data,
512 : nsAString& aValue,
513 : nsCSSValue::Serialization aSerialization,
514 : const nsCSSPropertyID aTable[]) const
515 : {
516 : // We know from above that all subproperties were specified.
517 : // However, we still can't represent that in the shorthand unless
518 : // they're all lists of the same length. So if they're different
519 : // lengths, we need to bail out.
520 : const nsCSSValueList *positionX =
521 0 : data->ValueFor(aTable[nsStyleImageLayers::positionX])->GetListValue();
522 : const nsCSSValueList *positionY =
523 0 : data->ValueFor(aTable[nsStyleImageLayers::positionY])->GetListValue();
524 : for (;;) {
525 0 : AppendSingleImageLayerPositionValue(positionX->mValue, positionY->mValue,
526 0 : aTable, aValue, aSerialization);
527 0 : positionX = positionX->mNext;
528 0 : positionY = positionY->mNext;
529 :
530 0 : if (!positionX || !positionY) {
531 0 : if (positionX || positionY) {
532 : // Uneven length lists, so can't be serialized as shorthand.
533 0 : aValue.Truncate();
534 : }
535 0 : return;
536 : }
537 0 : aValue.Append(char16_t(','));
538 0 : aValue.Append(char16_t(' '));
539 : }
540 : }
541 :
542 : void
543 0 : Declaration::GetPropertyValueInternal(
544 : nsCSSPropertyID aProperty, nsAString& aValue,
545 : nsCSSValue::Serialization aSerialization, bool* aIsTokenStream) const
546 : {
547 0 : aValue.Truncate(0);
548 0 : if (aIsTokenStream) {
549 0 : *aIsTokenStream = false;
550 : }
551 :
552 : // simple properties are easy.
553 0 : if (!nsCSSProps::IsShorthand(aProperty)) {
554 0 : AppendValueToString(aProperty, aValue, aSerialization, aIsTokenStream);
555 0 : return;
556 : }
557 :
558 : // DOM Level 2 Style says (when describing CSS2Properties, although
559 : // not CSSStyleDeclaration.getPropertyValue):
560 : // However, if there is no shorthand declaration that could be added
561 : // to the ruleset without changing in any way the rules already
562 : // declared in the ruleset (i.e., by adding longhand rules that were
563 : // previously not declared in the ruleset), then the empty string
564 : // should be returned for the shorthand property.
565 : // This means we need to check a number of cases:
566 : // (1) Since a shorthand sets all sub-properties, if some of its
567 : // subproperties were not specified, we must return the empty
568 : // string.
569 : // (2) Since 'inherit', 'initial' and 'unset' can only be specified
570 : // as the values for entire properties, we need to return the
571 : // empty string if some but not all of the subproperties have one
572 : // of those values.
573 : // (3) Since a single value only makes sense with or without
574 : // !important, we return the empty string if some values are
575 : // !important and some are not.
576 : // Since we're doing this check for 'inherit' and 'initial' up front,
577 : // we can also simplify the property serialization code by serializing
578 : // those values up front as well.
579 : //
580 : // Additionally, if a shorthand property was set using a value with a
581 : // variable reference and none of its component longhand properties were
582 : // then overridden on the declaration, we return the token stream
583 : // assigned to the shorthand.
584 0 : const nsCSSValue* tokenStream = nullptr;
585 0 : uint32_t totalCount = 0, importantCount = 0,
586 0 : initialCount = 0, inheritCount = 0, unsetCount = 0,
587 0 : matchingTokenStreamCount = 0, nonMatchingTokenStreamCount = 0;
588 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty,
589 : CSSEnabledState::eForAllContent) {
590 0 : if (*p == eCSSProperty__x_system_font) {
591 : // The system-font subproperty doesn't count.
592 0 : continue;
593 : }
594 0 : ++totalCount;
595 0 : const nsCSSValue *val = mData->ValueFor(*p);
596 0 : MOZ_ASSERT(!val || !mImportantData || !mImportantData->ValueFor(*p),
597 : "can't be in both blocks");
598 0 : if (!val && mImportantData) {
599 0 : ++importantCount;
600 0 : val = mImportantData->ValueFor(*p);
601 : }
602 0 : if (!val) {
603 : // Case (1) above: some subproperties not specified.
604 0 : return;
605 : }
606 0 : if (val->GetUnit() == eCSSUnit_Inherit) {
607 0 : ++inheritCount;
608 0 : } else if (val->GetUnit() == eCSSUnit_Initial) {
609 0 : ++initialCount;
610 0 : } else if (val->GetUnit() == eCSSUnit_Unset) {
611 0 : ++unsetCount;
612 0 : } else if (val->GetUnit() == eCSSUnit_TokenStream) {
613 0 : if (val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) {
614 0 : tokenStream = val;
615 0 : ++matchingTokenStreamCount;
616 : } else {
617 0 : ++nonMatchingTokenStreamCount;
618 : }
619 : }
620 : }
621 0 : if (importantCount != 0 && importantCount != totalCount) {
622 : // Case (3), no consistent importance.
623 0 : return;
624 : }
625 0 : if (initialCount == totalCount) {
626 : // Simplify serialization below by serializing initial up-front.
627 0 : nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue,
628 0 : nsCSSValue::eNormalized);
629 0 : return;
630 : }
631 0 : if (inheritCount == totalCount) {
632 : // Simplify serialization below by serializing inherit up-front.
633 0 : nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue,
634 0 : nsCSSValue::eNormalized);
635 0 : return;
636 : }
637 0 : if (unsetCount == totalCount) {
638 : // Simplify serialization below by serializing unset up-front.
639 0 : nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue,
640 0 : nsCSSValue::eNormalized);
641 0 : return;
642 : }
643 0 : if (initialCount != 0 || inheritCount != 0 ||
644 0 : unsetCount != 0 || nonMatchingTokenStreamCount != 0) {
645 : // Case (2): partially initial, inherit, unset or token stream.
646 0 : return;
647 : }
648 0 : if (tokenStream) {
649 0 : if (matchingTokenStreamCount == totalCount) {
650 : // Shorthand was specified using variable references and all of its
651 : // longhand components were set by the shorthand.
652 0 : if (aIsTokenStream) {
653 0 : *aIsTokenStream = true;
654 : }
655 0 : aValue.Append(tokenStream->GetTokenStreamValue()->mTokenStream);
656 : } else {
657 : // In all other cases, serialize to the empty string.
658 : }
659 0 : return;
660 : }
661 :
662 0 : nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
663 0 : switch (aProperty) {
664 : case eCSSProperty_margin:
665 : case eCSSProperty_padding:
666 : case eCSSProperty_border_color:
667 : case eCSSProperty_border_style:
668 : case eCSSProperty_border_width: {
669 : const nsCSSPropertyID* subprops =
670 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
671 0 : MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
672 : kNotFound, "first subprop must be top");
673 0 : MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
674 : kNotFound, "second subprop must be right");
675 0 : MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
676 : kNotFound, "third subprop must be bottom");
677 0 : MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
678 : kNotFound, "fourth subprop must be left");
679 : const nsCSSValue* vals[4] = {
680 0 : data->ValueFor(subprops[0]),
681 0 : data->ValueFor(subprops[1]),
682 0 : data->ValueFor(subprops[2]),
683 0 : data->ValueFor(subprops[3])
684 0 : };
685 : nsCSSValue::AppendSidesShorthandToString(subprops, vals, aValue,
686 0 : aSerialization);
687 0 : break;
688 : }
689 : case eCSSProperty_border_radius:
690 : case eCSSProperty__moz_outline_radius: {
691 : const nsCSSPropertyID* subprops =
692 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
693 : const nsCSSValue* vals[4] = {
694 0 : data->ValueFor(subprops[0]),
695 0 : data->ValueFor(subprops[1]),
696 0 : data->ValueFor(subprops[2]),
697 0 : data->ValueFor(subprops[3])
698 0 : };
699 : nsCSSValue::AppendBasicShapeRadiusToString(subprops, vals, aValue,
700 0 : aSerialization);
701 0 : break;
702 : }
703 : case eCSSProperty_border_image: {
704 : // Even though there are some cases where we could omit
705 : // 'border-image-source' (when it's none), it's probably not a
706 : // good idea since it's likely to be confusing. It would also
707 : // require adding the extra check that we serialize *something*.
708 : AppendValueToString(eCSSProperty_border_image_source, aValue,
709 0 : aSerialization);
710 :
711 0 : bool sliceDefault = data->HasDefaultBorderImageSlice();
712 0 : bool widthDefault = data->HasDefaultBorderImageWidth();
713 0 : bool outsetDefault = data->HasDefaultBorderImageOutset();
714 :
715 0 : if (!sliceDefault || !widthDefault || !outsetDefault) {
716 0 : aValue.Append(char16_t(' '));
717 : AppendValueToString(eCSSProperty_border_image_slice, aValue,
718 0 : aSerialization);
719 0 : if (!widthDefault || !outsetDefault) {
720 0 : aValue.AppendLiteral(" /");
721 0 : if (!widthDefault) {
722 0 : aValue.Append(char16_t(' '));
723 : AppendValueToString(eCSSProperty_border_image_width, aValue,
724 0 : aSerialization);
725 : }
726 0 : if (!outsetDefault) {
727 0 : aValue.AppendLiteral(" / ");
728 : AppendValueToString(eCSSProperty_border_image_outset, aValue,
729 0 : aSerialization);
730 : }
731 : }
732 : }
733 :
734 0 : bool repeatDefault = data->HasDefaultBorderImageRepeat();
735 0 : if (!repeatDefault) {
736 0 : aValue.Append(char16_t(' '));
737 : AppendValueToString(eCSSProperty_border_image_repeat, aValue,
738 0 : aSerialization);
739 : }
740 0 : break;
741 : }
742 : case eCSSProperty_border: {
743 : // If we have a non-default value for any of the properties that
744 : // this shorthand sets but cannot specify, we have to return the
745 : // empty string.
746 0 : if (data->ValueFor(eCSSProperty_border_image_source)->GetUnit() !=
747 0 : eCSSUnit_None ||
748 0 : !data->HasDefaultBorderImageSlice() ||
749 0 : !data->HasDefaultBorderImageWidth() ||
750 0 : !data->HasDefaultBorderImageOutset() ||
751 0 : !data->HasDefaultBorderImageRepeat() ||
752 0 : data->ValueFor(eCSSProperty__moz_border_top_colors)->GetUnit() !=
753 0 : eCSSUnit_None ||
754 0 : data->ValueFor(eCSSProperty__moz_border_right_colors)->GetUnit() !=
755 0 : eCSSUnit_None ||
756 0 : data->ValueFor(eCSSProperty__moz_border_bottom_colors)->GetUnit() !=
757 0 : eCSSUnit_None ||
758 0 : data->ValueFor(eCSSProperty__moz_border_left_colors)->GetUnit() !=
759 : eCSSUnit_None) {
760 0 : break;
761 : }
762 :
763 : const nsCSSPropertyID* subproptables[3] = {
764 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
765 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
766 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
767 0 : };
768 0 : bool match = true;
769 0 : for (const nsCSSPropertyID** subprops = subproptables,
770 0 : **subprops_end = ArrayEnd(subproptables);
771 0 : subprops < subprops_end; ++subprops) {
772 0 : const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
773 0 : for (int32_t side = 1; side < 4; ++side) {
774 : const nsCSSValue *otherSide =
775 0 : data->ValueFor((*subprops)[side]);
776 0 : if (*firstSide != *otherSide)
777 0 : match = false;
778 : }
779 : }
780 0 : if (!match) {
781 : // We can't express what we have in the border shorthand
782 0 : break;
783 : }
784 : // tweak aProperty and fall through
785 0 : aProperty = eCSSProperty_border_top;
786 : MOZ_FALLTHROUGH;
787 : }
788 : case eCSSProperty_border_top:
789 : case eCSSProperty_border_right:
790 : case eCSSProperty_border_bottom:
791 : case eCSSProperty_border_left:
792 : case eCSSProperty_border_inline_start:
793 : case eCSSProperty_border_inline_end:
794 : case eCSSProperty_border_block_start:
795 : case eCSSProperty_border_block_end:
796 : case eCSSProperty_column_rule:
797 : case eCSSProperty_outline: {
798 : const nsCSSPropertyID* subprops =
799 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
800 0 : MOZ_ASSERT(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
801 : NS_LITERAL_CSTRING("-color")),
802 : "third subprop must be the color property");
803 :
804 0 : bool ok = AppendValueToString(subprops[0], aValue, aSerialization);
805 0 : if (ok) {
806 0 : aValue.Append(u' ');
807 0 : ok = AppendValueToString(subprops[1], aValue, aSerialization);
808 0 : if (ok) {
809 0 : const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
810 : bool isCurrentColor =
811 0 : colorValue->GetUnit() == eCSSUnit_EnumColor &&
812 0 : colorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR;
813 :
814 : // Don't output a third value when it's currentcolor.
815 0 : if (!isCurrentColor) {
816 0 : aValue.Append(u' ');
817 0 : ok = AppendValueToString(subprops[2], aValue, aSerialization);
818 : }
819 : }
820 : }
821 :
822 0 : if (!ok) {
823 0 : aValue.Truncate();
824 : }
825 0 : break;
826 : }
827 : case eCSSProperty_background: {
828 : GetImageLayerValue(data, aValue, aSerialization,
829 0 : nsStyleImageLayers::kBackgroundLayerTable);
830 0 : break;
831 : }
832 : case eCSSProperty_background_position: {
833 : GetImageLayerPositionValue(data, aValue, aSerialization,
834 0 : nsStyleImageLayers::kBackgroundLayerTable);
835 0 : break;
836 : }
837 : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
838 : case eCSSProperty_mask: {
839 : GetImageLayerValue(data, aValue, aSerialization,
840 0 : nsStyleImageLayers::kMaskLayerTable);
841 0 : break;
842 : }
843 : case eCSSProperty_mask_position: {
844 : GetImageLayerPositionValue(data, aValue, aSerialization,
845 0 : nsStyleImageLayers::kMaskLayerTable);
846 0 : break;
847 : }
848 : #endif
849 : case eCSSProperty_font: {
850 : // systemFont might not be present; other values are guaranteed to be
851 : // available based on the shorthand check at the beginning of the
852 : // function, as long as the prop is enabled
853 : const nsCSSValue *systemFont =
854 0 : data->ValueFor(eCSSProperty__x_system_font);
855 : const nsCSSValue *style =
856 0 : data->ValueFor(eCSSProperty_font_style);
857 : const nsCSSValue *weight =
858 0 : data->ValueFor(eCSSProperty_font_weight);
859 : const nsCSSValue *size =
860 0 : data->ValueFor(eCSSProperty_font_size);
861 : const nsCSSValue *lh =
862 0 : data->ValueFor(eCSSProperty_line_height);
863 : const nsCSSValue *family =
864 0 : data->ValueFor(eCSSProperty_font_family);
865 : const nsCSSValue *stretch =
866 0 : data->ValueFor(eCSSProperty_font_stretch);
867 : const nsCSSValue *sizeAdjust =
868 0 : data->ValueFor(eCSSProperty_font_size_adjust);
869 : const nsCSSValue *featureSettings =
870 0 : data->ValueFor(eCSSProperty_font_feature_settings);
871 : const nsCSSValue *languageOverride =
872 0 : data->ValueFor(eCSSProperty_font_language_override);
873 : const nsCSSValue *fontKerning =
874 0 : data->ValueFor(eCSSProperty_font_kerning);
875 : const nsCSSValue *fontVariantAlternates =
876 0 : data->ValueFor(eCSSProperty_font_variant_alternates);
877 : const nsCSSValue *fontVariantCaps =
878 0 : data->ValueFor(eCSSProperty_font_variant_caps);
879 : const nsCSSValue *fontVariantEastAsian =
880 0 : data->ValueFor(eCSSProperty_font_variant_east_asian);
881 : const nsCSSValue *fontVariantLigatures =
882 0 : data->ValueFor(eCSSProperty_font_variant_ligatures);
883 : const nsCSSValue *fontVariantNumeric =
884 0 : data->ValueFor(eCSSProperty_font_variant_numeric);
885 : const nsCSSValue *fontVariantPosition =
886 0 : data->ValueFor(eCSSProperty_font_variant_position);
887 :
888 0 : if (systemFont &&
889 0 : systemFont->GetUnit() != eCSSUnit_None &&
890 0 : systemFont->GetUnit() != eCSSUnit_Null) {
891 0 : if (style->GetUnit() != eCSSUnit_System_Font ||
892 0 : weight->GetUnit() != eCSSUnit_System_Font ||
893 0 : size->GetUnit() != eCSSUnit_System_Font ||
894 0 : lh->GetUnit() != eCSSUnit_System_Font ||
895 0 : family->GetUnit() != eCSSUnit_System_Font ||
896 0 : stretch->GetUnit() != eCSSUnit_System_Font ||
897 0 : sizeAdjust->GetUnit() != eCSSUnit_System_Font ||
898 0 : featureSettings->GetUnit() != eCSSUnit_System_Font ||
899 0 : languageOverride->GetUnit() != eCSSUnit_System_Font ||
900 0 : fontKerning->GetUnit() != eCSSUnit_System_Font ||
901 0 : fontVariantAlternates->GetUnit() != eCSSUnit_System_Font ||
902 0 : fontVariantCaps->GetUnit() != eCSSUnit_System_Font ||
903 0 : fontVariantEastAsian->GetUnit() != eCSSUnit_System_Font ||
904 0 : fontVariantLigatures->GetUnit() != eCSSUnit_System_Font ||
905 0 : fontVariantNumeric->GetUnit() != eCSSUnit_System_Font ||
906 0 : fontVariantPosition->GetUnit() != eCSSUnit_System_Font) {
907 : // This can't be represented as a shorthand.
908 0 : return;
909 : }
910 : systemFont->AppendToString(eCSSProperty__x_system_font, aValue,
911 0 : aSerialization);
912 : } else {
913 : // properties reset by this shorthand property to their
914 : // initial values but not represented in its syntax
915 0 : if (sizeAdjust->GetUnit() != eCSSUnit_None ||
916 0 : featureSettings->GetUnit() != eCSSUnit_Normal ||
917 0 : languageOverride->GetUnit() != eCSSUnit_Normal ||
918 0 : fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO ||
919 0 : fontVariantAlternates->GetUnit() != eCSSUnit_Normal ||
920 0 : fontVariantEastAsian->GetUnit() != eCSSUnit_Normal ||
921 0 : fontVariantLigatures->GetUnit() != eCSSUnit_Normal ||
922 0 : fontVariantNumeric->GetUnit() != eCSSUnit_Normal ||
923 0 : fontVariantPosition->GetUnit() != eCSSUnit_Normal) {
924 0 : return;
925 : }
926 :
927 : // only a normal or small-caps values of font-variant-caps can
928 : // be represented in the font shorthand
929 0 : if (fontVariantCaps->GetUnit() != eCSSUnit_Normal &&
930 0 : (fontVariantCaps->GetUnit() != eCSSUnit_Enumerated ||
931 0 : fontVariantCaps->GetIntValue() != NS_FONT_VARIANT_CAPS_SMALLCAPS)) {
932 0 : return;
933 : }
934 :
935 0 : if (style->GetUnit() != eCSSUnit_Enumerated ||
936 0 : style->GetIntValue() != NS_FONT_STYLE_NORMAL) {
937 : style->AppendToString(eCSSProperty_font_style, aValue,
938 0 : aSerialization);
939 0 : aValue.Append(char16_t(' '));
940 : }
941 0 : if (fontVariantCaps->GetUnit() != eCSSUnit_Normal) {
942 : fontVariantCaps->AppendToString(eCSSProperty_font_variant_caps, aValue,
943 0 : aSerialization);
944 0 : aValue.Append(char16_t(' '));
945 : }
946 0 : if (weight->GetUnit() != eCSSUnit_Enumerated ||
947 0 : weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
948 : weight->AppendToString(eCSSProperty_font_weight, aValue,
949 0 : aSerialization);
950 0 : aValue.Append(char16_t(' '));
951 : }
952 0 : if (stretch->GetUnit() != eCSSUnit_Enumerated ||
953 0 : stretch->GetIntValue() != NS_FONT_STRETCH_NORMAL) {
954 : stretch->AppendToString(eCSSProperty_font_stretch, aValue,
955 0 : aSerialization);
956 0 : aValue.Append(char16_t(' '));
957 : }
958 0 : size->AppendToString(eCSSProperty_font_size, aValue, aSerialization);
959 0 : if (lh->GetUnit() != eCSSUnit_Normal) {
960 0 : aValue.Append(char16_t('/'));
961 0 : lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization);
962 : }
963 0 : aValue.Append(char16_t(' '));
964 : family->AppendToString(eCSSProperty_font_family, aValue,
965 0 : aSerialization);
966 : }
967 0 : break;
968 : }
969 : case eCSSProperty_font_variant: {
970 : const nsCSSPropertyID *subprops =
971 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
972 : const nsCSSValue *fontVariantLigatures =
973 0 : data->ValueFor(eCSSProperty_font_variant_ligatures);
974 :
975 : // all subproperty values normal? system font?
976 0 : bool normalLigs = true, normalNonLigs = true, systemFont = true,
977 0 : hasSystem = false;
978 0 : for (const nsCSSPropertyID *sp = subprops; *sp != eCSSProperty_UNKNOWN; sp++) {
979 0 : const nsCSSValue *spVal = data->ValueFor(*sp);
980 0 : bool isNormal = (spVal->GetUnit() == eCSSUnit_Normal);
981 0 : if (*sp == eCSSProperty_font_variant_ligatures) {
982 0 : normalLigs = normalLigs && isNormal;
983 : } else {
984 0 : normalNonLigs = normalNonLigs && isNormal;
985 : }
986 0 : bool isSystem = (spVal->GetUnit() == eCSSUnit_System_Font);
987 0 : systemFont = systemFont && isSystem;
988 0 : hasSystem = hasSystem || isSystem;
989 : }
990 :
991 : bool ligsNone =
992 0 : fontVariantLigatures->GetUnit() == eCSSUnit_None;
993 :
994 : // normal, none, or system font ==> single value
995 0 : if ((normalLigs && normalNonLigs) ||
996 0 : (normalNonLigs && ligsNone) ||
997 : systemFont) {
998 : fontVariantLigatures->AppendToString(eCSSProperty_font_variant_ligatures,
999 : aValue,
1000 0 : aSerialization);
1001 0 : } else if (ligsNone || hasSystem) {
1002 : // ligatures none but other values are non-normal ==> empty
1003 : // at least one but not all values are system font ==> empty
1004 0 : return;
1005 : } else {
1006 : // iterate over and append non-normal values
1007 0 : bool appendSpace = false;
1008 0 : for (const nsCSSPropertyID *sp = subprops;
1009 0 : *sp != eCSSProperty_UNKNOWN; sp++) {
1010 0 : const nsCSSValue *spVal = data->ValueFor(*sp);
1011 0 : if (spVal && spVal->GetUnit() != eCSSUnit_Normal) {
1012 0 : if (appendSpace) {
1013 0 : aValue.Append(char16_t(' '));
1014 : } else {
1015 0 : appendSpace = true;
1016 : }
1017 0 : spVal->AppendToString(*sp, aValue, aSerialization);
1018 : }
1019 : }
1020 : }
1021 0 : break;
1022 : }
1023 : case eCSSProperty_list_style:
1024 0 : if (AppendValueToString(eCSSProperty_list_style_position, aValue,
1025 : aSerialization)) {
1026 0 : aValue.Append(char16_t(' '));
1027 : }
1028 0 : if (AppendValueToString(eCSSProperty_list_style_image, aValue,
1029 : aSerialization)) {
1030 0 : aValue.Append(char16_t(' '));
1031 : }
1032 : AppendValueToString(eCSSProperty_list_style_type, aValue,
1033 0 : aSerialization);
1034 0 : break;
1035 : case eCSSProperty_overflow: {
1036 : const nsCSSValue &xValue =
1037 0 : *data->ValueFor(eCSSProperty_overflow_x);
1038 : const nsCSSValue &yValue =
1039 0 : *data->ValueFor(eCSSProperty_overflow_y);
1040 0 : if (xValue == yValue)
1041 0 : xValue.AppendToString(eCSSProperty_overflow_x, aValue, aSerialization);
1042 0 : break;
1043 : }
1044 : case eCSSProperty_text_decoration: {
1045 : const nsCSSValue *decorationColor =
1046 0 : data->ValueFor(eCSSProperty_text_decoration_color);
1047 : const nsCSSValue *decorationStyle =
1048 0 : data->ValueFor(eCSSProperty_text_decoration_style);
1049 :
1050 0 : MOZ_ASSERT(decorationStyle->GetUnit() == eCSSUnit_Enumerated,
1051 : "bad text-decoration-style unit");
1052 :
1053 : AppendValueToString(eCSSProperty_text_decoration_line, aValue,
1054 0 : aSerialization);
1055 0 : if (decorationStyle->GetIntValue() !=
1056 : NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
1057 0 : aValue.Append(char16_t(' '));
1058 : AppendValueToString(eCSSProperty_text_decoration_style, aValue,
1059 0 : aSerialization);
1060 : }
1061 0 : if (decorationColor->GetUnit() != eCSSUnit_EnumColor ||
1062 0 : decorationColor->GetIntValue() != NS_COLOR_CURRENTCOLOR) {
1063 0 : aValue.Append(char16_t(' '));
1064 : AppendValueToString(eCSSProperty_text_decoration_color, aValue,
1065 0 : aSerialization);
1066 : }
1067 0 : break;
1068 : }
1069 : case eCSSProperty_transition: {
1070 : const nsCSSValue *transProp =
1071 0 : data->ValueFor(eCSSProperty_transition_property);
1072 : const nsCSSValue *transDuration =
1073 0 : data->ValueFor(eCSSProperty_transition_duration);
1074 : const nsCSSValue *transTiming =
1075 0 : data->ValueFor(eCSSProperty_transition_timing_function);
1076 : const nsCSSValue *transDelay =
1077 0 : data->ValueFor(eCSSProperty_transition_delay);
1078 :
1079 0 : MOZ_ASSERT(transDuration->GetUnit() == eCSSUnit_List ||
1080 : transDuration->GetUnit() == eCSSUnit_ListDep,
1081 : "bad t-duration unit");
1082 0 : MOZ_ASSERT(transTiming->GetUnit() == eCSSUnit_List ||
1083 : transTiming->GetUnit() == eCSSUnit_ListDep,
1084 : "bad t-timing unit");
1085 0 : MOZ_ASSERT(transDelay->GetUnit() == eCSSUnit_List ||
1086 : transDelay->GetUnit() == eCSSUnit_ListDep,
1087 : "bad t-delay unit");
1088 :
1089 0 : const nsCSSValueList* dur = transDuration->GetListValue();
1090 0 : const nsCSSValueList* tim = transTiming->GetListValue();
1091 0 : const nsCSSValueList* del = transDelay->GetListValue();
1092 :
1093 0 : if (transProp->GetUnit() == eCSSUnit_None ||
1094 0 : transProp->GetUnit() == eCSSUnit_All) {
1095 : // If any of the other three lists has more than one element,
1096 : // we can't use the shorthand.
1097 0 : if (!dur->mNext && !tim->mNext && !del->mNext) {
1098 : transProp->AppendToString(eCSSProperty_transition_property, aValue,
1099 0 : aSerialization);
1100 0 : aValue.Append(char16_t(' '));
1101 0 : dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue,
1102 0 : aSerialization);
1103 0 : aValue.Append(char16_t(' '));
1104 0 : tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
1105 0 : aValue, aSerialization);
1106 0 : aValue.Append(char16_t(' '));
1107 0 : del->mValue.AppendToString(eCSSProperty_transition_delay, aValue,
1108 0 : aSerialization);
1109 0 : aValue.Append(char16_t(' '));
1110 : } else {
1111 0 : aValue.Truncate();
1112 : }
1113 : } else {
1114 0 : MOZ_ASSERT(transProp->GetUnit() == eCSSUnit_List ||
1115 : transProp->GetUnit() == eCSSUnit_ListDep,
1116 : "bad t-prop unit");
1117 0 : const nsCSSValueList* pro = transProp->GetListValue();
1118 : for (;;) {
1119 0 : pro->mValue.AppendToString(eCSSProperty_transition_property,
1120 0 : aValue, aSerialization);
1121 0 : aValue.Append(char16_t(' '));
1122 0 : dur->mValue.AppendToString(eCSSProperty_transition_duration,
1123 0 : aValue, aSerialization);
1124 0 : aValue.Append(char16_t(' '));
1125 0 : tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
1126 0 : aValue, aSerialization);
1127 0 : aValue.Append(char16_t(' '));
1128 0 : del->mValue.AppendToString(eCSSProperty_transition_delay,
1129 0 : aValue, aSerialization);
1130 0 : pro = pro->mNext;
1131 0 : dur = dur->mNext;
1132 0 : tim = tim->mNext;
1133 0 : del = del->mNext;
1134 0 : if (!pro || !dur || !tim || !del) {
1135 : break;
1136 : }
1137 0 : aValue.AppendLiteral(", ");
1138 : }
1139 0 : if (pro || dur || tim || del) {
1140 : // Lists not all the same length, can't use shorthand.
1141 0 : aValue.Truncate();
1142 : }
1143 : }
1144 0 : break;
1145 : }
1146 : case eCSSProperty_animation: {
1147 : const nsCSSPropertyID* subprops =
1148 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
1149 : static const size_t numProps = 8;
1150 0 : MOZ_ASSERT(subprops[numProps] == eCSSProperty_UNKNOWN,
1151 : "unexpected number of subproperties");
1152 : const nsCSSValue* values[numProps];
1153 : const nsCSSValueList* lists[numProps];
1154 :
1155 0 : for (uint32_t i = 0; i < numProps; ++i) {
1156 0 : values[i] = data->ValueFor(subprops[i]);
1157 0 : MOZ_ASSERT(values[i]->GetUnit() == eCSSUnit_List ||
1158 : values[i]->GetUnit() == eCSSUnit_ListDep,
1159 : "bad a-duration unit");
1160 0 : lists[i] = values[i]->GetListValue();
1161 : }
1162 :
1163 : for (;;) {
1164 : // We must serialize 'animation-name' last in case it has
1165 : // a value that conflicts with one of the other keyword properties.
1166 0 : MOZ_ASSERT(subprops[numProps - 1] == eCSSProperty_animation_name,
1167 : "animation-name must be last");
1168 0 : bool done = false;
1169 0 : for (uint32_t i = 0;;) {
1170 0 : lists[i]->mValue.AppendToString(subprops[i], aValue, aSerialization);
1171 0 : lists[i] = lists[i]->mNext;
1172 0 : if (!lists[i]) {
1173 0 : done = true;
1174 : }
1175 0 : if (++i == numProps) {
1176 0 : break;
1177 : }
1178 0 : aValue.Append(char16_t(' '));
1179 : }
1180 0 : if (done) {
1181 0 : break;
1182 : }
1183 0 : aValue.AppendLiteral(", ");
1184 0 : }
1185 0 : for (uint32_t i = 0; i < numProps; ++i) {
1186 0 : if (lists[i]) {
1187 : // Lists not all the same length, can't use shorthand.
1188 0 : aValue.Truncate();
1189 0 : break;
1190 : }
1191 : }
1192 0 : break;
1193 : }
1194 : case eCSSProperty_marker: {
1195 : const nsCSSValue &endValue =
1196 0 : *data->ValueFor(eCSSProperty_marker_end);
1197 : const nsCSSValue &midValue =
1198 0 : *data->ValueFor(eCSSProperty_marker_mid);
1199 : const nsCSSValue &startValue =
1200 0 : *data->ValueFor(eCSSProperty_marker_start);
1201 0 : if (endValue == midValue && midValue == startValue)
1202 0 : AppendValueToString(eCSSProperty_marker_end, aValue, aSerialization);
1203 0 : break;
1204 : }
1205 : case eCSSProperty_columns: {
1206 : // Two values, column-count and column-width, separated by a space.
1207 : const nsCSSPropertyID* subprops =
1208 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
1209 0 : AppendValueToString(subprops[0], aValue, aSerialization);
1210 0 : aValue.Append(char16_t(' '));
1211 0 : AppendValueToString(subprops[1], aValue, aSerialization);
1212 0 : break;
1213 : }
1214 : case eCSSProperty_flex: {
1215 : // flex-grow, flex-shrink, flex-basis, separated by single space
1216 : const nsCSSPropertyID* subprops =
1217 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
1218 :
1219 0 : AppendValueToString(subprops[0], aValue, aSerialization);
1220 0 : aValue.Append(char16_t(' '));
1221 0 : AppendValueToString(subprops[1], aValue, aSerialization);
1222 0 : aValue.Append(char16_t(' '));
1223 0 : AppendValueToString(subprops[2], aValue, aSerialization);
1224 0 : break;
1225 : }
1226 : case eCSSProperty_flex_flow: {
1227 : // flex-direction, flex-wrap, separated by single space
1228 : const nsCSSPropertyID* subprops =
1229 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
1230 0 : MOZ_ASSERT(subprops[2] == eCSSProperty_UNKNOWN,
1231 : "must have exactly two subproperties");
1232 :
1233 0 : AppendValueToString(subprops[0], aValue, aSerialization);
1234 0 : aValue.Append(char16_t(' '));
1235 0 : AppendValueToString(subprops[1], aValue, aSerialization);
1236 0 : break;
1237 : }
1238 : case eCSSProperty_grid_row:
1239 : case eCSSProperty_grid_column: {
1240 : // grid-{row,column}-start, grid-{row,column}-end, separated by a slash
1241 : const nsCSSPropertyID* subprops =
1242 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
1243 0 : MOZ_ASSERT(subprops[2] == eCSSProperty_UNKNOWN,
1244 : "must have exactly two subproperties");
1245 :
1246 : // TODO: should we simplify when possible?
1247 0 : AppendValueToString(subprops[0], aValue, aSerialization);
1248 0 : aValue.AppendLiteral(" / ");
1249 0 : AppendValueToString(subprops[1], aValue, aSerialization);
1250 0 : break;
1251 : }
1252 : case eCSSProperty_grid_area: {
1253 : const nsCSSPropertyID* subprops =
1254 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
1255 0 : MOZ_ASSERT(subprops[4] == eCSSProperty_UNKNOWN,
1256 : "must have exactly four subproperties");
1257 :
1258 : // TODO: should we simplify when possible?
1259 0 : AppendValueToString(subprops[0], aValue, aSerialization);
1260 0 : aValue.AppendLiteral(" / ");
1261 0 : AppendValueToString(subprops[1], aValue, aSerialization);
1262 0 : aValue.AppendLiteral(" / ");
1263 0 : AppendValueToString(subprops[2], aValue, aSerialization);
1264 0 : aValue.AppendLiteral(" / ");
1265 0 : AppendValueToString(subprops[3], aValue, aSerialization);
1266 0 : break;
1267 : }
1268 :
1269 : // The 'grid' shorthand has 3 different possibilities for syntax:
1270 : // #1 <'grid-template'>
1271 : // #2 <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>?
1272 : // #3 [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
1273 : case eCSSProperty_grid: {
1274 : const nsCSSValue& columnGapValue =
1275 0 : *data->ValueFor(eCSSProperty_grid_column_gap);
1276 0 : if (columnGapValue.GetUnit() != eCSSUnit_Pixel ||
1277 0 : columnGapValue.GetFloatValue() != 0.0f) {
1278 0 : return; // Not serializable, bail.
1279 : }
1280 : const nsCSSValue& rowGapValue =
1281 0 : *data->ValueFor(eCSSProperty_grid_row_gap);
1282 0 : if (rowGapValue.GetUnit() != eCSSUnit_Pixel ||
1283 0 : rowGapValue.GetFloatValue() != 0.0f) {
1284 0 : return; // Not serializable, bail.
1285 : }
1286 : const nsCSSValue& areasValue =
1287 0 : *data->ValueFor(eCSSProperty_grid_template_areas);
1288 : const nsCSSValue& columnsValue =
1289 0 : *data->ValueFor(eCSSProperty_grid_template_columns);
1290 : const nsCSSValue& rowsValue =
1291 0 : *data->ValueFor(eCSSProperty_grid_template_rows);
1292 :
1293 : const nsCSSValue& autoFlowValue =
1294 0 : *data->ValueFor(eCSSProperty_grid_auto_flow);
1295 : const nsCSSValue& autoColumnsValue =
1296 0 : *data->ValueFor(eCSSProperty_grid_auto_columns);
1297 : const nsCSSValue& autoRowsValue =
1298 0 : *data->ValueFor(eCSSProperty_grid_auto_rows);
1299 :
1300 : // grid-template-rows/areas:none + default grid-auto-columns +
1301 : // non-default row grid-auto-flow or grid-auto-rows.
1302 : // --> serialize as 'grid' syntax #3.
1303 : // (for default grid-auto-flow/rows we prefer to serialize to
1304 : // "none ['/' ...]" instead using syntax #2 or #1 below)
1305 0 : if (rowsValue.GetUnit() == eCSSUnit_None &&
1306 0 : areasValue.GetUnit() == eCSSUnit_None &&
1307 0 : autoColumnsValue.GetUnit() == eCSSUnit_Auto &&
1308 0 : autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
1309 0 : (autoFlowValue.GetIntValue() & NS_STYLE_GRID_AUTO_FLOW_ROW) &&
1310 0 : (autoFlowValue.GetIntValue() != NS_STYLE_GRID_AUTO_FLOW_ROW ||
1311 0 : autoRowsValue.GetUnit() != eCSSUnit_Auto)) {
1312 0 : aValue.AppendLiteral("auto-flow");
1313 0 : if (autoFlowValue.GetIntValue() & NS_STYLE_GRID_AUTO_FLOW_DENSE) {
1314 0 : aValue.AppendLiteral(" dense");
1315 : }
1316 0 : if (autoRowsValue.GetUnit() != eCSSUnit_Auto) {
1317 0 : aValue.Append(' ');
1318 : AppendValueToString(eCSSProperty_grid_auto_rows,
1319 0 : aValue, aSerialization);
1320 : }
1321 0 : aValue.AppendLiteral(" / ");
1322 : AppendValueToString(eCSSProperty_grid_template_columns,
1323 0 : aValue, aSerialization);
1324 0 : break;
1325 : }
1326 :
1327 : // grid-template-columns/areas:none + column grid-auto-flow +
1328 : // default grid-auto-rows.
1329 : // --> serialize as 'grid' syntax #2.
1330 0 : if (columnsValue.GetUnit() == eCSSUnit_None &&
1331 0 : areasValue.GetUnit() == eCSSUnit_None &&
1332 0 : autoRowsValue.GetUnit() == eCSSUnit_Auto &&
1333 0 : autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
1334 0 : (autoFlowValue.GetIntValue() & NS_STYLE_GRID_AUTO_FLOW_COLUMN)) {
1335 : AppendValueToString(eCSSProperty_grid_template_rows,
1336 0 : aValue, aSerialization);
1337 0 : aValue.AppendLiteral(" / auto-flow ");
1338 0 : if (autoFlowValue.GetIntValue() & NS_STYLE_GRID_AUTO_FLOW_DENSE) {
1339 0 : aValue.AppendLiteral("dense ");
1340 : }
1341 : AppendValueToString(eCSSProperty_grid_auto_columns,
1342 0 : aValue, aSerialization);
1343 0 : break;
1344 : }
1345 :
1346 0 : if (!(autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
1347 0 : autoFlowValue.GetIntValue() == NS_STYLE_GRID_AUTO_FLOW_ROW &&
1348 0 : autoColumnsValue.GetUnit() == eCSSUnit_Auto &&
1349 0 : autoRowsValue.GetUnit() == eCSSUnit_Auto)) {
1350 : // Not serializable, bail.
1351 0 : return;
1352 : }
1353 : // Fall through to eCSSProperty_grid_template (syntax #1)
1354 : MOZ_FALLTHROUGH;
1355 : }
1356 : case eCSSProperty_grid_template: {
1357 : const nsCSSValue& areasValue =
1358 0 : *data->ValueFor(eCSSProperty_grid_template_areas);
1359 : const nsCSSValue& columnsValue =
1360 0 : *data->ValueFor(eCSSProperty_grid_template_columns);
1361 : const nsCSSValue& rowsValue =
1362 0 : *data->ValueFor(eCSSProperty_grid_template_rows);
1363 0 : if (areasValue.GetUnit() == eCSSUnit_None) {
1364 : AppendValueToString(eCSSProperty_grid_template_rows,
1365 0 : aValue, aSerialization);
1366 0 : aValue.AppendLiteral(" / ");
1367 : AppendValueToString(eCSSProperty_grid_template_columns,
1368 0 : aValue, aSerialization);
1369 0 : break;
1370 : }
1371 0 : if (columnsValue.GetUnit() == eCSSUnit_List ||
1372 0 : columnsValue.GetUnit() == eCSSUnit_ListDep) {
1373 0 : const nsCSSValueList* columnsItem = columnsValue.GetListValue();
1374 0 : if (columnsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
1375 0 : columnsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
1376 : // We have "grid-template-areas:[something]; grid-template-columns:subgrid"
1377 : // which isn't a value that the shorthand can express. Bail.
1378 0 : return;
1379 : }
1380 : }
1381 0 : if (rowsValue.GetUnit() != eCSSUnit_List &&
1382 0 : rowsValue.GetUnit() != eCSSUnit_ListDep) {
1383 : // We have "grid-template-areas:[something]; grid-template-rows:none"
1384 : // which isn't a value that the shorthand can express. Bail.
1385 0 : return;
1386 : }
1387 0 : const nsCSSValueList* rowsItem = rowsValue.GetListValue();
1388 0 : if (rowsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
1389 0 : rowsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
1390 : // We have "grid-template-areas:[something]; grid-template-rows:subgrid"
1391 : // which isn't a value that the shorthand can express. Bail.
1392 0 : return;
1393 : }
1394 0 : const GridTemplateAreasValue* areas = areasValue.GetGridTemplateAreas();
1395 0 : uint32_t nRowItems = 0;
1396 0 : while (rowsItem) {
1397 0 : nRowItems++;
1398 0 : rowsItem = rowsItem->mNext;
1399 : }
1400 0 : MOZ_ASSERT(nRowItems % 2 == 1, "expected an odd number of items");
1401 0 : if ((nRowItems - 1) / 2 != areas->NRows()) {
1402 : // Not serializable, bail.
1403 0 : return;
1404 : }
1405 0 : rowsItem = rowsValue.GetListValue();
1406 0 : uint32_t row = 0;
1407 : for (;;) {
1408 0 : bool addSpaceSeparator = true;
1409 0 : nsCSSUnit unit = rowsItem->mValue.GetUnit();
1410 :
1411 0 : if (unit == eCSSUnit_Null) {
1412 : // Empty or omitted <line-names>. Serializes to nothing.
1413 0 : addSpaceSeparator = false; // Avoid a double space.
1414 :
1415 0 : } else if (unit == eCSSUnit_List || unit == eCSSUnit_ListDep) {
1416 : // Non-empty <line-names>
1417 0 : aValue.Append('[');
1418 0 : rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
1419 0 : aValue, aSerialization);
1420 0 : aValue.Append(']');
1421 :
1422 : } else {
1423 0 : nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[row++], aValue);
1424 0 : aValue.Append(char16_t(' '));
1425 :
1426 : // <track-size>
1427 0 : if (unit == eCSSUnit_Pair) {
1428 : // 'repeat()' isn't allowed with non-default 'grid-template-areas'.
1429 0 : aValue.Truncate();
1430 0 : return;
1431 : }
1432 0 : rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
1433 0 : aValue, aSerialization);
1434 0 : if (rowsItem->mNext &&
1435 0 : rowsItem->mNext->mValue.GetUnit() == eCSSUnit_Null &&
1436 0 : !rowsItem->mNext->mNext) {
1437 : // Break out of the loop early to avoid a trailing space.
1438 0 : break;
1439 : }
1440 : }
1441 :
1442 0 : rowsItem = rowsItem->mNext;
1443 0 : if (!rowsItem) {
1444 0 : break;
1445 : }
1446 :
1447 0 : if (addSpaceSeparator) {
1448 0 : aValue.Append(char16_t(' '));
1449 : }
1450 0 : }
1451 0 : if (columnsValue.GetUnit() != eCSSUnit_None) {
1452 0 : const nsCSSValueList* colsItem = columnsValue.GetListValue();
1453 0 : colsItem = colsItem->mNext; // first value is <line-names>
1454 0 : for (; colsItem; colsItem = colsItem->mNext) {
1455 0 : if (colsItem->mValue.GetUnit() == eCSSUnit_Pair) {
1456 : // 'repeat()' isn't allowed with non-default 'grid-template-areas'.
1457 0 : aValue.Truncate();
1458 0 : return;
1459 : }
1460 0 : colsItem = colsItem->mNext; // skip <line-names>
1461 : }
1462 0 : aValue.AppendLiteral(" / ");
1463 : AppendValueToString(eCSSProperty_grid_template_columns,
1464 0 : aValue, aSerialization);
1465 : }
1466 0 : break;
1467 : }
1468 : case eCSSProperty_place_content:
1469 : case eCSSProperty_place_items:
1470 : case eCSSProperty_place_self: {
1471 : const nsCSSPropertyID* subprops =
1472 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
1473 0 : MOZ_ASSERT(subprops[2] == eCSSProperty_UNKNOWN,
1474 : "must have exactly two subproperties");
1475 0 : auto IsSingleValue = [] (const nsCSSValue& aValue) {
1476 0 : switch (aValue.GetUnit()) {
1477 : case eCSSUnit_Auto:
1478 : case eCSSUnit_Inherit:
1479 : case eCSSUnit_Initial:
1480 : case eCSSUnit_Unset:
1481 0 : return true;
1482 : case eCSSUnit_Enumerated:
1483 : // return false if there is a fallback value or <overflow-position>
1484 0 : return aValue.GetIntValue() <= NS_STYLE_JUSTIFY_SPACE_EVENLY;
1485 : default:
1486 0 : MOZ_ASSERT_UNREACHABLE("Unexpected unit for CSS Align property val");
1487 : return false;
1488 : }
1489 : };
1490 : // Each value must be a single value (i.e. no fallback value and no
1491 : // <overflow-position>), otherwise it can't be represented as a shorthand
1492 : // value. ('first|last baseline' counts as a single value)
1493 0 : const nsCSSValue* align = data->ValueFor(subprops[0]);
1494 0 : const nsCSSValue* justify = data->ValueFor(subprops[1]);
1495 0 : if (!align || !IsSingleValue(*align) ||
1496 0 : !justify || !IsSingleValue(*justify)) {
1497 0 : return; // Not serializable, bail.
1498 : }
1499 : MOZ_FALLTHROUGH;
1500 : }
1501 : case eCSSProperty_grid_gap: {
1502 : const nsCSSPropertyID* subprops =
1503 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
1504 0 : MOZ_ASSERT(subprops[2] == eCSSProperty_UNKNOWN,
1505 : "must have exactly two subproperties");
1506 :
1507 0 : nsAutoString val1, val2;
1508 0 : AppendValueToString(subprops[0], val1, aSerialization);
1509 0 : AppendValueToString(subprops[1], val2, aSerialization);
1510 0 : if (val1 == val2) {
1511 0 : aValue.Append(val1);
1512 : } else {
1513 0 : aValue.Append(val1);
1514 0 : aValue.Append(' ');
1515 0 : aValue.Append(val2);
1516 : }
1517 0 : break;
1518 : }
1519 : case eCSSProperty_text_emphasis: {
1520 : const nsCSSValue* emphasisStyle =
1521 0 : data->ValueFor(eCSSProperty_text_emphasis_style);
1522 : const nsCSSValue* emphasisColor =
1523 0 : data->ValueFor(eCSSProperty_text_emphasis_color);
1524 0 : bool isDefaultColor = emphasisColor->GetUnit() == eCSSUnit_EnumColor &&
1525 0 : emphasisColor->GetIntValue() == NS_COLOR_CURRENTCOLOR;
1526 :
1527 0 : if (emphasisStyle->GetUnit() != eCSSUnit_None || isDefaultColor) {
1528 : AppendValueToString(eCSSProperty_text_emphasis_style,
1529 0 : aValue, aSerialization);
1530 0 : if (!isDefaultColor) {
1531 0 : aValue.Append(char16_t(' '));
1532 : }
1533 : }
1534 0 : if (!isDefaultColor) {
1535 : AppendValueToString(eCSSProperty_text_emphasis_color,
1536 0 : aValue, aSerialization);
1537 : }
1538 0 : break;
1539 : }
1540 : case eCSSProperty__moz_transform: {
1541 : // shorthands that are just aliases with different parsing rules
1542 : const nsCSSPropertyID* subprops =
1543 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
1544 0 : MOZ_ASSERT(subprops[1] == eCSSProperty_UNKNOWN,
1545 : "must have exactly one subproperty");
1546 0 : AppendValueToString(subprops[0], aValue, aSerialization);
1547 0 : break;
1548 : }
1549 : case eCSSProperty_scroll_snap_type: {
1550 : const nsCSSValue& xValue =
1551 0 : *data->ValueFor(eCSSProperty_scroll_snap_type_x);
1552 : const nsCSSValue& yValue =
1553 0 : *data->ValueFor(eCSSProperty_scroll_snap_type_y);
1554 0 : if (xValue == yValue) {
1555 : AppendValueToString(eCSSProperty_scroll_snap_type_x, aValue,
1556 0 : aSerialization);
1557 : }
1558 : // If scroll-snap-type-x and scroll-snap-type-y are not equal,
1559 : // we don't have a shorthand that can express. Bail.
1560 0 : break;
1561 : }
1562 : case eCSSProperty__webkit_text_stroke: {
1563 : const nsCSSValue* strokeWidth =
1564 0 : data->ValueFor(eCSSProperty__webkit_text_stroke_width);
1565 : const nsCSSValue* strokeColor =
1566 0 : data->ValueFor(eCSSProperty__webkit_text_stroke_color);
1567 0 : bool isDefaultColor = strokeColor->GetUnit() == eCSSUnit_EnumColor &&
1568 0 : strokeColor->GetIntValue() == NS_COLOR_CURRENTCOLOR;
1569 :
1570 0 : if (strokeWidth->GetUnit() != eCSSUnit_Integer ||
1571 0 : strokeWidth->GetIntValue() != 0 || isDefaultColor) {
1572 : AppendValueToString(eCSSProperty__webkit_text_stroke_width,
1573 0 : aValue, aSerialization);
1574 0 : if (!isDefaultColor) {
1575 0 : aValue.Append(char16_t(' '));
1576 : }
1577 : }
1578 0 : if (!isDefaultColor) {
1579 : AppendValueToString(eCSSProperty__webkit_text_stroke_color,
1580 0 : aValue, aSerialization);
1581 : }
1582 0 : break;
1583 : }
1584 : case eCSSProperty_all:
1585 : // If we got here, then we didn't have all "inherit" or "initial" or
1586 : // "unset" values for all of the longhand property components of 'all'.
1587 : // There is no other possible value that is valid for all properties,
1588 : // so serialize as the empty string.
1589 0 : break;
1590 : default:
1591 0 : MOZ_ASSERT(false, "no other shorthands");
1592 : break;
1593 : }
1594 : }
1595 :
1596 : bool
1597 0 : Declaration::GetPropertyIsImportantByID(nsCSSPropertyID aProperty) const
1598 : {
1599 0 : if (!mImportantData)
1600 0 : return false;
1601 :
1602 : // Calling ValueFor is inefficient, but we can assume '!important' is rare.
1603 :
1604 0 : if (!nsCSSProps::IsShorthand(aProperty)) {
1605 0 : return mImportantData->ValueFor(aProperty) != nullptr;
1606 : }
1607 :
1608 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty,
1609 : CSSEnabledState::eForAllContent) {
1610 0 : if (*p == eCSSProperty__x_system_font) {
1611 : // The system_font subproperty doesn't count.
1612 0 : continue;
1613 : }
1614 0 : if (!mImportantData->ValueFor(*p)) {
1615 0 : return false;
1616 : }
1617 : }
1618 0 : return true;
1619 : }
1620 :
1621 : void
1622 0 : Declaration::AppendPropertyAndValueToString(nsCSSPropertyID aProperty,
1623 : nsAString& aResult,
1624 : nsAutoString& aValue,
1625 : bool aValueIsTokenStream) const
1626 : {
1627 0 : MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT,
1628 : "property enum out of range");
1629 0 : MOZ_ASSERT((aProperty < eCSSProperty_COUNT_no_shorthands) == aValue.IsEmpty(),
1630 : "aValue should be given for shorthands but not longhands");
1631 0 : AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
1632 0 : if (aValue.IsEmpty()) {
1633 0 : AppendValueToString(aProperty, aValue,
1634 0 : nsCSSValue::eNormalized, &aValueIsTokenStream);
1635 : }
1636 0 : aResult.Append(':');
1637 0 : if (!aValueIsTokenStream) {
1638 0 : aResult.Append(' ');
1639 : }
1640 0 : aResult.Append(aValue);
1641 0 : if (GetPropertyIsImportantByID(aProperty)) {
1642 0 : if (!aValueIsTokenStream) {
1643 0 : aResult.Append(' ');
1644 : }
1645 0 : aResult.AppendLiteral("!important");
1646 : }
1647 0 : aResult.AppendLiteral("; ");
1648 0 : }
1649 :
1650 : void
1651 0 : Declaration::AppendVariableAndValueToString(const nsAString& aName,
1652 : nsAString& aResult) const
1653 : {
1654 0 : nsAutoString localName;
1655 0 : localName.AppendLiteral("--");
1656 0 : localName.Append(aName);
1657 0 : nsStyleUtil::AppendEscapedCSSIdent(localName, aResult);
1658 : CSSVariableDeclarations::Type type;
1659 0 : nsString value;
1660 : bool important;
1661 :
1662 0 : if (mImportantVariables && mImportantVariables->Get(aName, type, value)) {
1663 0 : important = true;
1664 : } else {
1665 0 : MOZ_ASSERT(mVariables);
1666 0 : MOZ_ASSERT(mVariables->Has(aName));
1667 0 : mVariables->Get(aName, type, value);
1668 0 : important = false;
1669 : }
1670 :
1671 0 : bool isTokenStream = type == CSSVariableDeclarations::eTokenStream;
1672 0 : aResult.Append(':');
1673 0 : if (!isTokenStream) {
1674 0 : aResult.Append(' ');
1675 : }
1676 0 : switch (type) {
1677 : case CSSVariableDeclarations::eTokenStream:
1678 0 : aResult.Append(value);
1679 0 : break;
1680 :
1681 : case CSSVariableDeclarations::eInitial:
1682 0 : aResult.AppendLiteral("initial");
1683 0 : break;
1684 :
1685 : case CSSVariableDeclarations::eInherit:
1686 0 : aResult.AppendLiteral("inherit");
1687 0 : break;
1688 :
1689 : case CSSVariableDeclarations::eUnset:
1690 0 : aResult.AppendLiteral("unset");
1691 0 : break;
1692 :
1693 : default:
1694 0 : MOZ_ASSERT(false, "unexpected variable value type");
1695 : }
1696 :
1697 0 : if (important) {
1698 0 : if (!isTokenStream) {
1699 0 : aResult.Append(' ');
1700 : }
1701 0 : aResult.AppendLiteral("!important");
1702 : }
1703 0 : aResult.AppendLiteral("; ");
1704 0 : }
1705 :
1706 : void
1707 0 : Declaration::ToString(nsAString& aString) const
1708 : {
1709 : // Tell the static analysis not to worry about thread-unsafe things here
1710 : // because because this function isn't reached during parallel style traversal.
1711 0 : MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
1712 :
1713 : nsCSSCompressedDataBlock *systemFontData =
1714 0 : GetPropertyIsImportantByID(eCSSProperty__x_system_font) ? mImportantData
1715 0 : : mData;
1716 : const nsCSSValue *systemFont =
1717 0 : systemFontData->ValueFor(eCSSProperty__x_system_font);
1718 0 : const bool haveSystemFont = systemFont &&
1719 0 : systemFont->GetUnit() != eCSSUnit_None &&
1720 0 : systemFont->GetUnit() != eCSSUnit_Null;
1721 0 : bool didSystemFont = false;
1722 :
1723 0 : int32_t count = mOrder.Length();
1724 : int32_t index;
1725 0 : AutoTArray<nsCSSPropertyID, 16> shorthandsUsed;
1726 0 : for (index = 0; index < count; index++) {
1727 0 : nsCSSPropertyID property = GetPropertyAt(index);
1728 :
1729 0 : if (property == eCSSPropertyExtra_variable) {
1730 0 : uint32_t variableIndex = mOrder[index] - eCSSProperty_COUNT;
1731 0 : AppendVariableAndValueToString(mVariableOrder[variableIndex], aString);
1732 0 : continue;
1733 : }
1734 :
1735 0 : if (!nsCSSProps::IsEnabled(property, CSSEnabledState::eForAllContent)) {
1736 0 : continue;
1737 : }
1738 0 : bool doneProperty = false;
1739 :
1740 : // If we already used this property in a shorthand, skip it.
1741 0 : if (shorthandsUsed.Length() > 0) {
1742 0 : for (const nsCSSPropertyID *shorthands =
1743 0 : nsCSSProps::ShorthandsContaining(property);
1744 0 : *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
1745 0 : if (shorthandsUsed.Contains(*shorthands)) {
1746 0 : doneProperty = true;
1747 0 : break;
1748 : }
1749 : }
1750 0 : if (doneProperty)
1751 0 : continue;
1752 : }
1753 :
1754 : // Try to use this property in a shorthand.
1755 0 : nsAutoString value;
1756 0 : for (const nsCSSPropertyID *shorthands =
1757 0 : nsCSSProps::ShorthandsContaining(property);
1758 0 : *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
1759 : // ShorthandsContaining returns the shorthands in order from those
1760 : // that contain the most subproperties to those that contain the
1761 : // least, which is exactly the order we want to test them.
1762 0 : nsCSSPropertyID shorthand = *shorthands;
1763 :
1764 : bool isTokenStream;
1765 : GetPropertyValueInternal(shorthand, value,
1766 0 : nsCSSValue::eNormalized, &isTokenStream);
1767 :
1768 : // in the system font case, skip over font-variant shorthand, since all
1769 : // subproperties are already dealt with via the font shorthand
1770 0 : if (shorthand == eCSSProperty_font_variant &&
1771 0 : value.EqualsLiteral("-moz-use-system-font")) {
1772 0 : continue;
1773 : }
1774 :
1775 : // If GetPropertyValueInternal gives us a non-empty string back, we can
1776 : // use that value; otherwise it's not possible to use this shorthand.
1777 0 : if (!value.IsEmpty()) {
1778 0 : AppendPropertyAndValueToString(shorthand, aString,
1779 0 : value, isTokenStream);
1780 0 : shorthandsUsed.AppendElement(shorthand);
1781 0 : doneProperty = true;
1782 0 : break;
1783 : }
1784 :
1785 0 : if (shorthand == eCSSProperty_font) {
1786 0 : if (haveSystemFont && !didSystemFont) {
1787 : // Output the shorthand font declaration that we will
1788 : // partially override later. But don't add it to
1789 : // |shorthandsUsed|, since we will have to override it.
1790 : systemFont->AppendToString(eCSSProperty__x_system_font, value,
1791 0 : nsCSSValue::eNormalized);
1792 0 : isTokenStream = systemFont->GetUnit() == eCSSUnit_TokenStream;
1793 0 : AppendPropertyAndValueToString(eCSSProperty_font, aString,
1794 0 : value, isTokenStream);
1795 0 : value.Truncate();
1796 0 : didSystemFont = true;
1797 : }
1798 :
1799 : // That we output the system font is enough for this property if:
1800 : // (1) it's the hidden system font subproperty (which either
1801 : // means we output it or we don't have it), or
1802 : // (2) its value is the hidden system font value and it matches
1803 : // the hidden system font subproperty in importance, and
1804 : // we output the system font subproperty.
1805 0 : const nsCSSValue *val = systemFontData->ValueFor(property);
1806 0 : if (property == eCSSProperty__x_system_font ||
1807 0 : (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
1808 0 : doneProperty = true;
1809 0 : break;
1810 : }
1811 : }
1812 : }
1813 0 : if (doneProperty)
1814 0 : continue;
1815 :
1816 0 : MOZ_ASSERT(value.IsEmpty(), "value should be empty now");
1817 0 : AppendPropertyAndValueToString(property, aString, value, false);
1818 : }
1819 0 : if (! aString.IsEmpty()) {
1820 : // if the string is not empty, we have trailing whitespace we
1821 : // should remove
1822 0 : aString.Truncate(aString.Length() - 1);
1823 : }
1824 0 : }
1825 :
1826 : #ifdef DEBUG
1827 : /* virtual */ void
1828 0 : Declaration::List(FILE* out, int32_t aIndent) const
1829 : {
1830 0 : const Rule* owningRule = GetOwningRule();
1831 0 : if (owningRule) {
1832 : // More useful to print the selector and sheet URI too.
1833 0 : owningRule->List(out, aIndent);
1834 0 : return;
1835 : }
1836 :
1837 0 : nsAutoCString str;
1838 0 : for (int32_t index = aIndent; --index >= 0; ) {
1839 0 : str.AppendLiteral(" ");
1840 : }
1841 :
1842 0 : str.AppendLiteral("{ ");
1843 0 : nsAutoString s;
1844 0 : ToString(s);
1845 0 : AppendUTF16toUTF8(s, str);
1846 0 : str.AppendLiteral("}\n");
1847 0 : fprintf_stderr(out, "%s", str.get());
1848 : }
1849 : #endif
1850 :
1851 : bool
1852 0 : Declaration::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
1853 : {
1854 0 : aReturn.Truncate();
1855 0 : if (aIndex < mOrder.Length()) {
1856 0 : nsCSSPropertyID property = GetPropertyAt(aIndex);
1857 0 : if (property == eCSSPropertyExtra_variable) {
1858 0 : GetCustomPropertyNameAt(aIndex, aReturn);
1859 0 : return true;
1860 : }
1861 0 : if (0 <= property) {
1862 0 : AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
1863 0 : return true;
1864 : }
1865 : }
1866 0 : return false;
1867 : }
1868 :
1869 : void
1870 84 : Declaration::InitializeEmpty()
1871 : {
1872 84 : MOZ_ASSERT(!mData && !mImportantData, "already initialized");
1873 84 : mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
1874 84 : }
1875 :
1876 : size_t
1877 14 : Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
1878 : {
1879 14 : size_t n = aMallocSizeOf(this);
1880 14 : n += mOrder.ShallowSizeOfExcludingThis(aMallocSizeOf);
1881 14 : n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0;
1882 14 : n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
1883 14 : if (mVariables) {
1884 0 : n += mVariables->SizeOfIncludingThis(aMallocSizeOf);
1885 : }
1886 14 : if (mImportantVariables) {
1887 0 : n += mImportantVariables->SizeOfIncludingThis(aMallocSizeOf);
1888 : }
1889 14 : return n;
1890 : }
1891 :
1892 : void
1893 0 : Declaration::GetVariableValue(const nsAString& aName, nsAString& aValue) const
1894 : {
1895 0 : aValue.Truncate();
1896 :
1897 : CSSVariableDeclarations::Type type;
1898 0 : nsString value;
1899 :
1900 0 : if ((mImportantVariables && mImportantVariables->Get(aName, type, value)) ||
1901 0 : (mVariables && mVariables->Get(aName, type, value))) {
1902 0 : switch (type) {
1903 : case CSSVariableDeclarations::eTokenStream:
1904 0 : aValue.Append(value);
1905 0 : break;
1906 :
1907 : case CSSVariableDeclarations::eInitial:
1908 0 : aValue.AppendLiteral("initial");
1909 0 : break;
1910 :
1911 : case CSSVariableDeclarations::eInherit:
1912 0 : aValue.AppendLiteral("inherit");
1913 0 : break;
1914 :
1915 : case CSSVariableDeclarations::eUnset:
1916 0 : aValue.AppendLiteral("unset");
1917 0 : break;
1918 :
1919 : default:
1920 0 : MOZ_ASSERT(false, "unexpected variable value type");
1921 : }
1922 : }
1923 0 : }
1924 :
1925 : void
1926 74 : Declaration::AddVariable(const nsAString& aName,
1927 : CSSVariableDeclarations::Type aType,
1928 : const nsString& aValue,
1929 : bool aIsImportant,
1930 : bool aOverrideImportant)
1931 : {
1932 74 : MOZ_ASSERT(IsMutable());
1933 :
1934 74 : nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
1935 74 : if (index == nsTArray<nsString>::NoIndex) {
1936 74 : index = mVariableOrder.Length();
1937 74 : mVariableOrder.AppendElement(aName);
1938 : }
1939 :
1940 296 : if (!aIsImportant && !aOverrideImportant &&
1941 148 : mImportantVariables && mImportantVariables->Has(aName)) {
1942 0 : return;
1943 : }
1944 :
1945 : CSSVariableDeclarations* variables;
1946 74 : if (aIsImportant) {
1947 0 : if (mVariables) {
1948 0 : mVariables->Remove(aName);
1949 : }
1950 0 : if (!mImportantVariables) {
1951 0 : mImportantVariables = new CSSVariableDeclarations;
1952 : }
1953 0 : variables = mImportantVariables;
1954 : } else {
1955 74 : if (mImportantVariables) {
1956 0 : mImportantVariables->Remove(aName);
1957 : }
1958 74 : if (!mVariables) {
1959 31 : mVariables = new CSSVariableDeclarations;
1960 : }
1961 74 : variables = mVariables;
1962 : }
1963 :
1964 74 : switch (aType) {
1965 : case CSSVariableDeclarations::eTokenStream:
1966 74 : variables->PutTokenStream(aName, aValue);
1967 74 : break;
1968 :
1969 : case CSSVariableDeclarations::eInitial:
1970 0 : MOZ_ASSERT(aValue.IsEmpty());
1971 0 : variables->PutInitial(aName);
1972 0 : break;
1973 :
1974 : case CSSVariableDeclarations::eInherit:
1975 0 : MOZ_ASSERT(aValue.IsEmpty());
1976 0 : variables->PutInherit(aName);
1977 0 : break;
1978 :
1979 : case CSSVariableDeclarations::eUnset:
1980 0 : MOZ_ASSERT(aValue.IsEmpty());
1981 0 : variables->PutUnset(aName);
1982 0 : break;
1983 :
1984 : default:
1985 0 : MOZ_ASSERT(false, "unexpected aType value");
1986 : }
1987 :
1988 74 : uint32_t propertyIndex = index + eCSSProperty_COUNT;
1989 74 : mOrder.RemoveElement(propertyIndex);
1990 74 : mOrder.AppendElement(propertyIndex);
1991 : }
1992 :
1993 : void
1994 0 : Declaration::RemoveVariable(const nsAString& aName)
1995 : {
1996 0 : if (mVariables) {
1997 0 : mVariables->Remove(aName);
1998 : }
1999 0 : if (mImportantVariables) {
2000 0 : mImportantVariables->Remove(aName);
2001 : }
2002 0 : nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
2003 0 : if (index != nsTArray<nsString>::NoIndex) {
2004 0 : mOrder.RemoveElement(index + eCSSProperty_COUNT);
2005 : }
2006 0 : }
2007 :
2008 : bool
2009 0 : Declaration::GetVariableIsImportant(const nsAString& aName) const
2010 : {
2011 0 : return mImportantVariables && mImportantVariables->Has(aName);
2012 : }
2013 :
2014 : } // namespace css
2015 : } // namespace mozilla
|