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