layout/style/nsStyleUtil.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial