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