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