1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsStyleUtil.h"
8 #include "nsStyleConsts.h"
9
10 #include "nsIContent.h"
11 #include "nsCSSProps.h"
12 #include "nsContentUtils.h"
13 #include "nsROCSSPrimitiveValue.h"
14 #include "nsStyleStruct.h"
15 #include "nsIContentPolicy.h"
16 #include "nsIContentSecurityPolicy.h"
17 #include "nsIURI.h"
18 #include "nsISupportsPrimitives.h"
19 #include "nsLayoutUtils.h"
20 #include "nsPrintfCString.h"
21 #include <cctype>
22
23 using namespace mozilla;
24
25 //------------------------------------------------------------------------------
26 // Font Algorithm Code
27 //------------------------------------------------------------------------------
28
29 // Compare two language strings
DashMatchCompare(const nsAString & aAttributeValue,const nsAString & aSelectorValue,const nsStringComparator & aComparator)30 bool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue,
31 const nsAString& aSelectorValue,
32 const nsStringComparator& aComparator) {
33 bool result;
34 uint32_t selectorLen = aSelectorValue.Length();
35 uint32_t attributeLen = aAttributeValue.Length();
36 if (selectorLen > attributeLen) {
37 result = false;
38 } else {
39 nsAString::const_iterator iter;
40 if (selectorLen != attributeLen &&
41 *aAttributeValue.BeginReading(iter).advance(selectorLen) !=
42 char16_t('-')) {
43 // to match, the aAttributeValue must have a dash after the end of
44 // the aSelectorValue's text (unless the aSelectorValue and the
45 // aAttributeValue have the same text)
46 result = false;
47 } else {
48 result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator);
49 }
50 }
51 return result;
52 }
53
ValueIncludes(const nsAString & aValueList,const nsAString & aValue,const nsStringComparator & aComparator)54 bool nsStyleUtil::ValueIncludes(const nsAString& aValueList,
55 const nsAString& aValue,
56 const nsStringComparator& aComparator) {
57 const char16_t *p = aValueList.BeginReading(),
58 *p_end = aValueList.EndReading();
59
60 while (p < p_end) {
61 // skip leading space
62 while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p)) ++p;
63
64 const char16_t* val_start = p;
65
66 // look for space or end
67 while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p)) ++p;
68
69 const char16_t* val_end = p;
70
71 if (val_start < val_end &&
72 aValue.Equals(Substring(val_start, val_end), aComparator))
73 return true;
74
75 ++p; // we know the next character is not whitespace
76 }
77 return false;
78 }
79
AppendEscapedCSSString(const nsAString & aString,nsAString & aReturn,char16_t quoteChar)80 void nsStyleUtil::AppendEscapedCSSString(const nsAString& aString,
81 nsAString& aReturn,
82 char16_t quoteChar) {
83 NS_PRECONDITION(quoteChar == '\'' || quoteChar == '"',
84 "CSS strings must be quoted with ' or \"");
85 aReturn.Append(quoteChar);
86
87 const char16_t* in = aString.BeginReading();
88 const char16_t* const end = aString.EndReading();
89 for (; in != end; in++) {
90 if (*in < 0x20 || *in == 0x7F) {
91 // Escape U+0000 through U+001F and U+007F numerically.
92 aReturn.AppendPrintf("\\%x ", *in);
93 } else {
94 if (*in == '"' || *in == '\'' || *in == '\\') {
95 // Escape backslash and quote characters symbolically.
96 // It's not technically necessary to escape the quote
97 // character that isn't being used to delimit the string,
98 // but we do it anyway because that makes testing simpler.
99 aReturn.Append(char16_t('\\'));
100 }
101 aReturn.Append(*in);
102 }
103 }
104
105 aReturn.Append(quoteChar);
106 }
107
AppendEscapedCSSIdent(const nsAString & aIdent,nsAString & aReturn)108 /* static */ void nsStyleUtil::AppendEscapedCSSIdent(const nsAString& aIdent,
109 nsAString& aReturn) {
110 // The relevant parts of the CSS grammar are:
111 // ident ([-]?{nmstart}|[-][-]){nmchar}*
112 // nmstart [_a-z]|{nonascii}|{escape}
113 // nmchar [_a-z0-9-]|{nonascii}|{escape}
114 // nonascii [^\0-\177]
115 // escape {unicode}|\\[^\n\r\f0-9a-f]
116 // unicode \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
117 // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
118 // modified for idents by
119 // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
120 // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
121
122 const char16_t* in = aIdent.BeginReading();
123 const char16_t* const end = aIdent.EndReading();
124
125 if (in == end) return;
126
127 // A leading dash does not need to be escaped as long as it is not the
128 // *only* character in the identifier.
129 if (*in == '-') {
130 if (in + 1 == end) {
131 aReturn.Append(char16_t('\\'));
132 aReturn.Append(char16_t('-'));
133 return;
134 }
135
136 aReturn.Append(char16_t('-'));
137 ++in;
138 }
139
140 // Escape a digit at the start (including after a dash),
141 // numerically. If we didn't escape it numerically, it would get
142 // interpreted as a numeric escape for the wrong character.
143 if (in != end && ('0' <= *in && *in <= '9')) {
144 aReturn.AppendPrintf("\\%x ", *in);
145 ++in;
146 }
147
148 for (; in != end; ++in) {
149 char16_t ch = *in;
150 if (ch == 0x00) {
151 aReturn.Append(char16_t(0xFFFD));
152 } else if (ch < 0x20 || 0x7F == ch) {
153 // Escape U+0000 through U+001F and U+007F numerically.
154 aReturn.AppendPrintf("\\%x ", *in);
155 } else {
156 // Escape ASCII non-identifier printables as a backslash plus
157 // the character.
158 if (ch < 0x7F && ch != '_' && ch != '-' && (ch < '0' || '9' < ch) &&
159 (ch < 'A' || 'Z' < ch) && (ch < 'a' || 'z' < ch)) {
160 aReturn.Append(char16_t('\\'));
161 }
162 aReturn.Append(ch);
163 }
164 }
165 }
166
167 // unquoted family names must be a sequence of idents
168 // so escape any parts that require escaping
AppendUnquotedFamilyName(const nsAString & aFamilyName,nsAString & aResult)169 static void AppendUnquotedFamilyName(const nsAString& aFamilyName,
170 nsAString& aResult) {
171 const char16_t *p, *p_end;
172 aFamilyName.BeginReading(p);
173 aFamilyName.EndReading(p_end);
174
175 bool moreThanOne = false;
176 while (p < p_end) {
177 const char16_t* identStart = p;
178 while (++p != p_end && *p != ' ') /* nothing */
179 ;
180
181 nsDependentSubstring ident(identStart, p);
182 if (!ident.IsEmpty()) {
183 if (moreThanOne) {
184 aResult.Append(' ');
185 }
186 nsStyleUtil::AppendEscapedCSSIdent(ident, aResult);
187 moreThanOne = true;
188 }
189
190 ++p;
191 }
192 }
193
AppendEscapedCSSFontFamilyList(const nsTArray<mozilla::FontFamilyName> & aNames,nsAString & aResult)194 /* static */ void nsStyleUtil::AppendEscapedCSSFontFamilyList(
195 const nsTArray<mozilla::FontFamilyName>& aNames, nsAString& aResult) {
196 size_t i, len = aNames.Length();
197 for (i = 0; i < len; i++) {
198 if (i != 0) {
199 aResult.AppendLiteral(", ");
200 }
201 const FontFamilyName& name = aNames[i];
202 switch (name.mType) {
203 case eFamily_named:
204 AppendUnquotedFamilyName(name.mName, aResult);
205 break;
206 case eFamily_named_quoted:
207 AppendEscapedCSSString(name.mName, aResult);
208 break;
209 default:
210 name.AppendToString(aResult);
211 }
212 }
213 }
214
AppendEscapedCSSFontFamilyList(const mozilla::FontFamilyList & aFamilyList,nsAString & aResult)215 /* static */ void nsStyleUtil::AppendEscapedCSSFontFamilyList(
216 const mozilla::FontFamilyList& aFamilyList, nsAString& aResult) {
217 if (aFamilyList.IsEmpty()) {
218 FontFamilyType defaultGeneric = aFamilyList.GetDefaultFontType();
219 // If the font list is empty, then serialize the default generic.
220 // See also: gfxFontGroup::BuildFontList()
221 if (defaultGeneric != eFamily_none) {
222 FontFamilyName(defaultGeneric).AppendToString(aResult);
223 } else {
224 NS_NOTREACHED("No fonts to serialize");
225 }
226 return;
227 }
228
229 AppendEscapedCSSFontFamilyList(aFamilyList.GetFontlist().get(), aResult);
230 }
231
AppendBitmaskCSSValue(nsCSSPropertyID aProperty,int32_t aMaskedValue,int32_t aFirstMask,int32_t aLastMask,nsAString & aResult)232 /* static */ void nsStyleUtil::AppendBitmaskCSSValue(nsCSSPropertyID aProperty,
233 int32_t aMaskedValue,
234 int32_t aFirstMask,
235 int32_t aLastMask,
236 nsAString& aResult) {
237 for (int32_t mask = aFirstMask; mask <= aLastMask; mask <<= 1) {
238 if (mask & aMaskedValue) {
239 AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, mask),
240 aResult);
241 aMaskedValue &= ~mask;
242 if (aMaskedValue) { // more left
243 aResult.Append(char16_t(' '));
244 }
245 }
246 }
247 MOZ_ASSERT(aMaskedValue == 0, "unexpected bit remaining in bitfield");
248 }
249
AppendAngleValue(const nsStyleCoord & aAngle,nsAString & aResult)250 /* static */ void nsStyleUtil::AppendAngleValue(const nsStyleCoord& aAngle,
251 nsAString& aResult) {
252 MOZ_ASSERT(aAngle.IsAngleValue(), "Should have angle value");
253
254 // Append number.
255 AppendCSSNumber(aAngle.GetAngleValue(), aResult);
256
257 // Append unit.
258 switch (aAngle.GetUnit()) {
259 case eStyleUnit_Degree:
260 aResult.AppendLiteral("deg");
261 break;
262 case eStyleUnit_Grad:
263 aResult.AppendLiteral("grad");
264 break;
265 case eStyleUnit_Radian:
266 aResult.AppendLiteral("rad");
267 break;
268 case eStyleUnit_Turn:
269 aResult.AppendLiteral("turn");
270 break;
271 default:
272 NS_NOTREACHED("unrecognized angle unit");
273 }
274 }
275
AppendPaintOrderValue(uint8_t aValue,nsAString & aResult)276 /* static */ void nsStyleUtil::AppendPaintOrderValue(uint8_t aValue,
277 nsAString& aResult) {
278 static_assert(
279 NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8,
280 "SVGStyleStruct::mPaintOrder and local variables not big enough");
281
282 if (aValue == NS_STYLE_PAINT_ORDER_NORMAL) {
283 aResult.AppendLiteral("normal");
284 return;
285 }
286
287 // Append the minimal value necessary for the given paint order.
288 static_assert(NS_STYLE_PAINT_ORDER_LAST_VALUE == 3,
289 "paint-order values added; check serialization");
290
291 // The following relies on the default order being the order of the
292 // constant values.
293
294 const uint8_t MASK = (1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1;
295
296 uint32_t lastPositionToSerialize = 0;
297 for (uint32_t position = NS_STYLE_PAINT_ORDER_LAST_VALUE - 1; position > 0;
298 position--) {
299 uint8_t component =
300 (aValue >> (position * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK;
301 uint8_t earlierComponent =
302 (aValue >> ((position - 1) * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK;
303 if (component < earlierComponent) {
304 lastPositionToSerialize = position - 1;
305 break;
306 }
307 }
308
309 for (uint32_t position = 0; position <= lastPositionToSerialize; position++) {
310 if (position > 0) {
311 aResult.Append(' ');
312 }
313 uint8_t component = aValue & MASK;
314 switch (component) {
315 case NS_STYLE_PAINT_ORDER_FILL:
316 aResult.AppendLiteral("fill");
317 break;
318
319 case NS_STYLE_PAINT_ORDER_STROKE:
320 aResult.AppendLiteral("stroke");
321 break;
322
323 case NS_STYLE_PAINT_ORDER_MARKERS:
324 aResult.AppendLiteral("markers");
325 break;
326
327 default:
328 NS_NOTREACHED("unexpected paint-order component value");
329 }
330 aValue >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
331 }
332 }
333
AppendFontTagAsString(uint32_t aTag,nsAString & aResult)334 /* static */ void nsStyleUtil::AppendFontTagAsString(uint32_t aTag,
335 nsAString& aResult) {
336 // A font tag (for feature/variation settings) is a 4-char code interpreted
337 // as a bigendian 32-bit value and stored/processed as a uint32_t.
338 // To serialize it, we put the four bytes (which are all guaranteed to be
339 // printable ASCII values) into a string, starting from the high byte of the
340 // value, then append that to the result with CSS escaping and quotes.
341 nsAutoString tagStr;
342 for (int shiftAmount = 24; shiftAmount >= 0; shiftAmount -= 8) {
343 char c = (aTag >> shiftAmount) & 0xff;
344 MOZ_ASSERT(isascii(c) && isprint(c),
345 "parser should have restricted tag to printable ASCII chars");
346 tagStr.Append(c);
347 }
348 AppendEscapedCSSString(tagStr, aResult);
349 }
350
AppendFontFeatureSettings(const nsTArray<gfxFontFeature> & aFeatures,nsAString & aResult)351 /* static */ void nsStyleUtil::AppendFontFeatureSettings(
352 const nsTArray<gfxFontFeature>& aFeatures, nsAString& aResult) {
353 for (uint32_t i = 0, numFeat = aFeatures.Length(); i < numFeat; i++) {
354 const gfxFontFeature& feat = aFeatures[i];
355
356 if (i != 0) {
357 aResult.AppendLiteral(", ");
358 }
359
360 AppendFontTagAsString(feat.mTag, aResult);
361
362 // omit value if it's 1, implied by default
363 if (feat.mValue != 1) {
364 aResult.Append(' ');
365 aResult.AppendInt(feat.mValue);
366 }
367 }
368 }
369
AppendFontFeatureSettings(const nsCSSValue & aSrc,nsAString & aResult)370 /* static */ void nsStyleUtil::AppendFontFeatureSettings(const nsCSSValue& aSrc,
371 nsAString& aResult) {
372 nsCSSUnit unit = aSrc.GetUnit();
373
374 if (unit == eCSSUnit_Normal) {
375 aResult.AppendLiteral("normal");
376 return;
377 }
378
379 NS_PRECONDITION(unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep,
380 "improper value unit for font-feature-settings:");
381
382 nsTArray<gfxFontFeature> featureSettings;
383 nsLayoutUtils::ComputeFontFeatures(aSrc.GetPairListValue(), featureSettings);
384 AppendFontFeatureSettings(featureSettings, aResult);
385 }
386
AppendFontVariationSettings(const nsTArray<gfxFontVariation> & aVariations,nsAString & aResult)387 /* static */ void nsStyleUtil::AppendFontVariationSettings(
388 const nsTArray<gfxFontVariation>& aVariations, nsAString& aResult) {
389 for (uint32_t i = 0, numVars = aVariations.Length(); i < numVars; i++) {
390 const gfxFontVariation& var = aVariations[i];
391
392 if (i != 0) {
393 aResult.AppendLiteral(", ");
394 }
395
396 // output tag
397 AppendFontTagAsString(var.mTag, aResult);
398
399 // output value
400 aResult.Append(' ');
401 aResult.AppendFloat(var.mValue);
402 }
403 }
404
AppendFontVariationSettings(const nsCSSValue & aSrc,nsAString & aResult)405 /* static */ void nsStyleUtil::AppendFontVariationSettings(
406 const nsCSSValue& aSrc, nsAString& aResult) {
407 nsCSSUnit unit = aSrc.GetUnit();
408
409 if (unit == eCSSUnit_Normal) {
410 aResult.AppendLiteral("normal");
411 return;
412 }
413
414 NS_PRECONDITION(unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep,
415 "improper value unit for font-variation-settings:");
416
417 nsTArray<gfxFontVariation> variationSettings;
418 nsLayoutUtils::ComputeFontVariations(aSrc.GetPairListValue(),
419 variationSettings);
420 AppendFontVariationSettings(variationSettings, aResult);
421 }
422
GetFunctionalAlternatesName(int32_t aFeature,nsAString & aFeatureName)423 /* static */ void nsStyleUtil::GetFunctionalAlternatesName(
424 int32_t aFeature, nsAString& aFeatureName) {
425 aFeatureName.Truncate();
426 nsCSSKeyword key = nsCSSProps::ValueToKeywordEnum(
427 aFeature, nsCSSProps::kFontVariantAlternatesFuncsKTable);
428
429 NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "bad alternate feature type");
430 AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(key), aFeatureName);
431 }
432
SerializeFunctionalAlternates(const nsTArray<gfxAlternateValue> & aAlternates,nsAString & aResult)433 /* static */ void nsStyleUtil::SerializeFunctionalAlternates(
434 const nsTArray<gfxAlternateValue>& aAlternates, nsAString& aResult) {
435 nsAutoString funcName, funcParams;
436 uint32_t numValues = aAlternates.Length();
437
438 uint32_t feature = 0;
439 for (uint32_t i = 0; i < numValues; i++) {
440 const gfxAlternateValue& v = aAlternates.ElementAt(i);
441 if (feature != v.alternate) {
442 feature = v.alternate;
443 if (!funcName.IsEmpty() && !funcParams.IsEmpty()) {
444 if (!aResult.IsEmpty()) {
445 aResult.Append(char16_t(' '));
446 }
447
448 // append the previous functional value
449 aResult.Append(funcName);
450 aResult.Append(char16_t('('));
451 aResult.Append(funcParams);
452 aResult.Append(char16_t(')'));
453 }
454
455 // function name
456 GetFunctionalAlternatesName(v.alternate, funcName);
457 NS_ASSERTION(!funcName.IsEmpty(), "unknown property value name");
458
459 // function params
460 funcParams.Truncate();
461 AppendEscapedCSSIdent(v.value, funcParams);
462 } else {
463 if (!funcParams.IsEmpty()) {
464 funcParams.AppendLiteral(", ");
465 }
466 AppendEscapedCSSIdent(v.value, funcParams);
467 }
468 }
469
470 // append the previous functional value
471 if (!funcName.IsEmpty() && !funcParams.IsEmpty()) {
472 if (!aResult.IsEmpty()) {
473 aResult.Append(char16_t(' '));
474 }
475
476 aResult.Append(funcName);
477 aResult.Append(char16_t('('));
478 aResult.Append(funcParams);
479 aResult.Append(char16_t(')'));
480 }
481 }
482
ComputeFunctionalAlternates(const nsCSSValueList * aList,nsTArray<gfxAlternateValue> & aAlternateValues)483 /* static */ void nsStyleUtil::ComputeFunctionalAlternates(
484 const nsCSSValueList* aList,
485 nsTArray<gfxAlternateValue>& aAlternateValues) {
486 gfxAlternateValue v;
487
488 aAlternateValues.Clear();
489 for (const nsCSSValueList* curr = aList; curr != nullptr;
490 curr = curr->mNext) {
491 // list contains function units
492 if (curr->mValue.GetUnit() != eCSSUnit_Function) {
493 continue;
494 }
495
496 // element 0 is the propval in ident form
497 const nsCSSValue::Array* func = curr->mValue.GetArrayValue();
498
499 // lookup propval
500 nsCSSKeyword key = func->Item(0).GetKeywordValue();
501 NS_ASSERTION(key != eCSSKeyword_UNKNOWN,
502 "unknown alternate property value");
503
504 int32_t alternate;
505 if (!nsCSSProps::FindKeyword(
506 key, nsCSSProps::kFontVariantAlternatesFuncsKTable, alternate)) {
507 NS_NOTREACHED("keyword not a font-variant-alternates value");
508 continue;
509 }
510 v.alternate = alternate;
511
512 // other elements are the idents associated with the propval
513 // append one alternate value for each one
514 uint32_t numElems = func->Count();
515 for (uint32_t i = 1; i < numElems; i++) {
516 const nsCSSValue& value = func->Item(i);
517 NS_ASSERTION(value.GetUnit() == eCSSUnit_Ident,
518 "weird unit found in variant alternate");
519 if (value.GetUnit() != eCSSUnit_Ident) {
520 continue;
521 }
522 value.GetStringValue(v.value);
523 aAlternateValues.AppendElement(v);
524 }
525 }
526 }
527
AppendSerializedUnicodePoint(uint32_t aCode,nsACString & aBuf)528 static void AppendSerializedUnicodePoint(uint32_t aCode, nsACString& aBuf) {
529 aBuf.Append(nsPrintfCString("%0X", aCode));
530 }
531
532 // A unicode-range: descriptor is represented as an array of integers,
533 // to be interpreted as a sequence of pairs: min max min max ...
534 // It is in source order. (Possibly it should be sorted and overlaps
535 // consolidated, but right now we don't do that.)
AppendUnicodeRange(const nsCSSValue & aValue,nsAString & aResult)536 /* static */ void nsStyleUtil::AppendUnicodeRange(const nsCSSValue& aValue,
537 nsAString& aResult) {
538 NS_PRECONDITION(
539 aValue.GetUnit() == eCSSUnit_Null || aValue.GetUnit() == eCSSUnit_Array,
540 "improper value unit for unicode-range:");
541 aResult.Truncate();
542 if (aValue.GetUnit() != eCSSUnit_Array) return;
543
544 nsCSSValue::Array const& sources = *aValue.GetArrayValue();
545 nsAutoCString buf;
546
547 MOZ_ASSERT(sources.Count() % 2 == 0,
548 "odd number of entries in a unicode-range: array");
549
550 for (uint32_t i = 0; i < sources.Count(); i += 2) {
551 uint32_t min = sources[i].GetIntValue();
552 uint32_t max = sources[i + 1].GetIntValue();
553
554 // We don't try to replicate the U+XX?? notation.
555 buf.AppendLiteral("U+");
556 AppendSerializedUnicodePoint(min, buf);
557
558 if (min != max) {
559 buf.Append('-');
560 AppendSerializedUnicodePoint(max, buf);
561 }
562 buf.AppendLiteral(", ");
563 }
564 buf.Truncate(buf.Length() - 2); // remove the last comma-space
565 CopyASCIItoUTF16(buf, aResult);
566 }
567
AppendSerializedFontSrc(const nsCSSValue & aValue,nsAString & aResult)568 /* static */ void nsStyleUtil::AppendSerializedFontSrc(const nsCSSValue& aValue,
569 nsAString& aResult) {
570 // A src: descriptor is represented as an array value; each entry in
571 // the array can be eCSSUnit_URL, eCSSUnit_Local_Font, or
572 // eCSSUnit_Font_Format. Blocks of eCSSUnit_Font_Format may appear
573 // only after one of the first two. (css3-fonts only contemplates
574 // annotating URLs with formats, but we handle the general case.)
575
576 NS_PRECONDITION(aValue.GetUnit() == eCSSUnit_Array,
577 "improper value unit for src:");
578
579 const nsCSSValue::Array& sources = *aValue.GetArrayValue();
580 size_t i = 0;
581
582 while (i < sources.Count()) {
583 nsAutoString formats;
584
585 if (sources[i].GetUnit() == eCSSUnit_URL) {
586 aResult.AppendLiteral("url(");
587 nsDependentString url(sources[i].GetOriginalURLValue());
588 nsStyleUtil::AppendEscapedCSSString(url, aResult);
589 aResult.Append(')');
590 } else if (sources[i].GetUnit() == eCSSUnit_Local_Font) {
591 aResult.AppendLiteral("local(");
592 nsDependentString local(sources[i].GetStringBufferValue());
593 nsStyleUtil::AppendEscapedCSSString(local, aResult);
594 aResult.Append(')');
595 } else {
596 NS_NOTREACHED("entry in src: descriptor with improper unit");
597 i++;
598 continue;
599 }
600
601 i++;
602 formats.Truncate();
603 while (i < sources.Count() &&
604 sources[i].GetUnit() == eCSSUnit_Font_Format) {
605 formats.Append('"');
606 formats.Append(sources[i].GetStringBufferValue());
607 formats.AppendLiteral("\", ");
608 i++;
609 }
610 if (formats.Length() > 0) {
611 formats.Truncate(formats.Length() - 2); // remove the last comma
612 aResult.AppendLiteral(" format(");
613 aResult.Append(formats);
614 aResult.Append(')');
615 }
616 aResult.AppendLiteral(", ");
617 }
618 aResult.Truncate(aResult.Length() - 2); // remove the last comma-space
619 }
620
AppendStepsTimingFunction(nsTimingFunction::Type aType,uint32_t aSteps,nsAString & aResult)621 /* static */ void nsStyleUtil::AppendStepsTimingFunction(
622 nsTimingFunction::Type aType, uint32_t aSteps, nsAString& aResult) {
623 MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
624 aType == nsTimingFunction::Type::StepEnd);
625
626 aResult.AppendLiteral("steps(");
627 aResult.AppendInt(aSteps);
628 if (aType == nsTimingFunction::Type::StepStart) {
629 aResult.AppendLiteral(", start)");
630 } else {
631 aResult.AppendLiteral(")");
632 }
633 }
634
AppendFramesTimingFunction(uint32_t aFrames,nsAString & aResult)635 /* static */ void nsStyleUtil::AppendFramesTimingFunction(uint32_t aFrames,
636 nsAString& aResult) {
637 aResult.AppendLiteral("frames(");
638 aResult.AppendInt(aFrames);
639 aResult.AppendLiteral(")");
640 }
641
AppendCubicBezierTimingFunction(float aX1,float aY1,float aX2,float aY2,nsAString & aResult)642 /* static */ void nsStyleUtil::AppendCubicBezierTimingFunction(
643 float aX1, float aY1, float aX2, float aY2, nsAString& aResult) {
644 // set the value from the cubic-bezier control points
645 // (We could try to regenerate the keywords if we want.)
646 aResult.AppendLiteral("cubic-bezier(");
647 aResult.AppendFloat(aX1);
648 aResult.AppendLiteral(", ");
649 aResult.AppendFloat(aY1);
650 aResult.AppendLiteral(", ");
651 aResult.AppendFloat(aX2);
652 aResult.AppendLiteral(", ");
653 aResult.AppendFloat(aY2);
654 aResult.Append(')');
655 }
656
AppendCubicBezierKeywordTimingFunction(nsTimingFunction::Type aType,nsAString & aResult)657 /* static */ void nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
658 nsTimingFunction::Type aType, nsAString& aResult) {
659 switch (aType) {
660 case nsTimingFunction::Type::Ease:
661 case nsTimingFunction::Type::Linear:
662 case nsTimingFunction::Type::EaseIn:
663 case nsTimingFunction::Type::EaseOut:
664 case nsTimingFunction::Type::EaseInOut: {
665 nsCSSKeyword keyword = nsCSSProps::ValueToKeywordEnum(
666 static_cast<int32_t>(aType),
667 nsCSSProps::kTransitionTimingFunctionKTable);
668 AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(keyword), aResult);
669 break;
670 }
671 default:
672 MOZ_ASSERT_UNREACHABLE("unexpected aType");
673 break;
674 }
675 }
676
ColorComponentToFloat(uint8_t aAlpha)677 /* static */ float nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha) {
678 // Alpha values are expressed as decimals, so we should convert
679 // back, using as few decimal places as possible for
680 // round-tripping.
681 // First try two decimal places:
682 float rounded = NS_roundf(float(aAlpha) * 100.0f / 255.0f) / 100.0f;
683 if (FloatToColorComponent(rounded) != aAlpha) {
684 // Use three decimal places.
685 rounded = NS_roundf(float(aAlpha) * 1000.0f / 255.0f) / 1000.0f;
686 }
687 return rounded;
688 }
689
IsSignificantChild(nsIContent * aChild,bool aWhitespaceIsSignificant)690 /* static */ bool nsStyleUtil::IsSignificantChild(
691 nsIContent* aChild, bool aWhitespaceIsSignificant) {
692 bool isText = aChild->IsNodeOfType(nsINode::eTEXT);
693
694 if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
695 !aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
696 return true;
697 }
698
699 return isText && aChild->TextLength() != 0 &&
700 (aWhitespaceIsSignificant || !aChild->TextIsOnlyWhitespace());
701 }
702
ThreadSafeIsSignificantChild(const nsIContent * aChild,bool aWhitespaceIsSignificant)703 /* static */ bool nsStyleUtil::ThreadSafeIsSignificantChild(
704 const nsIContent* aChild, bool aWhitespaceIsSignificant) {
705 bool isText = aChild->IsNodeOfType(nsINode::eTEXT);
706
707 if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
708 !aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
709 return true;
710 }
711
712 return isText && aChild->TextLength() != 0 &&
713 (aWhitespaceIsSignificant ||
714 !aChild->ThreadSafeTextIsOnlyWhitespace());
715 }
716
717 // For a replaced element whose concrete object size is no larger than the
718 // element's content-box, this method checks whether the given
719 // "object-position" coordinate might cause overflow in its dimension.
ObjectPositionCoordMightCauseOverflow(const Position::Coord & aCoord)720 static bool ObjectPositionCoordMightCauseOverflow(
721 const Position::Coord& aCoord) {
722 // Any nonzero length in "object-position" can push us to overflow
723 // (particularly if our concrete object size is exactly the same size as the
724 // replaced element's content-box).
725 if (aCoord.mLength != 0) {
726 return true;
727 }
728
729 // Percentages are interpreted as a fraction of the extra space. So,
730 // percentages in the 0-100% range are safe, but values outside of that
731 // range could cause overflow.
732 if (aCoord.mHasPercent &&
733 (aCoord.mPercent < 0.0f || aCoord.mPercent > 1.0f)) {
734 return true;
735 }
736 return false;
737 }
738
ObjectPropsMightCauseOverflow(const nsStylePosition * aStylePos)739 /* static */ bool nsStyleUtil::ObjectPropsMightCauseOverflow(
740 const nsStylePosition* aStylePos) {
741 auto objectFit = aStylePos->mObjectFit;
742
743 // "object-fit: cover" & "object-fit: none" can give us a render rect that's
744 // larger than our container element's content-box.
745 if (objectFit == NS_STYLE_OBJECT_FIT_COVER ||
746 objectFit == NS_STYLE_OBJECT_FIT_NONE) {
747 return true;
748 }
749 // (All other object-fit values produce a concrete object size that's no
750 // larger than the constraint region.)
751
752 // Check each of our "object-position" coords to see if it could cause
753 // overflow in its dimension:
754 const Position& objectPosistion = aStylePos->mObjectPosition;
755 if (ObjectPositionCoordMightCauseOverflow(objectPosistion.mXPosition) ||
756 ObjectPositionCoordMightCauseOverflow(objectPosistion.mYPosition)) {
757 return true;
758 }
759
760 return false;
761 }
762
CSPAllowsInlineStyle(Element * aElement,nsIPrincipal * aPrincipal,nsIPrincipal * aTriggeringPrincipal,nsIURI * aSourceURI,uint32_t aLineNumber,const nsAString & aStyleText,nsresult * aRv)763 /* static */ bool nsStyleUtil::CSPAllowsInlineStyle(
764 Element* aElement, nsIPrincipal* aPrincipal,
765 nsIPrincipal* aTriggeringPrincipal, nsIURI* aSourceURI,
766 uint32_t aLineNumber, const nsAString& aStyleText, nsresult* aRv) {
767 nsresult rv;
768
769 if (aRv) {
770 *aRv = NS_OK;
771 }
772
773 MOZ_ASSERT(!aElement || aElement->NodeInfo()->NameAtom() == nsGkAtoms::style,
774 "aElement passed to CSPAllowsInlineStyle "
775 "for an element that is not <style>");
776
777 nsIPrincipal* principal = aPrincipal;
778 if (aTriggeringPrincipal &&
779 BasePrincipal::Cast(aTriggeringPrincipal)->OverridesCSP(aPrincipal)) {
780 principal = aTriggeringPrincipal;
781 }
782
783 nsCOMPtr<nsIContentSecurityPolicy> csp;
784 rv = principal->GetCsp(getter_AddRefs(csp));
785
786 if (NS_FAILED(rv)) {
787 if (aRv) *aRv = rv;
788 return false;
789 }
790
791 if (!csp) {
792 // No CSP --> the style is allowed
793 return true;
794 }
795
796 // query the nonce
797 nsAutoString nonce;
798 if (aElement) {
799 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce);
800 }
801
802 nsCOMPtr<nsISupportsString> styleText(
803 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
804 if (styleText) {
805 styleText->SetData(aStyleText);
806 }
807
808 bool allowInlineStyle = true;
809 rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_STYLESHEET, nonce,
810 false, // aParserCreated only applies to scripts
811 styleText, aLineNumber, &allowInlineStyle);
812 NS_ENSURE_SUCCESS(rv, false);
813
814 return allowInlineStyle;
815 }
816