Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/style/nsStyleUtil.cpp
Line
Count
Source (jump to first uncovered line)
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 "mozilla/FontPropertyTypes.h"
11
#include "nsIContent.h"
12
#include "nsCSSProps.h"
13
#include "nsContentUtils.h"
14
#include "nsROCSSPrimitiveValue.h"
15
#include "nsStyleStruct.h"
16
#include "nsIContentPolicy.h"
17
#include "nsIContentSecurityPolicy.h"
18
#include "nsIURI.h"
19
#include "nsISupportsPrimitives.h"
20
#include "nsLayoutUtils.h"
21
#include "nsPrintfCString.h"
22
#include <cctype>
23
24
using namespace mozilla;
25
26
//------------------------------------------------------------------------------
27
// Font Algorithm Code
28
//------------------------------------------------------------------------------
29
30
// Compare two language strings
31
bool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue,
32
                                     const nsAString& aSelectorValue,
33
                                     const nsStringComparator& aComparator)
34
0
{
35
0
  bool result;
36
0
  uint32_t selectorLen = aSelectorValue.Length();
37
0
  uint32_t attributeLen = aAttributeValue.Length();
38
0
  if (selectorLen > attributeLen) {
39
0
    result = false;
40
0
  }
41
0
  else {
42
0
    nsAString::const_iterator iter;
43
0
    if (selectorLen != attributeLen &&
44
0
        *aAttributeValue.BeginReading(iter).advance(selectorLen) !=
45
0
            char16_t('-')) {
46
0
      // to match, the aAttributeValue must have a dash after the end of
47
0
      // the aSelectorValue's text (unless the aSelectorValue and the
48
0
      // aAttributeValue have the same text)
49
0
      result = false;
50
0
    }
51
0
    else {
52
0
      result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator);
53
0
    }
54
0
  }
55
0
  return result;
56
0
}
57
58
bool
59
nsStyleUtil::ValueIncludes(const nsAString& aValueList,
60
                           const nsAString& aValue,
61
                           const nsStringComparator& aComparator)
62
0
{
63
0
  const char16_t *p = aValueList.BeginReading(),
64
0
              *p_end = aValueList.EndReading();
65
0
66
0
  while (p < p_end) {
67
0
    // skip leading space
68
0
    while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p))
69
0
      ++p;
70
0
71
0
    const char16_t *val_start = p;
72
0
73
0
    // look for space or end
74
0
    while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p))
75
0
      ++p;
76
0
77
0
    const char16_t *val_end = p;
78
0
79
0
    if (val_start < val_end &&
80
0
        aValue.Equals(Substring(val_start, val_end), aComparator))
81
0
      return true;
82
0
83
0
    ++p; // we know the next character is not whitespace
84
0
  }
85
0
  return false;
86
0
}
87
88
void nsStyleUtil::AppendEscapedCSSString(const nsAString& aString,
89
                                         nsAString& aReturn,
90
                                         char16_t quoteChar)
91
0
{
92
0
  MOZ_ASSERT(quoteChar == '\'' || quoteChar == '"',
93
0
             "CSS strings must be quoted with ' or \"");
94
0
95
0
  aReturn.Append(quoteChar);
96
0
97
0
  const char16_t* in = aString.BeginReading();
98
0
  const char16_t* const end = aString.EndReading();
99
0
  for (; in != end; in++) {
100
0
    if (*in < 0x20 || *in == 0x7F) {
101
0
      // Escape U+0000 through U+001F and U+007F numerically.
102
0
      aReturn.AppendPrintf("\\%x ", *in);
103
0
    } else {
104
0
      if (*in == '"' || *in == '\'' || *in == '\\') {
105
0
        // Escape backslash and quote characters symbolically.
106
0
        // It's not technically necessary to escape the quote
107
0
        // character that isn't being used to delimit the string,
108
0
        // but we do it anyway because that makes testing simpler.
109
0
        aReturn.Append(char16_t('\\'));
110
0
      }
111
0
      aReturn.Append(*in);
112
0
    }
113
0
  }
114
0
115
0
  aReturn.Append(quoteChar);
116
0
}
117
118
/* static */ void
119
nsStyleUtil::AppendEscapedCSSIdent(const nsAString& aIdent, nsAString& aReturn)
120
0
{
121
0
  // The relevant parts of the CSS grammar are:
122
0
  //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
123
0
  //   nmstart  [_a-z]|{nonascii}|{escape}
124
0
  //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
125
0
  //   nonascii [^\0-\177]
126
0
  //   escape   {unicode}|\\[^\n\r\f0-9a-f]
127
0
  //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
128
0
  // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
129
0
  // modified for idents by
130
0
  // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
131
0
  // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
132
0
133
0
  const char16_t* in = aIdent.BeginReading();
134
0
  const char16_t* const end = aIdent.EndReading();
135
0
136
0
  if (in == end)
137
0
    return;
138
0
139
0
  // A leading dash does not need to be escaped as long as it is not the
140
0
  // *only* character in the identifier.
141
0
  if (*in == '-') {
142
0
    if (in + 1 == end) {
143
0
      aReturn.Append(char16_t('\\'));
144
0
      aReturn.Append(char16_t('-'));
145
0
      return;
146
0
    }
147
0
148
0
    aReturn.Append(char16_t('-'));
149
0
    ++in;
150
0
  }
151
0
152
0
  // Escape a digit at the start (including after a dash),
153
0
  // numerically.  If we didn't escape it numerically, it would get
154
0
  // interpreted as a numeric escape for the wrong character.
155
0
  if (in != end && ('0' <= *in && *in <= '9')) {
156
0
    aReturn.AppendPrintf("\\%x ", *in);
157
0
    ++in;
158
0
  }
159
0
160
0
  for (; in != end; ++in) {
161
0
    char16_t ch = *in;
162
0
    if (ch == 0x00) {
163
0
      aReturn.Append(char16_t(0xFFFD));
164
0
    } else if (ch < 0x20 || 0x7F == ch) {
165
0
      // Escape U+0000 through U+001F and U+007F numerically.
166
0
      aReturn.AppendPrintf("\\%x ", *in);
167
0
    } else {
168
0
      // Escape ASCII non-identifier printables as a backslash plus
169
0
      // the character.
170
0
      if (ch < 0x7F &&
171
0
          ch != '_' && ch != '-' &&
172
0
          (ch < '0' || '9' < ch) &&
173
0
          (ch < 'A' || 'Z' < ch) &&
174
0
          (ch < 'a' || 'z' < ch)) {
175
0
        aReturn.Append(char16_t('\\'));
176
0
      }
177
0
      aReturn.Append(ch);
178
0
    }
179
0
  }
180
0
}
181
182
/* static */ void
183
nsStyleUtil::AppendBitmaskCSSValue(const nsCSSKTableEntry aTable[],
184
                                   int32_t aMaskedValue,
185
                                   int32_t aFirstMask,
186
                                   int32_t aLastMask,
187
                                   nsAString& aResult)
188
0
{
189
0
  for (int32_t mask = aFirstMask; mask <= aLastMask; mask <<= 1) {
190
0
    if (mask & aMaskedValue) {
191
0
      AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(mask, aTable), aResult);
192
0
      aMaskedValue &= ~mask;
193
0
      if (aMaskedValue) { // more left
194
0
        aResult.Append(char16_t(' '));
195
0
      }
196
0
    }
197
0
  }
198
0
  MOZ_ASSERT(aMaskedValue == 0, "unexpected bit remaining in bitfield");
199
0
}
200
201
/* static */ void
202
nsStyleUtil::AppendAngleValue(const nsStyleCoord& aAngle, nsAString& aResult)
203
0
{
204
0
  MOZ_ASSERT(aAngle.IsAngleValue(), "Should have angle value");
205
0
206
0
  // Append number.
207
0
  AppendCSSNumber(aAngle.GetAngleValue(), aResult);
208
0
209
0
  // Append unit.
210
0
  switch (aAngle.GetUnit()) {
211
0
    case eStyleUnit_Degree: aResult.AppendLiteral("deg");  break;
212
0
    case eStyleUnit_Grad:   aResult.AppendLiteral("grad"); break;
213
0
    case eStyleUnit_Radian: aResult.AppendLiteral("rad");  break;
214
0
    case eStyleUnit_Turn:   aResult.AppendLiteral("turn"); break;
215
0
    default: MOZ_ASSERT_UNREACHABLE("unrecognized angle unit");
216
0
  }
217
0
}
218
219
/* static */ void
220
nsStyleUtil::AppendPaintOrderValue(uint8_t aValue,
221
                                   nsAString& aResult)
222
0
{
223
0
  static_assert
224
0
    (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8,
225
0
     "SVGStyleStruct::mPaintOrder and local variables not big enough");
226
0
227
0
  if (aValue == NS_STYLE_PAINT_ORDER_NORMAL) {
228
0
    aResult.AppendLiteral("normal");
229
0
    return;
230
0
  }
231
0
232
0
  // Append the minimal value necessary for the given paint order.
233
0
  static_assert(NS_STYLE_PAINT_ORDER_LAST_VALUE == 3,
234
0
                "paint-order values added; check serialization");
235
0
236
0
  // The following relies on the default order being the order of the
237
0
  // constant values.
238
0
239
0
  const uint8_t MASK = (1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1;
240
0
241
0
  uint32_t lastPositionToSerialize = 0;
242
0
  for (uint32_t position = NS_STYLE_PAINT_ORDER_LAST_VALUE - 1;
243
0
       position > 0;
244
0
       position--) {
245
0
    uint8_t component =
246
0
      (aValue >> (position * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK;
247
0
    uint8_t earlierComponent =
248
0
      (aValue >> ((position - 1) * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK;
249
0
    if (component < earlierComponent) {
250
0
      lastPositionToSerialize = position - 1;
251
0
      break;
252
0
    }
253
0
  }
254
0
255
0
  for (uint32_t position = 0; position <= lastPositionToSerialize; position++) {
256
0
    if (position > 0) {
257
0
      aResult.Append(' ');
258
0
    }
259
0
    uint8_t component = aValue & MASK;
260
0
    switch (component) {
261
0
      case NS_STYLE_PAINT_ORDER_FILL:
262
0
        aResult.AppendLiteral("fill");
263
0
        break;
264
0
265
0
      case NS_STYLE_PAINT_ORDER_STROKE:
266
0
        aResult.AppendLiteral("stroke");
267
0
        break;
268
0
269
0
      case NS_STYLE_PAINT_ORDER_MARKERS:
270
0
        aResult.AppendLiteral("markers");
271
0
        break;
272
0
273
0
      default:
274
0
        MOZ_ASSERT_UNREACHABLE("unexpected paint-order component value");
275
0
    }
276
0
    aValue >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
277
0
  }
278
0
}
279
280
/* static */ void
281
nsStyleUtil::AppendStepsTimingFunction(nsTimingFunction::Type aType,
282
                                       uint32_t aSteps,
283
                                       nsAString& aResult)
284
0
{
285
0
  MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
286
0
             aType == nsTimingFunction::Type::StepEnd);
287
0
288
0
  aResult.AppendLiteral("steps(");
289
0
  aResult.AppendInt(aSteps);
290
0
  if (aType == nsTimingFunction::Type::StepStart) {
291
0
    aResult.AppendLiteral(", start)");
292
0
  } else {
293
0
    aResult.AppendLiteral(")");
294
0
  }
295
0
}
296
297
/* static */ void
298
nsStyleUtil::AppendFramesTimingFunction(uint32_t aFrames,
299
                                        nsAString& aResult)
300
0
{
301
0
  aResult.AppendLiteral("frames(");
302
0
  aResult.AppendInt(aFrames);
303
0
  aResult.AppendLiteral(")");
304
0
}
305
306
/* static */ void
307
nsStyleUtil::AppendCubicBezierTimingFunction(float aX1, float aY1,
308
                                             float aX2, float aY2,
309
                                             nsAString& aResult)
310
0
{
311
0
  // set the value from the cubic-bezier control points
312
0
  // (We could try to regenerate the keywords if we want.)
313
0
  aResult.AppendLiteral("cubic-bezier(");
314
0
  aResult.AppendFloat(aX1);
315
0
  aResult.AppendLiteral(", ");
316
0
  aResult.AppendFloat(aY1);
317
0
  aResult.AppendLiteral(", ");
318
0
  aResult.AppendFloat(aX2);
319
0
  aResult.AppendLiteral(", ");
320
0
  aResult.AppendFloat(aY2);
321
0
  aResult.Append(')');
322
0
}
323
324
/* static */ void
325
nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
326
    nsTimingFunction::Type aType,
327
    nsAString& aResult)
328
0
{
329
0
  switch (aType) {
330
0
    case nsTimingFunction::Type::Ease:
331
0
    case nsTimingFunction::Type::Linear:
332
0
    case nsTimingFunction::Type::EaseIn:
333
0
    case nsTimingFunction::Type::EaseOut:
334
0
    case nsTimingFunction::Type::EaseInOut: {
335
0
      nsCSSKeyword keyword = nsCSSProps::ValueToKeywordEnum(
336
0
          static_cast<int32_t>(aType),
337
0
          nsCSSProps::kTransitionTimingFunctionKTable);
338
0
      AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(keyword),
339
0
                         aResult);
340
0
      break;
341
0
    }
342
0
    default:
343
0
      MOZ_ASSERT_UNREACHABLE("unexpected aType");
344
0
      break;
345
0
  }
346
0
}
347
348
/* static */ float
349
nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha)
350
0
{
351
0
  // Alpha values are expressed as decimals, so we should convert
352
0
  // back, using as few decimal places as possible for
353
0
  // round-tripping.
354
0
  // First try two decimal places:
355
0
  float rounded = NS_roundf(float(aAlpha) * 100.0f / 255.0f) / 100.0f;
356
0
  if (FloatToColorComponent(rounded) != aAlpha) {
357
0
    // Use three decimal places.
358
0
    rounded = NS_roundf(float(aAlpha) * 1000.0f / 255.0f) / 1000.0f;
359
0
  }
360
0
  return rounded;
361
0
}
362
363
/* static */ bool
364
nsStyleUtil::IsSignificantChild(nsIContent* aChild,
365
                                bool aWhitespaceIsSignificant)
366
0
{
367
0
  bool isText = aChild->IsText();
368
0
369
0
  if (!isText && !aChild->IsComment() && !aChild->IsProcessingInstruction()) {
370
0
    return true;
371
0
  }
372
0
373
0
  return isText && aChild->TextLength() != 0 &&
374
0
         (aWhitespaceIsSignificant ||
375
0
          !aChild->TextIsOnlyWhitespace());
376
0
}
377
378
/* static */ bool
379
nsStyleUtil::ThreadSafeIsSignificantChild(const nsIContent* aChild,
380
                                          bool aWhitespaceIsSignificant)
381
0
{
382
0
  bool isText = aChild->IsText();
383
0
384
0
  if (!isText && !aChild->IsComment() && !aChild->IsProcessingInstruction()) {
385
0
    return true;
386
0
  }
387
0
388
0
  return isText && aChild->TextLength() != 0 &&
389
0
         (aWhitespaceIsSignificant ||
390
0
          !aChild->ThreadSafeTextIsOnlyWhitespace());
391
0
}
392
393
// For a replaced element whose concrete object size is no larger than the
394
// element's content-box, this method checks whether the given
395
// "object-position" coordinate might cause overflow in its dimension.
396
static bool
397
ObjectPositionCoordMightCauseOverflow(const Position::Coord& aCoord)
398
0
{
399
0
  // Any nonzero length in "object-position" can push us to overflow
400
0
  // (particularly if our concrete object size is exactly the same size as the
401
0
  // replaced element's content-box).
402
0
  if (aCoord.mLength != 0) {
403
0
    return true;
404
0
  }
405
0
406
0
  // Percentages are interpreted as a fraction of the extra space. So,
407
0
  // percentages in the 0-100% range are safe, but values outside of that
408
0
  // range could cause overflow.
409
0
  if (aCoord.mHasPercent &&
410
0
      (aCoord.mPercent < 0.0f || aCoord.mPercent > 1.0f)) {
411
0
    return true;
412
0
  }
413
0
  return false;
414
0
}
415
416
417
/* static */ bool
418
nsStyleUtil::ObjectPropsMightCauseOverflow(const nsStylePosition* aStylePos)
419
0
{
420
0
  auto objectFit = aStylePos->mObjectFit;
421
0
422
0
  // "object-fit: cover" & "object-fit: none" can give us a render rect that's
423
0
  // larger than our container element's content-box.
424
0
  if (objectFit == NS_STYLE_OBJECT_FIT_COVER ||
425
0
      objectFit == NS_STYLE_OBJECT_FIT_NONE) {
426
0
    return true;
427
0
  }
428
0
  // (All other object-fit values produce a concrete object size that's no larger
429
0
  // than the constraint region.)
430
0
431
0
  // Check each of our "object-position" coords to see if it could cause
432
0
  // overflow in its dimension:
433
0
  const Position& objectPosistion = aStylePos->mObjectPosition;
434
0
  if (ObjectPositionCoordMightCauseOverflow(objectPosistion.mXPosition) ||
435
0
      ObjectPositionCoordMightCauseOverflow(objectPosistion.mYPosition)) {
436
0
    return true;
437
0
  }
438
0
439
0
  return false;
440
0
}
441
442
443
/* static */ bool
444
nsStyleUtil::CSPAllowsInlineStyle(Element* aElement,
445
                                  nsIPrincipal* aPrincipal,
446
                                  nsIPrincipal* aTriggeringPrincipal,
447
                                  nsIURI* aSourceURI,
448
                                  uint32_t aLineNumber,
449
                                  uint32_t aColumnNumber,
450
                                  const nsAString& aStyleText,
451
                                  nsresult* aRv)
452
0
{
453
0
  nsresult rv;
454
0
455
0
  if (aRv) {
456
0
    *aRv = NS_OK;
457
0
  }
458
0
459
0
  nsIPrincipal* principal = aPrincipal;
460
0
  if (aTriggeringPrincipal &&
461
0
      BasePrincipal::Cast(aTriggeringPrincipal)->OverridesCSP(aPrincipal)) {
462
0
    principal = aTriggeringPrincipal;
463
0
  }
464
0
465
0
  nsCOMPtr<nsIContentSecurityPolicy> csp;
466
0
  rv = principal->GetCsp(getter_AddRefs(csp));
467
0
468
0
  if (NS_FAILED(rv)) {
469
0
    if (aRv)
470
0
      *aRv = rv;
471
0
    return false;
472
0
  }
473
0
474
0
  if (!csp) {
475
0
    // No CSP --> the style is allowed
476
0
    return true;
477
0
  }
478
0
479
0
  // query the nonce
480
0
  nsAutoString nonce;
481
0
  if (aElement && aElement->NodeInfo()->NameAtom() == nsGkAtoms::style) {
482
0
    aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce);
483
0
  }
484
0
485
0
  bool allowInlineStyle = true;
486
0
  rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_STYLESHEET,
487
0
                            nonce,
488
0
                            false, // aParserCreated only applies to scripts
489
0
                            aElement, aStyleText, aLineNumber, aColumnNumber,
490
0
                            &allowInlineStyle);
491
0
  NS_ENSURE_SUCCESS(rv, false);
492
0
493
0
  return allowInlineStyle;
494
0
}
495
496
void
497
nsStyleUtil::AppendFontSlantStyle(const FontSlantStyle& aStyle, nsAString& aOut)
498
0
{
499
0
  if (aStyle.IsNormal()) {
500
0
    aOut.AppendLiteral("normal");
501
0
  } else if (aStyle.IsItalic()) {
502
0
    aOut.AppendLiteral("italic");
503
0
  } else {
504
0
    aOut.AppendLiteral("oblique");
505
0
    auto angle = aStyle.ObliqueAngle();
506
0
    if (angle != FontSlantStyle::kDefaultAngle) {
507
0
      aOut.AppendLiteral(" ");
508
0
      AppendAngleValue(nsStyleCoord(angle, eStyleUnit_Degree), aOut);
509
0
    }
510
0
  }
511
0
}