forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			781 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			781 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* 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 "nsRuleNode.h"
 | 
						|
#include "nsROCSSPrimitiveValue.h"
 | 
						|
#include "nsStyleStruct.h"
 | 
						|
#include "nsIContentPolicy.h"
 | 
						|
#include "nsIContentSecurityPolicy.h"
 | 
						|
#include "nsIURI.h"
 | 
						|
#include "nsPrintfCString.h"
 | 
						|
 | 
						|
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 nsSubstring& aValueList,
 | 
						|
                           const nsSubstring& 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 && *in < 0xA0)) {
 | 
						|
      // Escape U+0000 through U+001F and U+007F through U+009F numerically.
 | 
						|
      aReturn.AppendPrintf("\\%hx ", *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("\\%hx ", *in);
 | 
						|
    ++in;
 | 
						|
  }
 | 
						|
 | 
						|
  for (; in != end; ++in) {
 | 
						|
    char16_t ch = *in;
 | 
						|
    if (ch == 0x00) {
 | 
						|
      aReturn.Append(char16_t(0xFFFD));
 | 
						|
    } else if (ch < 0x20 || (0x7F <= ch && ch < 0xA0)) {
 | 
						|
      // Escape U+0000 through U+001F and U+007F through U+009F numerically.
 | 
						|
      aReturn.AppendPrintf("\\%hx ", *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 mozilla::FontFamilyList& aFamilyList,
 | 
						|
  nsAString& aResult)
 | 
						|
{
 | 
						|
  const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
 | 
						|
  size_t i, len = fontlist.Length();
 | 
						|
  for (i = 0; i < len; i++) {
 | 
						|
    if (i != 0) {
 | 
						|
      aResult.Append(',');
 | 
						|
    }
 | 
						|
    const FontFamilyName& name = fontlist[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::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::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(", ");
 | 
						|
    }
 | 
						|
 | 
						|
    // output tag
 | 
						|
    char tag[7];
 | 
						|
    tag[0] = '"';
 | 
						|
    tag[1] = (feat.mTag >> 24) & 0xff;
 | 
						|
    tag[2] = (feat.mTag >> 16) & 0xff;
 | 
						|
    tag[3] = (feat.mTag >> 8) & 0xff;
 | 
						|
    tag[4] = feat.mTag & 0xff;
 | 
						|
    tag[5] = '"';
 | 
						|
    tag[6] = 0;
 | 
						|
    aResult.AppendASCII(tag);
 | 
						|
 | 
						|
    // 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;
 | 
						|
  nsRuleNode::ComputeFontFeatures(aSrc.GetPairListValue(), featureSettings);
 | 
						|
  AppendFontFeatureSettings(featureSettings, 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 (key == eCSSKeyword_UNKNOWN ||
 | 
						|
        !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::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());
 | 
						|
}
 | 
						|
 | 
						|
// 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,
 | 
						|
                                  nsIURI* aSourceURI,
 | 
						|
                                  uint32_t aLineNumber,
 | 
						|
                                  const nsSubstring& 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>");
 | 
						|
 | 
						|
  nsCOMPtr<nsIContentSecurityPolicy> csp;
 | 
						|
  rv = aPrincipal->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);
 | 
						|
  }
 | 
						|
 | 
						|
  bool allowInlineStyle = true;
 | 
						|
  rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_STYLESHEET,
 | 
						|
                            nonce, aStyleText, aLineNumber,
 | 
						|
                            &allowInlineStyle);
 | 
						|
  NS_ENSURE_SUCCESS(rv, false);
 | 
						|
 | 
						|
  return allowInlineStyle;
 | 
						|
}
 |