Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nsStyleUtil.h" |
michael@0 | 7 | #include "nsStyleConsts.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "nsIContent.h" |
michael@0 | 10 | #include "nsCSSProps.h" |
michael@0 | 11 | #include "nsRuleNode.h" |
michael@0 | 12 | #include "nsROCSSPrimitiveValue.h" |
michael@0 | 13 | #include "nsIContentPolicy.h" |
michael@0 | 14 | #include "nsIContentSecurityPolicy.h" |
michael@0 | 15 | #include "nsIURI.h" |
michael@0 | 16 | |
michael@0 | 17 | using namespace mozilla; |
michael@0 | 18 | |
michael@0 | 19 | //------------------------------------------------------------------------------ |
michael@0 | 20 | // Font Algorithm Code |
michael@0 | 21 | //------------------------------------------------------------------------------ |
michael@0 | 22 | |
michael@0 | 23 | // Compare two language strings |
michael@0 | 24 | bool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue, |
michael@0 | 25 | const nsAString& aSelectorValue, |
michael@0 | 26 | const nsStringComparator& aComparator) |
michael@0 | 27 | { |
michael@0 | 28 | bool result; |
michael@0 | 29 | uint32_t selectorLen = aSelectorValue.Length(); |
michael@0 | 30 | uint32_t attributeLen = aAttributeValue.Length(); |
michael@0 | 31 | if (selectorLen > attributeLen) { |
michael@0 | 32 | result = false; |
michael@0 | 33 | } |
michael@0 | 34 | else { |
michael@0 | 35 | nsAString::const_iterator iter; |
michael@0 | 36 | if (selectorLen != attributeLen && |
michael@0 | 37 | *aAttributeValue.BeginReading(iter).advance(selectorLen) != |
michael@0 | 38 | char16_t('-')) { |
michael@0 | 39 | // to match, the aAttributeValue must have a dash after the end of |
michael@0 | 40 | // the aSelectorValue's text (unless the aSelectorValue and the |
michael@0 | 41 | // aAttributeValue have the same text) |
michael@0 | 42 | result = false; |
michael@0 | 43 | } |
michael@0 | 44 | else { |
michael@0 | 45 | result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator); |
michael@0 | 46 | } |
michael@0 | 47 | } |
michael@0 | 48 | return result; |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | void nsStyleUtil::AppendEscapedCSSString(const nsAString& aString, |
michael@0 | 52 | nsAString& aReturn, |
michael@0 | 53 | char16_t quoteChar) |
michael@0 | 54 | { |
michael@0 | 55 | NS_PRECONDITION(quoteChar == '\'' || quoteChar == '"', |
michael@0 | 56 | "CSS strings must be quoted with ' or \""); |
michael@0 | 57 | aReturn.Append(quoteChar); |
michael@0 | 58 | |
michael@0 | 59 | const char16_t* in = aString.BeginReading(); |
michael@0 | 60 | const char16_t* const end = aString.EndReading(); |
michael@0 | 61 | for (; in != end; in++) { |
michael@0 | 62 | if (*in < 0x20 || (*in >= 0x7F && *in < 0xA0)) { |
michael@0 | 63 | // Escape U+0000 through U+001F and U+007F through U+009F numerically. |
michael@0 | 64 | aReturn.AppendPrintf("\\%hX ", *in); |
michael@0 | 65 | } else { |
michael@0 | 66 | if (*in == '"' || *in == '\'' || *in == '\\') { |
michael@0 | 67 | // Escape backslash and quote characters symbolically. |
michael@0 | 68 | // It's not technically necessary to escape the quote |
michael@0 | 69 | // character that isn't being used to delimit the string, |
michael@0 | 70 | // but we do it anyway because that makes testing simpler. |
michael@0 | 71 | aReturn.Append(char16_t('\\')); |
michael@0 | 72 | } |
michael@0 | 73 | aReturn.Append(*in); |
michael@0 | 74 | } |
michael@0 | 75 | } |
michael@0 | 76 | |
michael@0 | 77 | aReturn.Append(quoteChar); |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | /* static */ bool |
michael@0 | 81 | nsStyleUtil::AppendEscapedCSSIdent(const nsAString& aIdent, nsAString& aReturn) |
michael@0 | 82 | { |
michael@0 | 83 | // The relevant parts of the CSS grammar are: |
michael@0 | 84 | // ident [-]?{nmstart}{nmchar}* |
michael@0 | 85 | // nmstart [_a-z]|{nonascii}|{escape} |
michael@0 | 86 | // nmchar [_a-z0-9-]|{nonascii}|{escape} |
michael@0 | 87 | // nonascii [^\0-\177] |
michael@0 | 88 | // escape {unicode}|\\[^\n\r\f0-9a-f] |
michael@0 | 89 | // unicode \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])? |
michael@0 | 90 | // from http://www.w3.org/TR/CSS21/syndata.html#tokenization |
michael@0 | 91 | |
michael@0 | 92 | const char16_t* in = aIdent.BeginReading(); |
michael@0 | 93 | const char16_t* const end = aIdent.EndReading(); |
michael@0 | 94 | |
michael@0 | 95 | if (in == end) |
michael@0 | 96 | return true; |
michael@0 | 97 | |
michael@0 | 98 | // A leading dash does not need to be escaped as long as it is not the |
michael@0 | 99 | // *only* character in the identifier. |
michael@0 | 100 | if (in + 1 != end && *in == '-') { |
michael@0 | 101 | aReturn.Append(char16_t('-')); |
michael@0 | 102 | ++in; |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | // Escape a digit at the start (including after a dash), |
michael@0 | 106 | // numerically. If we didn't escape it numerically, it would get |
michael@0 | 107 | // interpreted as a numeric escape for the wrong character. |
michael@0 | 108 | // A second dash immediately after a leading dash must also be |
michael@0 | 109 | // escaped, but this may be done symbolically. |
michael@0 | 110 | if (in != end && (*in == '-' || |
michael@0 | 111 | ('0' <= *in && *in <= '9'))) { |
michael@0 | 112 | if (*in == '-') { |
michael@0 | 113 | aReturn.Append(char16_t('\\')); |
michael@0 | 114 | aReturn.Append(char16_t('-')); |
michael@0 | 115 | } else { |
michael@0 | 116 | aReturn.AppendPrintf("\\%hX ", *in); |
michael@0 | 117 | } |
michael@0 | 118 | ++in; |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | for (; in != end; ++in) { |
michael@0 | 122 | char16_t ch = *in; |
michael@0 | 123 | if (ch == 0x00) { |
michael@0 | 124 | return false; |
michael@0 | 125 | } |
michael@0 | 126 | if (ch < 0x20 || (0x7F <= ch && ch < 0xA0)) { |
michael@0 | 127 | // Escape U+0000 through U+001F and U+007F through U+009F numerically. |
michael@0 | 128 | aReturn.AppendPrintf("\\%hX ", *in); |
michael@0 | 129 | } else { |
michael@0 | 130 | // Escape ASCII non-identifier printables as a backslash plus |
michael@0 | 131 | // the character. |
michael@0 | 132 | if (ch < 0x7F && |
michael@0 | 133 | ch != '_' && ch != '-' && |
michael@0 | 134 | (ch < '0' || '9' < ch) && |
michael@0 | 135 | (ch < 'A' || 'Z' < ch) && |
michael@0 | 136 | (ch < 'a' || 'z' < ch)) { |
michael@0 | 137 | aReturn.Append(char16_t('\\')); |
michael@0 | 138 | } |
michael@0 | 139 | aReturn.Append(ch); |
michael@0 | 140 | } |
michael@0 | 141 | } |
michael@0 | 142 | return true; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | /* static */ void |
michael@0 | 146 | nsStyleUtil::AppendBitmaskCSSValue(nsCSSProperty aProperty, |
michael@0 | 147 | int32_t aMaskedValue, |
michael@0 | 148 | int32_t aFirstMask, |
michael@0 | 149 | int32_t aLastMask, |
michael@0 | 150 | nsAString& aResult) |
michael@0 | 151 | { |
michael@0 | 152 | for (int32_t mask = aFirstMask; mask <= aLastMask; mask <<= 1) { |
michael@0 | 153 | if (mask & aMaskedValue) { |
michael@0 | 154 | AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, mask), |
michael@0 | 155 | aResult); |
michael@0 | 156 | aMaskedValue &= ~mask; |
michael@0 | 157 | if (aMaskedValue) { // more left |
michael@0 | 158 | aResult.Append(char16_t(' ')); |
michael@0 | 159 | } |
michael@0 | 160 | } |
michael@0 | 161 | } |
michael@0 | 162 | NS_ABORT_IF_FALSE(aMaskedValue == 0, "unexpected bit remaining in bitfield"); |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | /* static */ void |
michael@0 | 166 | nsStyleUtil::AppendAngleValue(const nsStyleCoord& aAngle, nsAString& aResult) |
michael@0 | 167 | { |
michael@0 | 168 | MOZ_ASSERT(aAngle.IsAngleValue(), "Should have angle value"); |
michael@0 | 169 | |
michael@0 | 170 | // Append number. |
michael@0 | 171 | AppendCSSNumber(aAngle.GetAngleValue(), aResult); |
michael@0 | 172 | |
michael@0 | 173 | // Append unit. |
michael@0 | 174 | switch (aAngle.GetUnit()) { |
michael@0 | 175 | case eStyleUnit_Degree: aResult.AppendLiteral("deg"); break; |
michael@0 | 176 | case eStyleUnit_Grad: aResult.AppendLiteral("grad"); break; |
michael@0 | 177 | case eStyleUnit_Radian: aResult.AppendLiteral("rad"); break; |
michael@0 | 178 | case eStyleUnit_Turn: aResult.AppendLiteral("turn"); break; |
michael@0 | 179 | default: NS_NOTREACHED("unrecognized angle unit"); |
michael@0 | 180 | } |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | /* static */ void |
michael@0 | 184 | nsStyleUtil::AppendPaintOrderValue(uint8_t aValue, |
michael@0 | 185 | nsAString& aResult) |
michael@0 | 186 | { |
michael@0 | 187 | static_assert |
michael@0 | 188 | (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8, |
michael@0 | 189 | "SVGStyleStruct::mPaintOrder and local variables not big enough"); |
michael@0 | 190 | |
michael@0 | 191 | if (aValue == NS_STYLE_PAINT_ORDER_NORMAL) { |
michael@0 | 192 | aResult.AppendLiteral("normal"); |
michael@0 | 193 | return; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | // Append the minimal value necessary for the given paint order. |
michael@0 | 197 | static_assert(NS_STYLE_PAINT_ORDER_LAST_VALUE == 3, |
michael@0 | 198 | "paint-order values added; check serialization"); |
michael@0 | 199 | |
michael@0 | 200 | // The following relies on the default order being the order of the |
michael@0 | 201 | // constant values. |
michael@0 | 202 | |
michael@0 | 203 | const uint8_t MASK = (1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1; |
michael@0 | 204 | |
michael@0 | 205 | uint32_t lastPositionToSerialize = 0; |
michael@0 | 206 | for (uint32_t position = NS_STYLE_PAINT_ORDER_LAST_VALUE - 1; |
michael@0 | 207 | position > 0; |
michael@0 | 208 | position--) { |
michael@0 | 209 | uint8_t component = |
michael@0 | 210 | (aValue >> (position * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK; |
michael@0 | 211 | uint8_t earlierComponent = |
michael@0 | 212 | (aValue >> ((position - 1) * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK; |
michael@0 | 213 | if (component < earlierComponent) { |
michael@0 | 214 | lastPositionToSerialize = position - 1; |
michael@0 | 215 | break; |
michael@0 | 216 | } |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | for (uint32_t position = 0; position <= lastPositionToSerialize; position++) { |
michael@0 | 220 | if (position > 0) { |
michael@0 | 221 | aResult.AppendLiteral(" "); |
michael@0 | 222 | } |
michael@0 | 223 | uint8_t component = aValue & MASK; |
michael@0 | 224 | switch (component) { |
michael@0 | 225 | case NS_STYLE_PAINT_ORDER_FILL: |
michael@0 | 226 | aResult.AppendLiteral("fill"); |
michael@0 | 227 | break; |
michael@0 | 228 | |
michael@0 | 229 | case NS_STYLE_PAINT_ORDER_STROKE: |
michael@0 | 230 | aResult.AppendLiteral("stroke"); |
michael@0 | 231 | break; |
michael@0 | 232 | |
michael@0 | 233 | case NS_STYLE_PAINT_ORDER_MARKERS: |
michael@0 | 234 | aResult.AppendLiteral("markers"); |
michael@0 | 235 | break; |
michael@0 | 236 | |
michael@0 | 237 | default: |
michael@0 | 238 | NS_NOTREACHED("unexpected paint-order component value"); |
michael@0 | 239 | } |
michael@0 | 240 | aValue >>= NS_STYLE_PAINT_ORDER_BITWIDTH; |
michael@0 | 241 | } |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | /* static */ void |
michael@0 | 245 | nsStyleUtil::AppendFontFeatureSettings(const nsTArray<gfxFontFeature>& aFeatures, |
michael@0 | 246 | nsAString& aResult) |
michael@0 | 247 | { |
michael@0 | 248 | for (uint32_t i = 0, numFeat = aFeatures.Length(); i < numFeat; i++) { |
michael@0 | 249 | const gfxFontFeature& feat = aFeatures[i]; |
michael@0 | 250 | |
michael@0 | 251 | if (i != 0) { |
michael@0 | 252 | aResult.AppendLiteral(", "); |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | // output tag |
michael@0 | 256 | char tag[7]; |
michael@0 | 257 | tag[0] = '"'; |
michael@0 | 258 | tag[1] = (feat.mTag >> 24) & 0xff; |
michael@0 | 259 | tag[2] = (feat.mTag >> 16) & 0xff; |
michael@0 | 260 | tag[3] = (feat.mTag >> 8) & 0xff; |
michael@0 | 261 | tag[4] = feat.mTag & 0xff; |
michael@0 | 262 | tag[5] = '"'; |
michael@0 | 263 | tag[6] = 0; |
michael@0 | 264 | aResult.AppendASCII(tag); |
michael@0 | 265 | |
michael@0 | 266 | // output value, if necessary |
michael@0 | 267 | if (feat.mValue == 0) { |
michael@0 | 268 | // 0 ==> off |
michael@0 | 269 | aResult.AppendLiteral(" off"); |
michael@0 | 270 | } else if (feat.mValue > 1) { |
michael@0 | 271 | aResult.AppendLiteral(" "); |
michael@0 | 272 | aResult.AppendInt(feat.mValue); |
michael@0 | 273 | } |
michael@0 | 274 | // else, omit value if 1, implied by default |
michael@0 | 275 | } |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | /* static */ void |
michael@0 | 279 | nsStyleUtil::AppendFontFeatureSettings(const nsCSSValue& aSrc, |
michael@0 | 280 | nsAString& aResult) |
michael@0 | 281 | { |
michael@0 | 282 | nsCSSUnit unit = aSrc.GetUnit(); |
michael@0 | 283 | |
michael@0 | 284 | if (unit == eCSSUnit_Normal) { |
michael@0 | 285 | aResult.AppendLiteral("normal"); |
michael@0 | 286 | return; |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | NS_PRECONDITION(unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep, |
michael@0 | 290 | "improper value unit for font-feature-settings:"); |
michael@0 | 291 | |
michael@0 | 292 | nsTArray<gfxFontFeature> featureSettings; |
michael@0 | 293 | nsRuleNode::ComputeFontFeatures(aSrc.GetPairListValue(), featureSettings); |
michael@0 | 294 | AppendFontFeatureSettings(featureSettings, aResult); |
michael@0 | 295 | } |
michael@0 | 296 | |
michael@0 | 297 | /* static */ void |
michael@0 | 298 | nsStyleUtil::GetFunctionalAlternatesName(int32_t aFeature, |
michael@0 | 299 | nsAString& aFeatureName) |
michael@0 | 300 | { |
michael@0 | 301 | aFeatureName.Truncate(); |
michael@0 | 302 | nsCSSKeyword key = |
michael@0 | 303 | nsCSSProps::ValueToKeywordEnum(aFeature, |
michael@0 | 304 | nsCSSProps::kFontVariantAlternatesFuncsKTable); |
michael@0 | 305 | |
michael@0 | 306 | NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "bad alternate feature type"); |
michael@0 | 307 | AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(key), aFeatureName); |
michael@0 | 308 | } |
michael@0 | 309 | |
michael@0 | 310 | /* static */ void |
michael@0 | 311 | nsStyleUtil::SerializeFunctionalAlternates( |
michael@0 | 312 | const nsTArray<gfxAlternateValue>& aAlternates, |
michael@0 | 313 | nsAString& aResult) |
michael@0 | 314 | { |
michael@0 | 315 | nsAutoString funcName, funcParams; |
michael@0 | 316 | uint32_t numValues = aAlternates.Length(); |
michael@0 | 317 | |
michael@0 | 318 | uint32_t feature = 0; |
michael@0 | 319 | for (uint32_t i = 0; i < numValues; i++) { |
michael@0 | 320 | const gfxAlternateValue& v = aAlternates.ElementAt(i); |
michael@0 | 321 | if (feature != v.alternate) { |
michael@0 | 322 | feature = v.alternate; |
michael@0 | 323 | if (!funcName.IsEmpty() && !funcParams.IsEmpty()) { |
michael@0 | 324 | if (!aResult.IsEmpty()) { |
michael@0 | 325 | aResult.Append(char16_t(' ')); |
michael@0 | 326 | } |
michael@0 | 327 | |
michael@0 | 328 | // append the previous functional value |
michael@0 | 329 | aResult.Append(funcName); |
michael@0 | 330 | aResult.Append(char16_t('(')); |
michael@0 | 331 | aResult.Append(funcParams); |
michael@0 | 332 | aResult.Append(char16_t(')')); |
michael@0 | 333 | } |
michael@0 | 334 | |
michael@0 | 335 | // function name |
michael@0 | 336 | GetFunctionalAlternatesName(v.alternate, funcName); |
michael@0 | 337 | NS_ASSERTION(!funcName.IsEmpty(), "unknown property value name"); |
michael@0 | 338 | |
michael@0 | 339 | // function params |
michael@0 | 340 | funcParams.Truncate(); |
michael@0 | 341 | AppendEscapedCSSIdent(v.value, funcParams); |
michael@0 | 342 | } else { |
michael@0 | 343 | if (!funcParams.IsEmpty()) { |
michael@0 | 344 | funcParams.Append(NS_LITERAL_STRING(", ")); |
michael@0 | 345 | } |
michael@0 | 346 | AppendEscapedCSSIdent(v.value, funcParams); |
michael@0 | 347 | } |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | // append the previous functional value |
michael@0 | 351 | if (!funcName.IsEmpty() && !funcParams.IsEmpty()) { |
michael@0 | 352 | if (!aResult.IsEmpty()) { |
michael@0 | 353 | aResult.Append(char16_t(' ')); |
michael@0 | 354 | } |
michael@0 | 355 | |
michael@0 | 356 | aResult.Append(funcName); |
michael@0 | 357 | aResult.Append(char16_t('(')); |
michael@0 | 358 | aResult.Append(funcParams); |
michael@0 | 359 | aResult.Append(char16_t(')')); |
michael@0 | 360 | } |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | /* static */ void |
michael@0 | 364 | nsStyleUtil::ComputeFunctionalAlternates(const nsCSSValueList* aList, |
michael@0 | 365 | nsTArray<gfxAlternateValue>& aAlternateValues) |
michael@0 | 366 | { |
michael@0 | 367 | gfxAlternateValue v; |
michael@0 | 368 | |
michael@0 | 369 | aAlternateValues.Clear(); |
michael@0 | 370 | for (const nsCSSValueList* curr = aList; curr != nullptr; curr = curr->mNext) { |
michael@0 | 371 | // list contains function units |
michael@0 | 372 | if (curr->mValue.GetUnit() != eCSSUnit_Function) { |
michael@0 | 373 | continue; |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | // element 0 is the propval in ident form |
michael@0 | 377 | const nsCSSValue::Array *func = curr->mValue.GetArrayValue(); |
michael@0 | 378 | |
michael@0 | 379 | // lookup propval |
michael@0 | 380 | nsCSSKeyword key = func->Item(0).GetKeywordValue(); |
michael@0 | 381 | NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "unknown alternate property value"); |
michael@0 | 382 | |
michael@0 | 383 | int32_t alternate; |
michael@0 | 384 | if (key == eCSSKeyword_UNKNOWN || |
michael@0 | 385 | !nsCSSProps::FindKeyword(key, |
michael@0 | 386 | nsCSSProps::kFontVariantAlternatesFuncsKTable, |
michael@0 | 387 | alternate)) { |
michael@0 | 388 | NS_NOTREACHED("keyword not a font-variant-alternates value"); |
michael@0 | 389 | continue; |
michael@0 | 390 | } |
michael@0 | 391 | v.alternate = alternate; |
michael@0 | 392 | |
michael@0 | 393 | // other elements are the idents associated with the propval |
michael@0 | 394 | // append one alternate value for each one |
michael@0 | 395 | uint32_t numElems = func->Count(); |
michael@0 | 396 | for (uint32_t i = 1; i < numElems; i++) { |
michael@0 | 397 | const nsCSSValue& value = func->Item(i); |
michael@0 | 398 | NS_ASSERTION(value.GetUnit() == eCSSUnit_Ident, |
michael@0 | 399 | "weird unit found in variant alternate"); |
michael@0 | 400 | if (value.GetUnit() != eCSSUnit_Ident) { |
michael@0 | 401 | continue; |
michael@0 | 402 | } |
michael@0 | 403 | value.GetStringValue(v.value); |
michael@0 | 404 | aAlternateValues.AppendElement(v); |
michael@0 | 405 | } |
michael@0 | 406 | } |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | /* static */ float |
michael@0 | 410 | nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha) |
michael@0 | 411 | { |
michael@0 | 412 | // Alpha values are expressed as decimals, so we should convert |
michael@0 | 413 | // back, using as few decimal places as possible for |
michael@0 | 414 | // round-tripping. |
michael@0 | 415 | // First try two decimal places: |
michael@0 | 416 | float rounded = NS_roundf(float(aAlpha) * 100.0f / 255.0f) / 100.0f; |
michael@0 | 417 | if (FloatToColorComponent(rounded) != aAlpha) { |
michael@0 | 418 | // Use three decimal places. |
michael@0 | 419 | rounded = NS_roundf(float(aAlpha) * 1000.0f / 255.0f) / 1000.0f; |
michael@0 | 420 | } |
michael@0 | 421 | return rounded; |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | /* static */ bool |
michael@0 | 425 | nsStyleUtil::IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant, |
michael@0 | 426 | bool aWhitespaceIsSignificant) |
michael@0 | 427 | { |
michael@0 | 428 | NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant, |
michael@0 | 429 | "Nonsensical arguments"); |
michael@0 | 430 | |
michael@0 | 431 | bool isText = aChild->IsNodeOfType(nsINode::eTEXT); |
michael@0 | 432 | |
michael@0 | 433 | if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) && |
michael@0 | 434 | !aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) { |
michael@0 | 435 | return true; |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | return aTextIsSignificant && isText && aChild->TextLength() != 0 && |
michael@0 | 439 | (aWhitespaceIsSignificant || |
michael@0 | 440 | !aChild->TextIsOnlyWhitespace()); |
michael@0 | 441 | } |
michael@0 | 442 | |
michael@0 | 443 | /* static */ bool |
michael@0 | 444 | nsStyleUtil::CSPAllowsInlineStyle(nsIContent* aContent, |
michael@0 | 445 | nsIPrincipal* aPrincipal, |
michael@0 | 446 | nsIURI* aSourceURI, |
michael@0 | 447 | uint32_t aLineNumber, |
michael@0 | 448 | const nsSubstring& aStyleText, |
michael@0 | 449 | nsresult* aRv) |
michael@0 | 450 | { |
michael@0 | 451 | nsresult rv; |
michael@0 | 452 | |
michael@0 | 453 | if (aRv) { |
michael@0 | 454 | *aRv = NS_OK; |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | MOZ_ASSERT(!aContent || aContent->Tag() == nsGkAtoms::style, |
michael@0 | 458 | "aContent passed to CSPAllowsInlineStyle " |
michael@0 | 459 | "for an element that is not <style>"); |
michael@0 | 460 | |
michael@0 | 461 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
michael@0 | 462 | rv = aPrincipal->GetCsp(getter_AddRefs(csp)); |
michael@0 | 463 | |
michael@0 | 464 | if (NS_FAILED(rv)) { |
michael@0 | 465 | if (aRv) |
michael@0 | 466 | *aRv = rv; |
michael@0 | 467 | return false; |
michael@0 | 468 | } |
michael@0 | 469 | |
michael@0 | 470 | if (!csp) { |
michael@0 | 471 | // No CSP --> the style is allowed |
michael@0 | 472 | return true; |
michael@0 | 473 | } |
michael@0 | 474 | |
michael@0 | 475 | // An inline style can be allowed because all inline styles are allowed, |
michael@0 | 476 | // or else because it is whitelisted by a nonce-source or hash-source. This |
michael@0 | 477 | // is a logical OR between whitelisting methods, so the allowInlineStyle |
michael@0 | 478 | // outparam can be reused for each check as long as we stop checking as soon |
michael@0 | 479 | // as it is set to true. This also optimizes performance by avoiding the |
michael@0 | 480 | // overhead of unnecessary checks. |
michael@0 | 481 | bool allowInlineStyle = true; |
michael@0 | 482 | nsAutoTArray<unsigned short, 3> violations; |
michael@0 | 483 | |
michael@0 | 484 | bool reportInlineViolation; |
michael@0 | 485 | rv = csp->GetAllowsInlineStyle(&reportInlineViolation, &allowInlineStyle); |
michael@0 | 486 | if (NS_FAILED(rv)) { |
michael@0 | 487 | if (aRv) |
michael@0 | 488 | *aRv = rv; |
michael@0 | 489 | return false; |
michael@0 | 490 | } |
michael@0 | 491 | if (reportInlineViolation) { |
michael@0 | 492 | violations.AppendElement(static_cast<unsigned short>( |
michael@0 | 493 | nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE)); |
michael@0 | 494 | } |
michael@0 | 495 | |
michael@0 | 496 | nsAutoString nonce; |
michael@0 | 497 | if (!allowInlineStyle) { |
michael@0 | 498 | // We can only find a nonce if aContent is provided |
michael@0 | 499 | bool foundNonce = !!aContent && |
michael@0 | 500 | aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce); |
michael@0 | 501 | if (foundNonce) { |
michael@0 | 502 | bool reportNonceViolation; |
michael@0 | 503 | rv = csp->GetAllowsNonce(nonce, nsIContentPolicy::TYPE_STYLESHEET, |
michael@0 | 504 | &reportNonceViolation, &allowInlineStyle); |
michael@0 | 505 | if (NS_FAILED(rv)) { |
michael@0 | 506 | if (aRv) |
michael@0 | 507 | *aRv = rv; |
michael@0 | 508 | return false; |
michael@0 | 509 | } |
michael@0 | 510 | |
michael@0 | 511 | if (reportNonceViolation) { |
michael@0 | 512 | violations.AppendElement(static_cast<unsigned short>( |
michael@0 | 513 | nsIContentSecurityPolicy::VIOLATION_TYPE_NONCE_STYLE)); |
michael@0 | 514 | } |
michael@0 | 515 | } |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | if (!allowInlineStyle) { |
michael@0 | 519 | bool reportHashViolation; |
michael@0 | 520 | rv = csp->GetAllowsHash(aStyleText, nsIContentPolicy::TYPE_STYLESHEET, |
michael@0 | 521 | &reportHashViolation, &allowInlineStyle); |
michael@0 | 522 | if (NS_FAILED(rv)) { |
michael@0 | 523 | if (aRv) |
michael@0 | 524 | *aRv = rv; |
michael@0 | 525 | return false; |
michael@0 | 526 | } |
michael@0 | 527 | if (reportHashViolation) { |
michael@0 | 528 | violations.AppendElement(static_cast<unsigned short>( |
michael@0 | 529 | nsIContentSecurityPolicy::VIOLATION_TYPE_HASH_STYLE)); |
michael@0 | 530 | } |
michael@0 | 531 | } |
michael@0 | 532 | |
michael@0 | 533 | // What violation(s) should be reported? |
michael@0 | 534 | // |
michael@0 | 535 | // 1. If the style tag has a nonce attribute, and the nonce does not match |
michael@0 | 536 | // the policy, report VIOLATION_TYPE_NONCE_STYLE. |
michael@0 | 537 | // 2. If the policy has at least one hash-source, and the hashed contents of |
michael@0 | 538 | // the style tag did not match any of them, report VIOLATION_TYPE_HASH_STYLE |
michael@0 | 539 | // 3. Otherwise, report VIOLATION_TYPE_INLINE_STYLE if appropriate. |
michael@0 | 540 | // |
michael@0 | 541 | // 1 and 2 may occur together, 3 should only occur by itself. Naturally, |
michael@0 | 542 | // every VIOLATION_TYPE_NONCE_STYLE and VIOLATION_TYPE_HASH_STYLE are also |
michael@0 | 543 | // VIOLATION_TYPE_INLINE_STYLE, but reporting the |
michael@0 | 544 | // VIOLATION_TYPE_INLINE_STYLE is redundant and does not help the developer. |
michael@0 | 545 | if (!violations.IsEmpty()) { |
michael@0 | 546 | MOZ_ASSERT(violations[0] == nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE, |
michael@0 | 547 | "How did we get any violations without an initial inline style violation?"); |
michael@0 | 548 | // This inline style is not allowed by CSP, so report the violation |
michael@0 | 549 | nsAutoCString asciiSpec; |
michael@0 | 550 | aSourceURI->GetAsciiSpec(asciiSpec); |
michael@0 | 551 | nsAutoString styleSample(aStyleText); |
michael@0 | 552 | |
michael@0 | 553 | // cap the length of the style sample at 40 chars. |
michael@0 | 554 | if (styleSample.Length() > 40) { |
michael@0 | 555 | styleSample.Truncate(40); |
michael@0 | 556 | styleSample.AppendLiteral("..."); |
michael@0 | 557 | } |
michael@0 | 558 | |
michael@0 | 559 | for (uint32_t i = 0; i < violations.Length(); i++) { |
michael@0 | 560 | // Skip reporting the redundant inline style violation if there are |
michael@0 | 561 | // other (nonce and/or hash violations) as well. |
michael@0 | 562 | if (i > 0 || violations.Length() == 1) { |
michael@0 | 563 | csp->LogViolationDetails(violations[i], NS_ConvertUTF8toUTF16(asciiSpec), |
michael@0 | 564 | styleSample, aLineNumber, nonce, aStyleText); |
michael@0 | 565 | } |
michael@0 | 566 | } |
michael@0 | 567 | } |
michael@0 | 568 | |
michael@0 | 569 | if (!allowInlineStyle) { |
michael@0 | 570 | NS_ASSERTION(!violations.IsEmpty(), |
michael@0 | 571 | "CSP blocked inline style but is not reporting a violation"); |
michael@0 | 572 | // The inline style should be blocked. |
michael@0 | 573 | return false; |
michael@0 | 574 | } |
michael@0 | 575 | // CSP allows inline styles. |
michael@0 | 576 | return true; |
michael@0 | 577 | } |