forked from mirrors/gecko-dev
		
	 0df895545b
			
		
	
	
		0df895545b
		
	
	
	
	
		
			
			MozReview-Commit-ID: PVslOIt1ls --HG-- extra : rebase_source : 9f77e4cc7cc32fd977c54587120edeb9e8cb3136
		
			
				
	
	
		
			895 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			895 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsStyleUtil.h"
 | |
| #include "nsStyleConsts.h"
 | |
| 
 | |
| #include "nsIContent.h"
 | |
| #include "nsCSSProps.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsROCSSPrimitiveValue.h"
 | |
| #include "nsStyleStruct.h"
 | |
| #include "nsIContentPolicy.h"
 | |
| #include "nsIContentSecurityPolicy.h"
 | |
| #include "nsIURI.h"
 | |
| #include "nsISupportsPrimitives.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "nsPrintfCString.h"
 | |
| #include <cctype>
 | |
| 
 | |
| using namespace mozilla;
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Font Algorithm Code
 | |
| //------------------------------------------------------------------------------
 | |
| 
 | |
| // Compare two language strings
 | |
| bool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue,
 | |
|                                      const nsAString& aSelectorValue,
 | |
|                                      const nsStringComparator& aComparator)
 | |
| {
 | |
|   bool result;
 | |
|   uint32_t selectorLen = aSelectorValue.Length();
 | |
|   uint32_t attributeLen = aAttributeValue.Length();
 | |
|   if (selectorLen > attributeLen) {
 | |
|     result = false;
 | |
|   }
 | |
|   else {
 | |
|     nsAString::const_iterator iter;
 | |
|     if (selectorLen != attributeLen &&
 | |
|         *aAttributeValue.BeginReading(iter).advance(selectorLen) !=
 | |
|             char16_t('-')) {
 | |
|       // to match, the aAttributeValue must have a dash after the end of
 | |
|       // the aSelectorValue's text (unless the aSelectorValue and the
 | |
|       // aAttributeValue have the same text)
 | |
|       result = false;
 | |
|     }
 | |
|     else {
 | |
|       result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator);
 | |
|     }
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsStyleUtil::ValueIncludes(const nsAString& aValueList,
 | |
|                            const nsAString& aValue,
 | |
|                            const nsStringComparator& aComparator)
 | |
| {
 | |
|   const char16_t *p = aValueList.BeginReading(),
 | |
|               *p_end = aValueList.EndReading();
 | |
| 
 | |
|   while (p < p_end) {
 | |
|     // skip leading space
 | |
|     while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p))
 | |
|       ++p;
 | |
| 
 | |
|     const char16_t *val_start = p;
 | |
| 
 | |
|     // look for space or end
 | |
|     while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p))
 | |
|       ++p;
 | |
| 
 | |
|     const char16_t *val_end = p;
 | |
| 
 | |
|     if (val_start < val_end &&
 | |
|         aValue.Equals(Substring(val_start, val_end), aComparator))
 | |
|       return true;
 | |
| 
 | |
|     ++p; // we know the next character is not whitespace
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void nsStyleUtil::AppendEscapedCSSString(const nsAString& aString,
 | |
|                                          nsAString& aReturn,
 | |
|                                          char16_t quoteChar)
 | |
| {
 | |
|   NS_PRECONDITION(quoteChar == '\'' || quoteChar == '"',
 | |
|                   "CSS strings must be quoted with ' or \"");
 | |
|   aReturn.Append(quoteChar);
 | |
| 
 | |
|   const char16_t* in = aString.BeginReading();
 | |
|   const char16_t* const end = aString.EndReading();
 | |
|   for (; in != end; in++) {
 | |
|     if (*in < 0x20 || *in == 0x7F) {
 | |
|       // Escape U+0000 through U+001F and U+007F numerically.
 | |
|       aReturn.AppendPrintf("\\%x ", *in);
 | |
|     } else {
 | |
|       if (*in == '"' || *in == '\'' || *in == '\\') {
 | |
|         // Escape backslash and quote characters symbolically.
 | |
|         // It's not technically necessary to escape the quote
 | |
|         // character that isn't being used to delimit the string,
 | |
|         // but we do it anyway because that makes testing simpler.
 | |
|         aReturn.Append(char16_t('\\'));
 | |
|       }
 | |
|       aReturn.Append(*in);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   aReturn.Append(quoteChar);
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendEscapedCSSIdent(const nsAString& aIdent, nsAString& aReturn)
 | |
| {
 | |
|   // The relevant parts of the CSS grammar are:
 | |
|   //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
 | |
|   //   nmstart  [_a-z]|{nonascii}|{escape}
 | |
|   //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
 | |
|   //   nonascii [^\0-\177]
 | |
|   //   escape   {unicode}|\\[^\n\r\f0-9a-f]
 | |
|   //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
 | |
|   // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
 | |
|   // modified for idents by
 | |
|   // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
 | |
|   // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
 | |
| 
 | |
|   const char16_t* in = aIdent.BeginReading();
 | |
|   const char16_t* const end = aIdent.EndReading();
 | |
| 
 | |
|   if (in == end)
 | |
|     return;
 | |
| 
 | |
|   // A leading dash does not need to be escaped as long as it is not the
 | |
|   // *only* character in the identifier.
 | |
|   if (*in == '-') {
 | |
|     if (in + 1 == end) {
 | |
|       aReturn.Append(char16_t('\\'));
 | |
|       aReturn.Append(char16_t('-'));
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     aReturn.Append(char16_t('-'));
 | |
|     ++in;
 | |
|   }
 | |
| 
 | |
|   // Escape a digit at the start (including after a dash),
 | |
|   // numerically.  If we didn't escape it numerically, it would get
 | |
|   // interpreted as a numeric escape for the wrong character.
 | |
|   if (in != end && ('0' <= *in && *in <= '9')) {
 | |
|     aReturn.AppendPrintf("\\%x ", *in);
 | |
|     ++in;
 | |
|   }
 | |
| 
 | |
|   for (; in != end; ++in) {
 | |
|     char16_t ch = *in;
 | |
|     if (ch == 0x00) {
 | |
|       aReturn.Append(char16_t(0xFFFD));
 | |
|     } else if (ch < 0x20 || 0x7F == ch) {
 | |
|       // Escape U+0000 through U+001F and U+007F numerically.
 | |
|       aReturn.AppendPrintf("\\%x ", *in);
 | |
|     } else {
 | |
|       // Escape ASCII non-identifier printables as a backslash plus
 | |
|       // the character.
 | |
|       if (ch < 0x7F &&
 | |
|           ch != '_' && ch != '-' &&
 | |
|           (ch < '0' || '9' < ch) &&
 | |
|           (ch < 'A' || 'Z' < ch) &&
 | |
|           (ch < 'a' || 'z' < ch)) {
 | |
|         aReturn.Append(char16_t('\\'));
 | |
|       }
 | |
|       aReturn.Append(ch);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // unquoted family names must be a sequence of idents
 | |
| // so escape any parts that require escaping
 | |
| static void
 | |
| AppendUnquotedFamilyName(const nsAString& aFamilyName, nsAString& aResult)
 | |
| {
 | |
|   const char16_t *p, *p_end;
 | |
|   aFamilyName.BeginReading(p);
 | |
|   aFamilyName.EndReading(p_end);
 | |
| 
 | |
|    bool moreThanOne = false;
 | |
|    while (p < p_end) {
 | |
|      const char16_t* identStart = p;
 | |
|      while (++p != p_end && *p != ' ')
 | |
|        /* nothing */ ;
 | |
| 
 | |
|      nsDependentSubstring ident(identStart, p);
 | |
|      if (!ident.IsEmpty()) {
 | |
|        if (moreThanOne) {
 | |
|          aResult.Append(' ');
 | |
|        }
 | |
|        nsStyleUtil::AppendEscapedCSSIdent(ident, aResult);
 | |
|        moreThanOne = true;
 | |
|      }
 | |
| 
 | |
|      ++p;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendEscapedCSSFontFamilyList(
 | |
|   const nsTArray<mozilla::FontFamilyName>& aNames,
 | |
|   nsAString& aResult)
 | |
| {
 | |
|   size_t i, len = aNames.Length();
 | |
|   for (i = 0; i < len; i++) {
 | |
|     if (i != 0) {
 | |
|       aResult.AppendLiteral(", ");
 | |
|     }
 | |
|     const FontFamilyName& name = aNames[i];
 | |
|     switch (name.mType) {
 | |
|       case eFamily_named:
 | |
|         AppendUnquotedFamilyName(name.mName, aResult);
 | |
|         break;
 | |
|       case eFamily_named_quoted:
 | |
|         AppendEscapedCSSString(name.mName, aResult);
 | |
|         break;
 | |
|       default:
 | |
|         name.AppendToString(aResult);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendEscapedCSSFontFamilyList(
 | |
|   const mozilla::FontFamilyList& aFamilyList,
 | |
|   nsAString& aResult)
 | |
| {
 | |
|   if (aFamilyList.IsEmpty()) {
 | |
|     FontFamilyType defaultGeneric = aFamilyList.GetDefaultFontType();
 | |
|     // If the font list is empty, then serialize the default generic.
 | |
|     // See also: gfxFontGroup::BuildFontList()
 | |
|     if (defaultGeneric != eFamily_none) {
 | |
|       FontFamilyName(defaultGeneric).AppendToString(aResult);
 | |
|     } else {
 | |
|       NS_NOTREACHED("No fonts to serialize");
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   AppendEscapedCSSFontFamilyList(aFamilyList.GetFontlist().get(), aResult);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendBitmaskCSSValue(nsCSSPropertyID aProperty,
 | |
|                                    int32_t aMaskedValue,
 | |
|                                    int32_t aFirstMask,
 | |
|                                    int32_t aLastMask,
 | |
|                                    nsAString& aResult)
 | |
| {
 | |
|   for (int32_t mask = aFirstMask; mask <= aLastMask; mask <<= 1) {
 | |
|     if (mask & aMaskedValue) {
 | |
|       AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, mask),
 | |
|                          aResult);
 | |
|       aMaskedValue &= ~mask;
 | |
|       if (aMaskedValue) { // more left
 | |
|         aResult.Append(char16_t(' '));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   MOZ_ASSERT(aMaskedValue == 0, "unexpected bit remaining in bitfield");
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendAngleValue(const nsStyleCoord& aAngle, nsAString& aResult)
 | |
| {
 | |
|   MOZ_ASSERT(aAngle.IsAngleValue(), "Should have angle value");
 | |
| 
 | |
|   // Append number.
 | |
|   AppendCSSNumber(aAngle.GetAngleValue(), aResult);
 | |
| 
 | |
|   // Append unit.
 | |
|   switch (aAngle.GetUnit()) {
 | |
|     case eStyleUnit_Degree: aResult.AppendLiteral("deg");  break;
 | |
|     case eStyleUnit_Grad:   aResult.AppendLiteral("grad"); break;
 | |
|     case eStyleUnit_Radian: aResult.AppendLiteral("rad");  break;
 | |
|     case eStyleUnit_Turn:   aResult.AppendLiteral("turn"); break;
 | |
|     default: NS_NOTREACHED("unrecognized angle unit");
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendPaintOrderValue(uint8_t aValue,
 | |
|                                    nsAString& aResult)
 | |
| {
 | |
|   static_assert
 | |
|     (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8,
 | |
|      "SVGStyleStruct::mPaintOrder and local variables not big enough");
 | |
| 
 | |
|   if (aValue == NS_STYLE_PAINT_ORDER_NORMAL) {
 | |
|     aResult.AppendLiteral("normal");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Append the minimal value necessary for the given paint order.
 | |
|   static_assert(NS_STYLE_PAINT_ORDER_LAST_VALUE == 3,
 | |
|                 "paint-order values added; check serialization");
 | |
| 
 | |
|   // The following relies on the default order being the order of the
 | |
|   // constant values.
 | |
| 
 | |
|   const uint8_t MASK = (1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1;
 | |
| 
 | |
|   uint32_t lastPositionToSerialize = 0;
 | |
|   for (uint32_t position = NS_STYLE_PAINT_ORDER_LAST_VALUE - 1;
 | |
|        position > 0;
 | |
|        position--) {
 | |
|     uint8_t component =
 | |
|       (aValue >> (position * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK;
 | |
|     uint8_t earlierComponent =
 | |
|       (aValue >> ((position - 1) * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK;
 | |
|     if (component < earlierComponent) {
 | |
|       lastPositionToSerialize = position - 1;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (uint32_t position = 0; position <= lastPositionToSerialize; position++) {
 | |
|     if (position > 0) {
 | |
|       aResult.Append(' ');
 | |
|     }
 | |
|     uint8_t component = aValue & MASK;
 | |
|     switch (component) {
 | |
|       case NS_STYLE_PAINT_ORDER_FILL:
 | |
|         aResult.AppendLiteral("fill");
 | |
|         break;
 | |
| 
 | |
|       case NS_STYLE_PAINT_ORDER_STROKE:
 | |
|         aResult.AppendLiteral("stroke");
 | |
|         break;
 | |
| 
 | |
|       case NS_STYLE_PAINT_ORDER_MARKERS:
 | |
|         aResult.AppendLiteral("markers");
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         NS_NOTREACHED("unexpected paint-order component value");
 | |
|     }
 | |
|     aValue >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendFontTagAsString(uint32_t aTag, nsAString& aResult)
 | |
| {
 | |
|   // A font tag (for feature/variation settings) is a 4-char code interpreted
 | |
|   // as a bigendian 32-bit value and stored/processed as a uint32_t.
 | |
|   // To serialize it, we put the four bytes (which are all guaranteed to be
 | |
|   // printable ASCII values) into a string, starting from the high byte of the
 | |
|   // value, then append that to the result with CSS escaping and quotes.
 | |
|   nsAutoString tagStr;
 | |
|   for (int shiftAmount = 24; shiftAmount >= 0; shiftAmount -= 8) {
 | |
|     char c = (aTag >> shiftAmount) & 0xff;
 | |
|     MOZ_ASSERT(isascii(c) && isprint(c),
 | |
|                "parser should have restricted tag to printable ASCII chars");
 | |
|     tagStr.Append(c);
 | |
|   }
 | |
|   AppendEscapedCSSString(tagStr, aResult);
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendFontFeatureSettings(const nsTArray<gfxFontFeature>& aFeatures,
 | |
|                                        nsAString& aResult)
 | |
| {
 | |
|   for (uint32_t i = 0, numFeat = aFeatures.Length(); i < numFeat; i++) {
 | |
|     const gfxFontFeature& feat = aFeatures[i];
 | |
| 
 | |
|     if (i != 0) {
 | |
|       aResult.AppendLiteral(", ");
 | |
|     }
 | |
| 
 | |
|     AppendFontTagAsString(feat.mTag, aResult);
 | |
| 
 | |
|     // output value, if necessary
 | |
|     if (feat.mValue == 0) {
 | |
|       // 0 ==> off
 | |
|       aResult.AppendLiteral(" off");
 | |
|     } else if (feat.mValue > 1) {
 | |
|       aResult.Append(' ');
 | |
|       aResult.AppendInt(feat.mValue);
 | |
|     }
 | |
|     // else, omit value if 1, implied by default
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendFontFeatureSettings(const nsCSSValue& aSrc,
 | |
|                                        nsAString& aResult)
 | |
| {
 | |
|   nsCSSUnit unit = aSrc.GetUnit();
 | |
| 
 | |
|   if (unit == eCSSUnit_Normal) {
 | |
|     aResult.AppendLiteral("normal");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   NS_PRECONDITION(unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep,
 | |
|                   "improper value unit for font-feature-settings:");
 | |
| 
 | |
|   nsTArray<gfxFontFeature> featureSettings;
 | |
|   nsLayoutUtils::ComputeFontFeatures(aSrc.GetPairListValue(), featureSettings);
 | |
|   AppendFontFeatureSettings(featureSettings, aResult);
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendFontVariationSettings(const nsTArray<gfxFontVariation>& aVariations,
 | |
|                                          nsAString& aResult)
 | |
| {
 | |
|   for (uint32_t i = 0, numVars = aVariations.Length(); i < numVars; i++) {
 | |
|     const gfxFontVariation& var = aVariations[i];
 | |
| 
 | |
|     if (i != 0) {
 | |
|       aResult.AppendLiteral(", ");
 | |
|     }
 | |
| 
 | |
|     // output tag
 | |
|     AppendFontTagAsString(var.mTag, aResult);
 | |
| 
 | |
|     // output value
 | |
|     aResult.Append(' ');
 | |
|     aResult.AppendFloat(var.mValue);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendFontVariationSettings(const nsCSSValue& aSrc,
 | |
|                                          nsAString& aResult)
 | |
| {
 | |
|   nsCSSUnit unit = aSrc.GetUnit();
 | |
| 
 | |
|   if (unit == eCSSUnit_Normal) {
 | |
|     aResult.AppendLiteral("normal");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   NS_PRECONDITION(unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep,
 | |
|                   "improper value unit for font-variation-settings:");
 | |
| 
 | |
|   nsTArray<gfxFontVariation> variationSettings;
 | |
|   nsLayoutUtils::ComputeFontVariations(aSrc.GetPairListValue(),
 | |
|                                        variationSettings);
 | |
|   AppendFontVariationSettings(variationSettings, aResult);
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::GetFunctionalAlternatesName(int32_t aFeature,
 | |
|                                          nsAString& aFeatureName)
 | |
| {
 | |
|   aFeatureName.Truncate();
 | |
|   nsCSSKeyword key =
 | |
|     nsCSSProps::ValueToKeywordEnum(aFeature,
 | |
|                            nsCSSProps::kFontVariantAlternatesFuncsKTable);
 | |
| 
 | |
|   NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "bad alternate feature type");
 | |
|   AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(key), aFeatureName);
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::SerializeFunctionalAlternates(
 | |
|     const nsTArray<gfxAlternateValue>& aAlternates,
 | |
|     nsAString& aResult)
 | |
| {
 | |
|   nsAutoString funcName, funcParams;
 | |
|   uint32_t numValues = aAlternates.Length();
 | |
| 
 | |
|   uint32_t feature = 0;
 | |
|   for (uint32_t i = 0; i < numValues; i++) {
 | |
|     const gfxAlternateValue& v = aAlternates.ElementAt(i);
 | |
|     if (feature != v.alternate) {
 | |
|       feature = v.alternate;
 | |
|       if (!funcName.IsEmpty() && !funcParams.IsEmpty()) {
 | |
|         if (!aResult.IsEmpty()) {
 | |
|           aResult.Append(char16_t(' '));
 | |
|         }
 | |
| 
 | |
|         // append the previous functional value
 | |
|         aResult.Append(funcName);
 | |
|         aResult.Append(char16_t('('));
 | |
|         aResult.Append(funcParams);
 | |
|         aResult.Append(char16_t(')'));
 | |
|       }
 | |
| 
 | |
|       // function name
 | |
|       GetFunctionalAlternatesName(v.alternate, funcName);
 | |
|       NS_ASSERTION(!funcName.IsEmpty(), "unknown property value name");
 | |
| 
 | |
|       // function params
 | |
|       funcParams.Truncate();
 | |
|       AppendEscapedCSSIdent(v.value, funcParams);
 | |
|     } else {
 | |
|       if (!funcParams.IsEmpty()) {
 | |
|         funcParams.AppendLiteral(", ");
 | |
|       }
 | |
|       AppendEscapedCSSIdent(v.value, funcParams);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|     // append the previous functional value
 | |
|   if (!funcName.IsEmpty() && !funcParams.IsEmpty()) {
 | |
|     if (!aResult.IsEmpty()) {
 | |
|       aResult.Append(char16_t(' '));
 | |
|     }
 | |
| 
 | |
|     aResult.Append(funcName);
 | |
|     aResult.Append(char16_t('('));
 | |
|     aResult.Append(funcParams);
 | |
|     aResult.Append(char16_t(')'));
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::ComputeFunctionalAlternates(const nsCSSValueList* aList,
 | |
|                                   nsTArray<gfxAlternateValue>& aAlternateValues)
 | |
| {
 | |
|   gfxAlternateValue v;
 | |
| 
 | |
|   aAlternateValues.Clear();
 | |
|   for (const nsCSSValueList* curr = aList; curr != nullptr; curr = curr->mNext) {
 | |
|     // list contains function units
 | |
|     if (curr->mValue.GetUnit() != eCSSUnit_Function) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // element 0 is the propval in ident form
 | |
|     const nsCSSValue::Array *func = curr->mValue.GetArrayValue();
 | |
| 
 | |
|     // lookup propval
 | |
|     nsCSSKeyword key = func->Item(0).GetKeywordValue();
 | |
|     NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "unknown alternate property value");
 | |
| 
 | |
|     int32_t alternate;
 | |
|     if (!nsCSSProps::FindKeyword(key,
 | |
|                                  nsCSSProps::kFontVariantAlternatesFuncsKTable,
 | |
|                                  alternate)) {
 | |
|       NS_NOTREACHED("keyword not a font-variant-alternates value");
 | |
|       continue;
 | |
|     }
 | |
|     v.alternate = alternate;
 | |
| 
 | |
|     // other elements are the idents associated with the propval
 | |
|     // append one alternate value for each one
 | |
|     uint32_t numElems = func->Count();
 | |
|     for (uint32_t i = 1; i < numElems; i++) {
 | |
|       const nsCSSValue& value = func->Item(i);
 | |
|       NS_ASSERTION(value.GetUnit() == eCSSUnit_Ident,
 | |
|                    "weird unit found in variant alternate");
 | |
|       if (value.GetUnit() != eCSSUnit_Ident) {
 | |
|         continue;
 | |
|       }
 | |
|       value.GetStringValue(v.value);
 | |
|       aAlternateValues.AppendElement(v);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| AppendSerializedUnicodePoint(uint32_t aCode, nsACString& aBuf)
 | |
| {
 | |
|   aBuf.Append(nsPrintfCString("%0X", aCode));
 | |
| }
 | |
| 
 | |
| // A unicode-range: descriptor is represented as an array of integers,
 | |
| // to be interpreted as a sequence of pairs: min max min max ...
 | |
| // It is in source order.  (Possibly it should be sorted and overlaps
 | |
| // consolidated, but right now we don't do that.)
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendUnicodeRange(const nsCSSValue& aValue, nsAString& aResult)
 | |
| {
 | |
|   NS_PRECONDITION(aValue.GetUnit() == eCSSUnit_Null ||
 | |
|                   aValue.GetUnit() == eCSSUnit_Array,
 | |
|                   "improper value unit for unicode-range:");
 | |
|   aResult.Truncate();
 | |
|   if (aValue.GetUnit() != eCSSUnit_Array)
 | |
|     return;
 | |
| 
 | |
|   nsCSSValue::Array const & sources = *aValue.GetArrayValue();
 | |
|   nsAutoCString buf;
 | |
| 
 | |
|   MOZ_ASSERT(sources.Count() % 2 == 0,
 | |
|              "odd number of entries in a unicode-range: array");
 | |
| 
 | |
|   for (uint32_t i = 0; i < sources.Count(); i += 2) {
 | |
|     uint32_t min = sources[i].GetIntValue();
 | |
|     uint32_t max = sources[i+1].GetIntValue();
 | |
| 
 | |
|     // We don't try to replicate the U+XX?? notation.
 | |
|     buf.AppendLiteral("U+");
 | |
|     AppendSerializedUnicodePoint(min, buf);
 | |
| 
 | |
|     if (min != max) {
 | |
|       buf.Append('-');
 | |
|       AppendSerializedUnicodePoint(max, buf);
 | |
|     }
 | |
|     buf.AppendLiteral(", ");
 | |
|   }
 | |
|   buf.Truncate(buf.Length() - 2); // remove the last comma-space
 | |
|   CopyASCIItoUTF16(buf, aResult);
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendSerializedFontSrc(const nsCSSValue& aValue,
 | |
|                                      nsAString& aResult)
 | |
| {
 | |
|   // A src: descriptor is represented as an array value; each entry in
 | |
|   // the array can be eCSSUnit_URL, eCSSUnit_Local_Font, or
 | |
|   // eCSSUnit_Font_Format.  Blocks of eCSSUnit_Font_Format may appear
 | |
|   // only after one of the first two.  (css3-fonts only contemplates
 | |
|   // annotating URLs with formats, but we handle the general case.)
 | |
| 
 | |
|   NS_PRECONDITION(aValue.GetUnit() == eCSSUnit_Array,
 | |
|                   "improper value unit for src:");
 | |
| 
 | |
|   const nsCSSValue::Array& sources = *aValue.GetArrayValue();
 | |
|   size_t i = 0;
 | |
| 
 | |
|   while (i < sources.Count()) {
 | |
|     nsAutoString formats;
 | |
| 
 | |
|     if (sources[i].GetUnit() == eCSSUnit_URL) {
 | |
|       aResult.AppendLiteral("url(");
 | |
|       nsDependentString url(sources[i].GetOriginalURLValue());
 | |
|       nsStyleUtil::AppendEscapedCSSString(url, aResult);
 | |
|       aResult.Append(')');
 | |
|     } else if (sources[i].GetUnit() == eCSSUnit_Local_Font) {
 | |
|       aResult.AppendLiteral("local(");
 | |
|       nsDependentString local(sources[i].GetStringBufferValue());
 | |
|       nsStyleUtil::AppendEscapedCSSString(local, aResult);
 | |
|       aResult.Append(')');
 | |
|     } else {
 | |
|       NS_NOTREACHED("entry in src: descriptor with improper unit");
 | |
|       i++;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     i++;
 | |
|     formats.Truncate();
 | |
|     while (i < sources.Count() &&
 | |
|            sources[i].GetUnit() == eCSSUnit_Font_Format) {
 | |
|       formats.Append('"');
 | |
|       formats.Append(sources[i].GetStringBufferValue());
 | |
|       formats.AppendLiteral("\", ");
 | |
|       i++;
 | |
|     }
 | |
|     if (formats.Length() > 0) {
 | |
|       formats.Truncate(formats.Length() - 2); // remove the last comma
 | |
|       aResult.AppendLiteral(" format(");
 | |
|       aResult.Append(formats);
 | |
|       aResult.Append(')');
 | |
|     }
 | |
|     aResult.AppendLiteral(", ");
 | |
|   }
 | |
|   aResult.Truncate(aResult.Length() - 2); // remove the last comma-space
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendStepsTimingFunction(nsTimingFunction::Type aType,
 | |
|                                        uint32_t aSteps,
 | |
|                                        nsAString& aResult)
 | |
| {
 | |
|   MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
 | |
|              aType == nsTimingFunction::Type::StepEnd);
 | |
| 
 | |
|   aResult.AppendLiteral("steps(");
 | |
|   aResult.AppendInt(aSteps);
 | |
|   if (aType == nsTimingFunction::Type::StepStart) {
 | |
|     aResult.AppendLiteral(", start)");
 | |
|   } else {
 | |
|     aResult.AppendLiteral(")");
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendFramesTimingFunction(uint32_t aFrames,
 | |
|                                         nsAString& aResult)
 | |
| {
 | |
|   aResult.AppendLiteral("frames(");
 | |
|   aResult.AppendInt(aFrames);
 | |
|   aResult.AppendLiteral(")");
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendCubicBezierTimingFunction(float aX1, float aY1,
 | |
|                                              float aX2, float aY2,
 | |
|                                              nsAString& aResult)
 | |
| {
 | |
|   // set the value from the cubic-bezier control points
 | |
|   // (We could try to regenerate the keywords if we want.)
 | |
|   aResult.AppendLiteral("cubic-bezier(");
 | |
|   aResult.AppendFloat(aX1);
 | |
|   aResult.AppendLiteral(", ");
 | |
|   aResult.AppendFloat(aY1);
 | |
|   aResult.AppendLiteral(", ");
 | |
|   aResult.AppendFloat(aX2);
 | |
|   aResult.AppendLiteral(", ");
 | |
|   aResult.AppendFloat(aY2);
 | |
|   aResult.Append(')');
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
 | |
|     nsTimingFunction::Type aType,
 | |
|     nsAString& aResult)
 | |
| {
 | |
|   switch (aType) {
 | |
|     case nsTimingFunction::Type::Ease:
 | |
|     case nsTimingFunction::Type::Linear:
 | |
|     case nsTimingFunction::Type::EaseIn:
 | |
|     case nsTimingFunction::Type::EaseOut:
 | |
|     case nsTimingFunction::Type::EaseInOut: {
 | |
|       nsCSSKeyword keyword = nsCSSProps::ValueToKeywordEnum(
 | |
|           static_cast<int32_t>(aType),
 | |
|           nsCSSProps::kTransitionTimingFunctionKTable);
 | |
|       AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(keyword),
 | |
|                          aResult);
 | |
|       break;
 | |
|     }
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("unexpected aType");
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ float
 | |
| nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha)
 | |
| {
 | |
|   // Alpha values are expressed as decimals, so we should convert
 | |
|   // back, using as few decimal places as possible for
 | |
|   // round-tripping.
 | |
|   // First try two decimal places:
 | |
|   float rounded = NS_roundf(float(aAlpha) * 100.0f / 255.0f) / 100.0f;
 | |
|   if (FloatToColorComponent(rounded) != aAlpha) {
 | |
|     // Use three decimal places.
 | |
|     rounded = NS_roundf(float(aAlpha) * 1000.0f / 255.0f) / 1000.0f;
 | |
|   }
 | |
|   return rounded;
 | |
| }
 | |
| 
 | |
| /* static */ bool
 | |
| nsStyleUtil::IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
 | |
|                                 bool aWhitespaceIsSignificant)
 | |
| {
 | |
|   NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant,
 | |
|                "Nonsensical arguments");
 | |
| 
 | |
|   bool isText = aChild->IsNodeOfType(nsINode::eTEXT);
 | |
| 
 | |
|   if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
 | |
|       !aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
 | |
|          (aWhitespaceIsSignificant ||
 | |
|           !aChild->TextIsOnlyWhitespace());
 | |
| }
 | |
| 
 | |
| /* static */ bool
 | |
| nsStyleUtil::ThreadSafeIsSignificantChild(const nsIContent* aChild,
 | |
|                                           bool aTextIsSignificant,
 | |
|                                           bool aWhitespaceIsSignificant)
 | |
| {
 | |
|   NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant,
 | |
|                "Nonsensical arguments");
 | |
| 
 | |
|   bool isText = aChild->IsNodeOfType(nsINode::eTEXT);
 | |
| 
 | |
|   if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
 | |
|       !aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
 | |
|          (aWhitespaceIsSignificant ||
 | |
|           !aChild->ThreadSafeTextIsOnlyWhitespace());
 | |
| }
 | |
| 
 | |
| // For a replaced element whose concrete object size is no larger than the
 | |
| // element's content-box, this method checks whether the given
 | |
| // "object-position" coordinate might cause overflow in its dimension.
 | |
| static bool
 | |
| ObjectPositionCoordMightCauseOverflow(const Position::Coord& aCoord)
 | |
| {
 | |
|   // Any nonzero length in "object-position" can push us to overflow
 | |
|   // (particularly if our concrete object size is exactly the same size as the
 | |
|   // replaced element's content-box).
 | |
|   if (aCoord.mLength != 0) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Percentages are interpreted as a fraction of the extra space. So,
 | |
|   // percentages in the 0-100% range are safe, but values outside of that
 | |
|   // range could cause overflow.
 | |
|   if (aCoord.mHasPercent &&
 | |
|       (aCoord.mPercent < 0.0f || aCoord.mPercent > 1.0f)) {
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* static */ bool
 | |
| nsStyleUtil::ObjectPropsMightCauseOverflow(const nsStylePosition* aStylePos)
 | |
| {
 | |
|   auto objectFit = aStylePos->mObjectFit;
 | |
| 
 | |
|   // "object-fit: cover" & "object-fit: none" can give us a render rect that's
 | |
|   // larger than our container element's content-box.
 | |
|   if (objectFit == NS_STYLE_OBJECT_FIT_COVER ||
 | |
|       objectFit == NS_STYLE_OBJECT_FIT_NONE) {
 | |
|     return true;
 | |
|   }
 | |
|   // (All other object-fit values produce a concrete object size that's no larger
 | |
|   // than the constraint region.)
 | |
| 
 | |
|   // Check each of our "object-position" coords to see if it could cause
 | |
|   // overflow in its dimension:
 | |
|   const Position& objectPosistion = aStylePos->mObjectPosition;
 | |
|   if (ObjectPositionCoordMightCauseOverflow(objectPosistion.mXPosition) ||
 | |
|       ObjectPositionCoordMightCauseOverflow(objectPosistion.mYPosition)) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* static */ bool
 | |
| nsStyleUtil::CSPAllowsInlineStyle(nsIContent* aContent,
 | |
|                                   nsIPrincipal* aPrincipal,
 | |
|                                   nsIPrincipal* aTriggeringPrincipal,
 | |
|                                   nsIURI* aSourceURI,
 | |
|                                   uint32_t aLineNumber,
 | |
|                                   const nsAString& aStyleText,
 | |
|                                   nsresult* aRv)
 | |
| {
 | |
|   nsresult rv;
 | |
| 
 | |
|   if (aRv) {
 | |
|     *aRv = NS_OK;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(!aContent || aContent->NodeInfo()->NameAtom() == nsGkAtoms::style,
 | |
|       "aContent passed to CSPAllowsInlineStyle "
 | |
|       "for an element that is not <style>");
 | |
| 
 | |
|   nsIPrincipal* principal = aPrincipal;
 | |
|   if (aTriggeringPrincipal &&
 | |
|       BasePrincipal::Cast(aTriggeringPrincipal)->OverridesCSP(aPrincipal)) {
 | |
|     principal = aTriggeringPrincipal;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIContentSecurityPolicy> csp;
 | |
|   rv = principal->GetCsp(getter_AddRefs(csp));
 | |
| 
 | |
|   if (NS_FAILED(rv)) {
 | |
|     if (aRv)
 | |
|       *aRv = rv;
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!csp) {
 | |
|     // No CSP --> the style is allowed
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // query the nonce
 | |
|   nsAutoString nonce;
 | |
|   if (aContent) {
 | |
|     aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce);
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsISupportsString> styleText(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
 | |
|   if (styleText) {
 | |
|     styleText->SetData(aStyleText);
 | |
|   }
 | |
| 
 | |
|   bool allowInlineStyle = true;
 | |
|   rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_STYLESHEET,
 | |
|                             nonce,
 | |
|                             false, // aParserCreated only applies to scripts
 | |
|                             styleText, aLineNumber,
 | |
|                             &allowInlineStyle);
 | |
|   NS_ENSURE_SUCCESS(rv, false);
 | |
| 
 | |
|   return allowInlineStyle;
 | |
| }
 |