text: /*
   * Copyright (C) 2013 Google Inc. All rights reserved.
   *
   *     * Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above
   * copyright notice, this list of conditions and the following disclaimer
   * in the documentation and/or other materials provided with the
   * distribution.
   *     * Neither the name of Google Inc. nor the names of its
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
  
  #include <algorithm>
  #include <utility>
  
  #include "build/build_config.h"
  #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
  #include "third_party/blink/renderer/core/css/css_axis_value.h"
  #include "third_party/blink/renderer/core/css/css_color.h"
  #include "third_party/blink/renderer/core/css/css_content_distribution_value.h"
  #include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
  #include "third_party/blink/renderer/core/css/css_font_family_value.h"
  #include "third_party/blink/renderer/core/css/css_font_feature_value.h"
  #include "third_party/blink/renderer/core/css/css_font_style_range_value.h"
  #include "third_party/blink/renderer/core/css/css_font_variation_value.h"
  #include "third_party/blink/renderer/core/css/css_grid_auto_repeat_value.h"
  #include "third_party/blink/renderer/core/css/css_grid_integer_repeat_value.h"
  #include "third_party/blink/renderer/core/css/css_math_expression_node.h"
  #include "third_party/blink/renderer/core/css/css_math_function_value.h"
  #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
  #include "third_party/blink/renderer/core/css/css_path_value.h"
  #include "third_party/blink/renderer/core/css/css_pending_system_font_value.h"
  #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
  #include "third_party/blink/renderer/core/css/css_quad_value.h"
  #include "third_party/blink/renderer/core/css/css_reflect_value.h"
  #include "third_party/blink/renderer/core/css/css_shadow_value.h"
  #include "third_party/blink/renderer/core/css/css_uri_value.h"
  #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
  #include "third_party/blink/renderer/core/css/resolver/filter_operation_resolver.h"
  #include "third_party/blink/renderer/core/css/resolver/transform_builder.h"
  #include "third_party/blink/renderer/core/css/style_engine.h"
  #include "third_party/blink/renderer/core/frame/local_frame.h"
  #include "third_party/blink/renderer/core/frame/web_feature.h"
  #include "third_party/blink/renderer/core/style/reference_clip_path_operation.h"
  #include "third_party/blink/renderer/core/style/shape_clip_path_operation.h"
  #include "third_party/blink/renderer/core/style/style_svg_resource.h"
  #include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h"
  #include "third_party/blink/renderer/platform/heap/heap.h"
  #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
  #include "third_party/blink/renderer/platform/wtf/hash_map.h"
  #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
  
  namespace blink {
  
  namespace {
  
  static GridLength ConvertGridTrackBreadth(const StyleResolverState& state,
                                            const CSSValue& value) {
    // Fractional unit.
    auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value);
    if (primitive_value && primitive_value->IsFlex())
      return GridLength(primitive_value->GetDoubleValue());
  
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value) {
      if (identifier_value->GetValueID() == CSSValueID::kMinContent)
        return Length::MinContent();
      if (identifier_value->GetValueID() == CSSValueID::kMaxContent)
        return Length::MaxContent();
    }
  
    return StyleBuilderConverter::ConvertLengthOrAuto(state, value);
  }
  
  }  // namespace
  
  scoped_refptr<StyleReflection> StyleBuilderConverter::ConvertBoxReflect(
      StyleResolverState& state,
      const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return ComputedStyleInitialValues::InitialBoxReflect();
    }
  
    const auto& reflect_value = To<cssvalue::CSSReflectValue>(value);
    scoped_refptr<StyleReflection> reflection = StyleReflection::Create();
    reflection->SetDirection(
        reflect_value.Direction()->ConvertTo<CSSReflectionDirection>());
    if (reflect_value.Offset())
      reflection->SetOffset(reflect_value.Offset()->ConvertToLength(
          state.CssToLengthConversionData()));
    if (reflect_value.Mask()) {
      NinePieceImage mask = NinePieceImage::MaskDefaults();
      CSSToStyleMap::MapNinePieceImage(state, CSSPropertyID::kWebkitBoxReflect,
                                       *reflect_value.Mask(), mask);
      reflection->SetMask(mask);
    }
  
    return reflection;
  }
  
  scoped_refptr<StyleSVGResource> StyleBuilderConverter::ConvertElementReference(
      StyleResolverState& state,
      const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return nullptr;
    }
  
    const auto& url_value = To<cssvalue::CSSURIValue>(value);
    SVGResource* resource =
        state.GetElementStyleResources().GetSVGResourceFromValue(
            CSSPropertyID::kInvalid, url_value);
    return StyleSVGResource::Create(resource, url_value.ValueForSerialization());
  }
  
  LengthBox StyleBuilderConverter::ConvertClip(StyleResolverState& state,
                                               const CSSValue& value) {
    const CSSQuadValue& rect = To<CSSQuadValue>(value);
  
    return LengthBox(ConvertLengthOrAuto(state, *rect.Top()),
                     ConvertLengthOrAuto(state, *rect.Right()),
                     ConvertLengthOrAuto(state, *rect.Bottom()),
                     ConvertLengthOrAuto(state, *rect.Left()));
  }
  
  scoped_refptr<ClipPathOperation> StyleBuilderConverter::ConvertClipPath(
      StyleResolverState& state,
      const CSSValue& value) {
    if (value.IsBasicShapeValue() || value.IsPathValue())
      return ShapeClipPathOperation::Create(BasicShapeForValue(state, value));
  
    if (const auto* url_value = DynamicTo<cssvalue::CSSURIValue>(value)) {
      SVGResource* resource =
          state.GetElementStyleResources().GetSVGResourceFromValue(
              CSSPropertyID::kClipPath, *url_value);
      // TODO(fs): Doesn't work with external SVG references (crbug.com/109212.)
      return ReferenceClipPathOperation::Create(
          url_value->ValueForSerialization(), resource);
    }
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    DCHECK(identifier_value &&
           identifier_value->GetValueID() == CSSValueID::kNone);
    return nullptr;
  }
  
  FilterOperations StyleBuilderConverter::ConvertFilterOperations(
      StyleResolverState& state,
      const CSSValue& value,
      CSSPropertyID property_id) {
    return FilterOperationResolver::CreateFilterOperations(state, value,
                                                           property_id);
  }
  
  FilterOperations StyleBuilderConverter::ConvertOffscreenFilterOperations(
      const CSSValue& value,
      const Font& font) {
    return FilterOperationResolver::CreateOffscreenFilterOperations(value, font);
  }
  
  static FontDescription::GenericFamilyType ConvertGenericFamily(
      CSSValueID value_id) {
    switch (value_id) {
      case CSSValueID::kWebkitBody:
        return FontDescription::kStandardFamily;
      case CSSValueID::kSerif:
        return FontDescription::kSerifFamily;
      case CSSValueID::kSansSerif:
        return FontDescription::kSansSerifFamily;
      case CSSValueID::kCursive:
        return FontDescription::kCursiveFamily;
      case CSSValueID::kFantasy:
        return FontDescription::kFantasyFamily;
      case CSSValueID::kMonospace:
        return FontDescription::kMonospaceFamily;
      default:
        return FontDescription::kNoFamily;
    }
  }
  
  static bool ConvertFontFamilyName(
      const CSSValue& value,
      FontDescription::GenericFamilyType& generic_family,
      AtomicString& family_name,
      FontBuilder* font_builder,
      const Document* document_for_count) {
    if (auto* font_family_value = DynamicTo<CSSFontFamilyValue>(value)) {
      generic_family = FontDescription::kNoFamily;
      family_name = AtomicString(font_family_value->Value());
  #if defined(OS_MAC)
      if (family_name == FontCache::LegacySystemFontFamily()) {
        document_for_count->CountUse(WebFeature::kBlinkMacSystemFont);
        family_name = font_family_names::kSystemUi;
      }
  #endif
    } else if (font_builder) {
      generic_family =
          ConvertGenericFamily(To<CSSIdentifierValue>(value).GetValueID());
      family_name = font_builder->GenericFontFamilyName(generic_family);
    }
  
    return !family_name.IsEmpty();
  }
  
  FontDescription::FamilyDescription StyleBuilderConverterBase::ConvertFontFamily(
      const CSSValue& value,
      FontBuilder* font_builder,
      const Document* document_for_count) {
    FontDescription::FamilyDescription desc(FontDescription::kNoFamily);
  
    if (const auto* system_font =
            DynamicTo<cssvalue::CSSPendingSystemFontValue>(value)) {
      desc.family.SetFamily(system_font->ResolveFontFamily());
      return desc;
    }
  
    FontFamily* curr_family = nullptr;
  
    for (auto& family : To<CSSValueList>(value)) {
      FontDescription::GenericFamilyType generic_family =
          FontDescription::kNoFamily;
      AtomicString family_name;
  
      if (!ConvertFontFamilyName(*family, generic_family, family_name,
                                 font_builder, document_for_count))
        continue;
  
      if (!curr_family) {
        curr_family = &desc.family;
      } else {
        scoped_refptr<SharedFontFamily> new_family = SharedFontFamily::Create();
        curr_family->AppendFamily(new_family);
        curr_family = new_family.get();
      }
  
      curr_family->SetFamily(family_name);
  
      if (generic_family != FontDescription::kNoFamily)
        desc.generic_family = generic_family;
    }
  
    return desc;
  }
  
  FontDescription::FamilyDescription StyleBuilderConverter::ConvertFontFamily(
      StyleResolverState& state,
      const CSSValue& value) {
    return StyleBuilderConverterBase::ConvertFontFamily(
        value,
        state.GetDocument().GetSettings() ? &state.GetFontBuilder() : nullptr,
        &state.GetDocument());
  }
  
  scoped_refptr<FontFeatureSettings>
  StyleBuilderConverter::ConvertFontFeatureSettings(StyleResolverState& state,
                                                    const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kNormal)
      return FontBuilder::InitialFeatureSettings();
  
    const auto& list = To<CSSValueList>(value);
    scoped_refptr<FontFeatureSettings> settings = FontFeatureSettings::Create();
    int len = list.length();
    for (int i = 0; i < len; ++i) {
      const auto& feature = To<cssvalue::CSSFontFeatureValue>(list.Item(i));
      settings->Append(FontFeature(feature.Tag(), feature.Value()));
    }
    return settings;
  }
  
  static bool CompareTags(FontVariationAxis a, FontVariationAxis b) {
    return a.Tag() < b.Tag();
  }
  
  scoped_refptr<FontVariationSettings>
  StyleBuilderConverter::ConvertFontVariationSettings(
      const StyleResolverState& state,
      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kNormal)
      return FontBuilder::InitialVariationSettings();
  
    const auto& list = To<CSSValueList>(value);
    int len = list.length();
    HashMap<uint32_t, float> axes;
    // Use a temporary HashMap to remove duplicate tags, keeping the last
    // occurrence of each.
    for (int i = 0; i < len; ++i) {
      const auto& feature = To<cssvalue::CSSFontVariationValue>(list.Item(i));
      axes.Set(AtomicStringToFourByteTag(feature.Tag()), feature.Value());
    }
    scoped_refptr<FontVariationSettings> settings =
        FontVariationSettings::Create();
    for (auto& axis : axes) {
      settings->Append(FontVariationAxis(axis.key, axis.value));
    }
    std::sort(settings->begin(), settings->end(), CompareTags);
    return settings;
  }
  
  float MathScriptScaleFactor(StyleResolverState& state) {
    int a = state.ParentStyle()->MathDepth();
    int b = state.Style()->MathDepth();
    if (b == a)
      return 1.0;
    bool invertScaleFactor = false;
    if (b < a) {
      std::swap(a, b);
      invertScaleFactor = true;
    }
  
    // Determine the scale factors from the inherited font.
    float defaultScaleDown = 0.71;
    int exponent = b - a;
    float scaleFactor = 1.0;
    if (const SimpleFontData* font_data =
            state.ParentStyle()->GetFont().PrimaryFont()) {
      HarfBuzzFace* parent_harfbuzz_face =
          font_data->PlatformData().GetHarfBuzzFace();
      if (OpenTypeMathSupport::HasMathData(parent_harfbuzz_face)) {
        float scriptPercentScaleDown =
            OpenTypeMathSupport::MathConstant(
                parent_harfbuzz_face,
                OpenTypeMathSupport::MathConstants::kScriptPercentScaleDown)
                .value_or(0);
        // Note: zero can mean both zero for the math constant and the fallback.
        if (!scriptPercentScaleDown)
          scriptPercentScaleDown = defaultScaleDown;
        float scriptScriptPercentScaleDown =
            OpenTypeMathSupport::MathConstant(
                parent_harfbuzz_face,
                OpenTypeMathSupport::MathConstants::kScriptScriptPercentScaleDown)
                .value_or(0);
        // Note: zero can mean both zero for the math constant and the fallback.
        if (!scriptScriptPercentScaleDown)
          scriptScriptPercentScaleDown = defaultScaleDown * defaultScaleDown;
        if (a <= 0 && b >= 2) {
          scaleFactor *= scriptScriptPercentScaleDown;
          exponent -= 2;
        } else if (a == 1) {
          scaleFactor *= scriptScriptPercentScaleDown / scriptPercentScaleDown;
          exponent--;
        } else if (b == 1) {
          scaleFactor *= scriptPercentScaleDown;
          exponent--;
        }
      }
    }
    scaleFactor *= pow(defaultScaleDown, exponent);
    return invertScaleFactor ? 1 / scaleFactor : scaleFactor;
  }
  
  static float ComputeFontSize(const CSSToLengthConversionData& conversion_data,
                               const CSSPrimitiveValue& primitive_value,
                               const FontDescription::Size& parent_size) {
    if (primitive_value.IsLength()) {
      float result = primitive_value.ComputeLength<float>(conversion_data);
      float font_size_zoom = conversion_data.FontSizeZoom();
      // TODO(crbug.com/408777): Only accounting for numeric literal value here
      // will leave calc() without zoom correction.
      if (primitive_value.IsNumericLiteralValue() && font_size_zoom != 1) {
        CSSPrimitiveValue::UnitType type =
            To<CSSNumericLiteralValue>(&primitive_value)->GetType();
        if (type == CSSPrimitiveValue::UnitType::kChs ||
            type == CSSPrimitiveValue::UnitType::kExs) {
          return result / font_size_zoom;
        }
      }
      return result;
    }
    if (primitive_value.IsCalculated()) {
      return To<CSSMathFunctionValue>(primitive_value)
          .ToCalcValue(conversion_data)
          ->Evaluate(parent_size.value);
    }
    NOTREACHED();
    return 0;
  }
  
  FontDescription::Size StyleBuilderConverterBase::ConvertFontSize(
      const CSSValue& value,
      const CSSToLengthConversionData& conversion_data,
      FontDescription::Size parent_size,
      const Document* document) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      CSSValueID value_id = identifier_value->GetValueID();
      if (FontSizeFunctions::IsValidValueID(value_id)) {
        return FontDescription::Size(FontSizeFunctions::KeywordSize(value_id),
                                     0.0f, false);
      }
      if (value_id == CSSValueID::kSmaller)
        return FontDescription::SmallerSize(parent_size);
      if (value_id == CSSValueID::kLarger)
        return FontDescription::LargerSize(parent_size);
      NOTREACHED();
      return FontBuilder::InitialSize();
    }
  
    if (const auto* system_font =
            DynamicTo<cssvalue::CSSPendingSystemFontValue>(value)) {
      return FontDescription::Size(0, system_font->ResolveFontSize(document),
                                   true);
    }
  
    const auto& primitive_value = To<CSSPrimitiveValue>(value);
    if (primitive_value.IsPercentage()) {
      return FontDescription::Size(
          0, (primitive_value.GetFloatValue() * parent_size.value / 100.0f),
          parent_size.is_absolute);
    }
  
    // TODO(crbug.com/979895): This is the result of a refactoring, which might
    // have revealed an existing bug with calculated lengths. Investigate.
    const bool is_absolute =
        parent_size.is_absolute || primitive_value.IsMathFunctionValue() ||
        !To<CSSNumericLiteralValue>(primitive_value).IsFontRelativeLength() ||
        To<CSSNumericLiteralValue>(primitive_value).GetType() ==
            CSSPrimitiveValue::UnitType::kRems;
    return FontDescription::Size(
        0, ComputeFontSize(conversion_data, primitive_value, parent_size),
        is_absolute);
  }
  
  FontDescription::Size StyleBuilderConverter::ConvertFontSize(
      StyleResolverState& state,
      const CSSValue& value) {
    // FIXME: Find out when parentStyle could be 0?
    auto parent_size = state.ParentStyle()
                           ? state.ParentFontDescription().GetSize()
                           : FontDescription::Size(0, 0.0f, false);
  
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kMath) {
      auto scale_factor = MathScriptScaleFactor(state);
      state.Style()->SetHasGlyphRelativeUnits();
      return FontDescription::Size(0, (scale_factor * parent_size.value),
                                   parent_size.is_absolute);
    }
  
    return StyleBuilderConverterBase::ConvertFontSize(
        value, state.FontSizeConversionData(), parent_size, &state.GetDocument());
  }
  
  float StyleBuilderConverter::ConvertFontSizeAdjust(StyleResolverState& state,
                                                     const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kNone)
      return FontBuilder::InitialSizeAdjust();
  
    const auto& primitive_value = To<CSSPrimitiveValue>(value);
    DCHECK(primitive_value.IsNumber());
    return primitive_value.GetFloatValue();
  }
  
  FontSelectionValue StyleBuilderConverterBase::ConvertFontStretch(
      const blink::CSSValue& value) {
    if (const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value)) {
      if (primitive_value->IsPercentage())
        return clampTo<FontSelectionValue>(primitive_value->GetFloatValue());
    }
  
    // TODO(drott) crbug.com/750014: Consider not parsing them as IdentifierValue
    // any more?
    if (const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      switch (identifier_value->GetValueID()) {
        case CSSValueID::kUltraCondensed:
          return UltraCondensedWidthValue();
        case CSSValueID::kExtraCondensed:
          return ExtraCondensedWidthValue();
        case CSSValueID::kCondensed:
          return CondensedWidthValue();
        case CSSValueID::kSemiCondensed:
          return SemiCondensedWidthValue();
        case CSSValueID::kNormal:
          return NormalWidthValue();
        case CSSValueID::kSemiExpanded:
          return SemiExpandedWidthValue();
        case CSSValueID::kExpanded:
          return ExpandedWidthValue();
        case CSSValueID::kExtraExpanded:
          return ExtraExpandedWidthValue();
        case CSSValueID::kUltraExpanded:
          return UltraExpandedWidthValue();
        default:
          break;
      }
    }
  
    if (value.IsPendingSystemFontValue())
      return NormalWidthValue();
  
    NOTREACHED();
    return NormalWidthValue();
  }
  
  FontSelectionValue StyleBuilderConverter::ConvertFontStretch(
      blink::StyleResolverState& state,
      const blink::CSSValue& value) {
    return StyleBuilderConverterBase::ConvertFontStretch(value);
  }
  
  FontSelectionValue StyleBuilderConverterBase::ConvertFontStyle(
      const CSSValue& value) {
    DCHECK(!value.IsPrimitiveValue());
  
    if (const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      switch (identifier_value->GetValueID()) {
        case CSSValueID::kItalic:
        case CSSValueID::kOblique:
          return ItalicSlopeValue();
        case CSSValueID::kNormal:
          return NormalSlopeValue();
        default:
          NOTREACHED();
          return NormalSlopeValue();
      }
    } else if (const auto* system_font =
                   DynamicTo<cssvalue::CSSPendingSystemFontValue>(value)) {
      if (system_font->ResolveFontStyle() == ItalicSlopeValue())
        return ItalicSlopeValue();
      return NormalSlopeValue();
    } else if (const auto* style_range_value =
                   DynamicTo<cssvalue::CSSFontStyleRangeValue>(value)) {
      const CSSValueList* values = style_range_value->GetObliqueValues();
      CHECK_LT(values->length(), 2u);
      if (values->length()) {
        return FontSelectionValue(
            To<CSSPrimitiveValue>(values->Item(0)).ComputeDegrees());
      } else {
        identifier_value = style_range_value->GetFontStyleValue();
        if (identifier_value->GetValueID() == CSSValueID::kNormal)
          return NormalSlopeValue();
        if (identifier_value->GetValueID() == CSSValueID::kItalic ||
            identifier_value->GetValueID() == CSSValueID::kOblique)
          return ItalicSlopeValue();
      }
    }
  
    NOTREACHED();
    return NormalSlopeValue();
  }
  
  FontSelectionValue StyleBuilderConverter::ConvertFontStyle(
      StyleResolverState& state,
      const CSSValue& value) {
    return StyleBuilderConverterBase::ConvertFontStyle(value);
  }
  
  FontSelectionValue StyleBuilderConverterBase::ConvertFontWeight(
      const CSSValue& value,
      FontSelectionValue parent_weight) {
    if (const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value)) {
      if (primitive_value->IsNumber())
        return clampTo<FontSelectionValue>(primitive_value->GetFloatValue());
    }
  
    if (const auto* system_font =
            DynamicTo<cssvalue::CSSPendingSystemFontValue>(value))
      return system_font->ResolveFontWeight();
  
    if (const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      switch (identifier_value->GetValueID()) {
        case CSSValueID::kNormal:
          return NormalWeightValue();
        case CSSValueID::kBold:
          return BoldWeightValue();
        case CSSValueID::kBolder:
          return FontDescription::BolderWeight(parent_weight);
        case CSSValueID::kLighter:
          return FontDescription::LighterWeight(parent_weight);
        default:
          NOTREACHED();
          return NormalWeightValue();
      }
    }
    NOTREACHED();
    return NormalWeightValue();
  }
  
  FontSelectionValue StyleBuilderConverter::ConvertFontWeight(
      StyleResolverState& state,
      const CSSValue& value) {
    return StyleBuilderConverterBase::ConvertFontWeight(
        value, state.ParentStyle()->GetFontDescription().Weight());
  }
  
  FontDescription::FontVariantCaps
  StyleBuilderConverterBase::ConvertFontVariantCaps(const CSSValue& value) {
    if (value.IsPendingSystemFontValue())
      return FontDescription::kCapsNormal;
  
    CSSValueID value_id = To<CSSIdentifierValue>(value).GetValueID();
    switch (value_id) {
      case CSSValueID::kNormal:
        return FontDescription::kCapsNormal;
      case CSSValueID::kSmallCaps:
        return FontDescription::kSmallCaps;
      case CSSValueID::kAllSmallCaps:
        return FontDescription::kAllSmallCaps;
      case CSSValueID::kPetiteCaps:
        return FontDescription::kPetiteCaps;
      case CSSValueID::kAllPetiteCaps:
        return FontDescription::kAllPetiteCaps;
      case CSSValueID::kUnicase:
        return FontDescription::kUnicase;
      case CSSValueID::kTitlingCaps:
        return FontDescription::kTitlingCaps;
      default:
        return FontDescription::kCapsNormal;
    }
  }
  
  FontDescription::FontVariantCaps StyleBuilderConverter::ConvertFontVariantCaps(
      StyleResolverState&,
      const CSSValue& value) {
    return StyleBuilderConverterBase::ConvertFontVariantCaps(value);
  }
  
  FontDescription::VariantLigatures
  StyleBuilderConverter::ConvertFontVariantLigatures(StyleResolverState&,
                                                     const CSSValue& value) {
    if (const auto* value_list = DynamicTo<CSSValueList>(value)) {
      FontDescription::VariantLigatures ligatures;
      for (wtf_size_t i = 0; i < value_list->length(); ++i) {
        const CSSValue& item = value_list->Item(i);
        switch (To<CSSIdentifierValue>(item).GetValueID()) {
          case CSSValueID::kNoCommonLigatures:
            ligatures.common = FontDescription::kDisabledLigaturesState;
            break;
          case CSSValueID::kCommonLigatures:
            ligatures.common = FontDescription::kEnabledLigaturesState;
            break;
          case CSSValueID::kNoDiscretionaryLigatures:
            ligatures.discretionary = FontDescription::kDisabledLigaturesState;
            break;
          case CSSValueID::kDiscretionaryLigatures:
            ligatures.discretionary = FontDescription::kEnabledLigaturesState;
            break;
          case CSSValueID::kNoHistoricalLigatures:
            ligatures.historical = FontDescription::kDisabledLigaturesState;
            break;
          case CSSValueID::kHistoricalLigatures:
            ligatures.historical = FontDescription::kEnabledLigaturesState;
            break;
          case CSSValueID::kNoContextual:
            ligatures.contextual = FontDescription::kDisabledLigaturesState;
            break;
          case CSSValueID::kContextual:
            ligatures.contextual = FontDescription::kEnabledLigaturesState;
            break;
          default:
            NOTREACHED();
            break;
        }
      }
      return ligatures;
    }
  
    if (value.IsPendingSystemFontValue())
      return FontDescription::VariantLigatures();
  
    if (To<CSSIdentifierValue>(value).GetValueID() == CSSValueID::kNone) {
      return FontDescription::VariantLigatures(
          FontDescription::kDisabledLigaturesState);
    }
  
    DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kNormal);
    return FontDescription::VariantLigatures();
  }
  
  FontVariantNumeric StyleBuilderConverter::ConvertFontVariantNumeric(
      StyleResolverState&,
      const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNormal);
      return FontVariantNumeric();
    }
  
    if (value.IsPendingSystemFontValue())
      return FontVariantNumeric();
  
    FontVariantNumeric variant_numeric;
    for (const CSSValue* feature : To<CSSValueList>(value)) {
      switch (To<CSSIdentifierValue>(feature)->GetValueID()) {
        case CSSValueID::kLiningNums:
          variant_numeric.SetNumericFigure(FontVariantNumeric::kLiningNums);
          break;
        case CSSValueID::kOldstyleNums:
          variant_numeric.SetNumericFigure(FontVariantNumeric::kOldstyleNums);
          break;
        case CSSValueID::kProportionalNums:
          variant_numeric.SetNumericSpacing(
              FontVariantNumeric::kProportionalNums);
          break;
        case CSSValueID::kTabularNums:
          variant_numeric.SetNumericSpacing(FontVariantNumeric::kTabularNums);
          break;
        case CSSValueID::kDiagonalFractions:
          variant_numeric.SetNumericFraction(
              FontVariantNumeric::kDiagonalFractions);
          break;
        case CSSValueID::kStackedFractions:
          variant_numeric.SetNumericFraction(
              FontVariantNumeric::kStackedFractions);
          break;
        case CSSValueID::kOrdinal:
          variant_numeric.SetOrdinal(FontVariantNumeric::kOrdinalOn);
          break;
        case CSSValueID::kSlashedZero:
          variant_numeric.SetSlashedZero(FontVariantNumeric::kSlashedZeroOn);
          break;
        default:
          NOTREACHED();
          break;
      }
    }
    return variant_numeric;
  }
  
  FontVariantEastAsian StyleBuilderConverter::ConvertFontVariantEastAsian(
      StyleResolverState&,
      const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNormal);
      return FontVariantEastAsian();
    }
  
    if (value.IsPendingSystemFontValue())
      return FontVariantEastAsian();
  
    FontVariantEastAsian variant_east_asian;
    for (const CSSValue* feature : To<CSSValueList>(value)) {
      switch (To<CSSIdentifierValue>(feature)->GetValueID()) {
        case CSSValueID::kJis78:
          variant_east_asian.SetForm(FontVariantEastAsian::kJis78);
          break;
        case CSSValueID::kJis83:
          variant_east_asian.SetForm(FontVariantEastAsian::kJis83);
          break;
        case CSSValueID::kJis90:
          variant_east_asian.SetForm(FontVariantEastAsian::kJis90);
          break;
        case CSSValueID::kJis04:
          variant_east_asian.SetForm(FontVariantEastAsian::kJis04);
          break;
        case CSSValueID::kSimplified:
          variant_east_asian.SetForm(FontVariantEastAsian::kSimplified);
          break;
        case CSSValueID::kTraditional:
          variant_east_asian.SetForm(FontVariantEastAsian::kTraditional);
          break;
        case CSSValueID::kFullWidth:
          variant_east_asian.SetWidth(FontVariantEastAsian::kFullWidth);
          break;
        case CSSValueID::kProportionalWidth:
          variant_east_asian.SetWidth(FontVariantEastAsian::kProportionalWidth);
          break;
        case CSSValueID::kRuby:
          variant_east_asian.SetRuby(true);
          break;
        default:
          NOTREACHED();
          break;
      }
    }
    return variant_east_asian;
  }
  
  StyleSelfAlignmentData StyleBuilderConverter::ConvertSelfOrDefaultAlignmentData(
      StyleResolverState&,
      const CSSValue& value) {
    StyleSelfAlignmentData alignment_data =
        ComputedStyleInitialValues::InitialAlignSelf();
    if (const auto* pair = DynamicTo<CSSValuePair>(value)) {
      if (To<CSSIdentifierValue>(pair->First()).GetValueID() ==
          CSSValueID::kLegacy) {
        alignment_data.SetPositionType(ItemPositionType::kLegacy);
        alignment_data.SetPosition(
            To<CSSIdentifierValue>(pair->Second()).ConvertTo<ItemPosition>());
      } else if (To<CSSIdentifierValue>(pair->First()).GetValueID() ==
                 CSSValueID::kFirst) {
        alignment_data.SetPosition(ItemPosition::kBaseline);
      } else if (To<CSSIdentifierValue>(pair->First()).GetValueID() ==
                 CSSValueID::kLast) {
        alignment_data.SetPosition(ItemPosition::kLastBaseline);
      } else {
        alignment_data.SetOverflow(
            To<CSSIdentifierValue>(pair->First()).ConvertTo<OverflowAlignment>());
        alignment_data.SetPosition(
            To<CSSIdentifierValue>(pair->Second()).ConvertTo<ItemPosition>());
      }
    } else {
      alignment_data.SetPosition(
          To<CSSIdentifierValue>(value).ConvertTo<ItemPosition>());
    }
    return alignment_data;
  }
  
  StyleContentAlignmentData StyleBuilderConverter::ConvertContentAlignmentData(
      StyleResolverState&,
      const CSSValue& value) {
    StyleContentAlignmentData alignment_data =
        ComputedStyleInitialValues::InitialContentAlignment();
    const cssvalue::CSSContentDistributionValue& content_value =
        To<cssvalue::CSSContentDistributionValue>(value);
    if (IsValidCSSValueID(content_value.Distribution())) {
      alignment_data.SetDistribution(
          CSSIdentifierValue::Create(content_value.Distribution())
              ->ConvertTo<ContentDistributionType>());
    }
    if (IsValidCSSValueID(content_value.Position())) {
      alignment_data.SetPosition(
          CSSIdentifierValue::Create(content_value.Position())
              ->ConvertTo<ContentPosition>());
    }
    if (IsValidCSSValueID(content_value.Overflow())) {
      alignment_data.SetOverflow(
          CSSIdentifierValue::Create(content_value.Overflow())
              ->ConvertTo<OverflowAlignment>());
    }
  
    return alignment_data;
  }
  
  GridAutoFlow StyleBuilderConverter::ConvertGridAutoFlow(StyleResolverState&,
                                                          const CSSValue& value) {
    const auto& list = To<CSSValueList>(value);
  
    DCHECK_GE(list.length(), 1u);
    const CSSIdentifierValue& first = To<CSSIdentifierValue>(list.Item(0));
    const CSSIdentifierValue* second =
        list.length() == 2 ? &To<CSSIdentifierValue>(list.Item(1)) : nullptr;
  
    switch (first.GetValueID()) {
      case CSSValueID::kRow:
        if (second && second->GetValueID() == CSSValueID::kDense)
          return kAutoFlowRowDense;
        return kAutoFlowRow;
      case CSSValueID::kColumn:
        if (second && second->GetValueID() == CSSValueID::kDense)
          return kAutoFlowColumnDense;
        return kAutoFlowColumn;
      case CSSValueID::kDense:
        if (second && second->GetValueID() == CSSValueID::kColumn)
          return kAutoFlowColumnDense;
        return kAutoFlowRowDense;
      default:
        NOTREACHED();
        return ComputedStyleInitialValues::InitialGridAutoFlow();
    }
  }
  
  GridPosition StyleBuilderConverter::ConvertGridPosition(StyleResolverState&,
                                                          const CSSValue& value) {
    // We accept the specification's grammar:
    // 'auto' | [ <integer> || <custom-ident> ] |
    // [ span && [ <integer> || <custom-ident> ] ] | <custom-ident>
  
    GridPosition position;
  
    if (auto* ident_value = DynamicTo<CSSCustomIdentValue>(value)) {
      position.SetNamedGridArea(ident_value->Value());
      return position;
    }
  
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kAuto);
      return position;
    }
  
    const auto& values = To<CSSValueList>(value);
    DCHECK(values.length());
  
    bool is_span_position = false;
    // The specification makes the <integer> optional, in which case it default to
    // '1'.
    int grid_line_number = 1;
    AtomicString grid_line_name;
  
    auto* it = values.begin();
    const CSSValue* current_value = it->Get();
    auto* current_identifier_value = DynamicTo<CSSIdentifierValue>(current_value);
    if (current_identifier_value &&
        current_identifier_value->GetValueID() == CSSValueID::kSpan) {
      is_span_position = true;
      ++it;
      current_value = it != values.end() ? it->Get() : nullptr;
    }
  
    auto* current_primitive_value = DynamicTo<CSSPrimitiveValue>(current_value);
    if (current_primitive_value && current_primitive_value->IsNumber()) {
      grid_line_number = current_primitive_value->GetIntValue();
      ++it;
      current_value = it != values.end() ? it->Get() : nullptr;
    }
  
    auto* current_ident_value = DynamicTo<CSSCustomIdentValue>(current_value);
    if (current_ident_value) {
      grid_line_name = current_ident_value->Value();
      ++it;
    }
  
    DCHECK_EQ(it, values.end());
    if (is_span_position)
      position.SetSpanPosition(grid_line_number, grid_line_name);
    else
      position.SetExplicitPosition(grid_line_number, grid_line_name);
  
    return position;
  }
  
  GridTrackSize StyleBuilderConverter::ConvertGridTrackSize(
      StyleResolverState& state,
      const CSSValue& value) {
    if (value.IsPrimitiveValue() || value.IsIdentifierValue())
      return GridTrackSize(ConvertGridTrackBreadth(state, value));
  
    auto& function = To<CSSFunctionValue>(value);
    if (function.FunctionType() == CSSValueID::kFitContent) {
      SECURITY_DCHECK(function.length() == 1);
      return GridTrackSize(ConvertGridTrackBreadth(state, function.Item(0)),
                           kFitContentTrackSizing);
    }
  
    SECURITY_DCHECK(function.length() == 2);
    GridLength min_track_breadth(
        ConvertGridTrackBreadth(state, function.Item(0)));
    GridLength max_track_breadth(
        ConvertGridTrackBreadth(state, function.Item(1)));
    return GridTrackSize(min_track_breadth, max_track_breadth);
  }
  
  static void ConvertGridLineNamesList(
      const CSSValue& value,
      size_t current_named_grid_line,
      NamedGridLinesMap& named_grid_lines,
      OrderedNamedGridLines& ordered_named_grid_lines) {
    DCHECK(value.IsGridLineNamesValue());
  
    for (auto& named_grid_line_value : To<CSSValueList>(value)) {
      String named_grid_line =
          To<CSSCustomIdentValue>(*named_grid_line_value).Value();
      NamedGridLinesMap::AddResult result =
          named_grid_lines.insert(named_grid_line, Vector<size_t>());
      result.stored_value->value.push_back(current_named_grid_line);
      OrderedNamedGridLines::AddResult ordered_insertion_result =
          ordered_named_grid_lines.insert(current_named_grid_line,
                                          Vector<String>());
      ordered_insertion_result.stored_value->value.push_back(named_grid_line);
    }
  }
  
  GridTrackList StyleBuilderConverter::ConvertGridTrackSizeList(
      StyleResolverState& state,
      const CSSValue& value) {
    Vector<GridTrackSize> track_sizes;
    for (auto& curr_value : To<CSSValueList>(value)) {
      DCHECK(!curr_value->IsGridLineNamesValue());
      DCHECK(!curr_value->IsGridAutoRepeatValue());
      DCHECK(!curr_value->IsGridIntegerRepeatValue());
      track_sizes.push_back(ConvertGridTrackSize(state, *curr_value));
    }
  
    return GridTrackList(track_sizes);
  }
  
  void StyleBuilderConverter::ConvertGridTrackList(
      const CSSValue& value,
      GridTrackList& track_sizes,
      NamedGridLinesMap& named_grid_lines,
      OrderedNamedGridLines& ordered_named_grid_lines,
      Vector<GridTrackSize>& auto_repeat_track_sizes,
      NamedGridLinesMap& auto_repeat_named_grid_lines,
      OrderedNamedGridLines& auto_repeat_ordered_named_grid_lines,
      size_t& auto_repeat_insertion_point,
      AutoRepeatType& auto_repeat_type,
      StyleResolverState& state) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return;
    }
  
    size_t current_named_grid_line = 0;
    auto convert_line_name_or_track_size = [&](const CSSValue& curr_value) {
      if (curr_value.IsGridLineNamesValue()) {
        ConvertGridLineNamesList(curr_value, current_named_grid_line,
                                 named_grid_lines, ordered_named_grid_lines);
      } else {
        ++current_named_grid_line;
        track_sizes.LegacyTrackList().push_back(
            ConvertGridTrackSize(state, curr_value));
      }
    };
  
    for (auto curr_value : To<CSSValueList>(value)) {
      if (auto* grid_auto_repeat_value =
              DynamicTo<cssvalue::CSSGridAutoRepeatValue>(curr_value.Get())) {
        Vector<GridTrackSize> repeated_track_sizes;
        size_t auto_repeat_index = 0;
        CSSValueID auto_repeat_id = grid_auto_repeat_value->AutoRepeatID();
        DCHECK(auto_repeat_id == CSSValueID::kAutoFill ||
               auto_repeat_id == CSSValueID::kAutoFit);
        auto_repeat_type = auto_repeat_id == CSSValueID::kAutoFill
                               ? AutoRepeatType::kAutoFill
                               : AutoRepeatType::kAutoFit;
        for (auto auto_repeat_value : To<CSSValueList>(*curr_value)) {
          if (auto_repeat_value->IsGridLineNamesValue()) {
            ConvertGridLineNamesList(*auto_repeat_value, auto_repeat_index,
                                     auto_repeat_named_grid_lines,
                                     auto_repeat_ordered_named_grid_lines);
            continue;
          }
          ++auto_repeat_index;
          repeated_track_sizes.push_back(
              ConvertGridTrackSize(state, *auto_repeat_value));
        }
        if (RuntimeEnabledFeatures::LayoutNGGridEnabled()) {
          track_sizes.NGTrackList().AddAutoRepeater(
              repeated_track_sizes,
              static_cast<NGGridTrackRepeater::RepeatType>(auto_repeat_type));
        }
        DCHECK(auto_repeat_track_sizes.IsEmpty());
        auto_repeat_track_sizes = std::move(repeated_track_sizes);
        auto_repeat_insertion_point = current_named_grid_line++;
        continue;
      }
  
      if (auto* repeated_values =
              DynamicTo<cssvalue::CSSGridIntegerRepeatValue>(curr_value.Get())) {
        size_t repetitions = repeated_values->Repetitions();
        for (size_t i = 0; i < repetitions; ++i) {
          for (auto curr_repeat_value : *repeated_values)
            convert_line_name_or_track_size(*curr_repeat_value);
        }
        if (RuntimeEnabledFeatures::LayoutNGGridEnabled()) {
          Vector<GridTrackSize> repeater_sizes;
          for (auto curr_repeat_value : *repeated_values) {
            if (!curr_repeat_value->IsGridLineNamesValue()) {
              repeater_sizes.push_back(
                  ConvertGridTrackSize(state, *curr_repeat_value));
            }
          }
          track_sizes.NGTrackList().AddRepeater(repeater_sizes, repetitions);
        }
        continue;
      }
  
      convert_line_name_or_track_size(*curr_value);
      if (RuntimeEnabledFeatures::LayoutNGGridEnabled() &&
          !curr_value->IsGridLineNamesValue()) {
        track_sizes.NGTrackList().AddRepeater(
            {ConvertGridTrackSize(state, *curr_value)}, 1);
      }
    }
  
    // The parser should have rejected any <track-list> without any <track-size>
    // as this is not conformant to the syntax.
    DCHECK(!track_sizes.LegacyTrackList().IsEmpty() ||
           !auto_repeat_track_sizes.IsEmpty());
  }
  
  void StyleBuilderConverter::CreateImplicitNamedGridLinesFromGridArea(
      const NamedGridAreaMap& named_grid_areas,
      NamedGridLinesMap& named_grid_lines,
      GridTrackSizingDirection direction) {
    for (const auto& named_grid_area_entry : named_grid_areas) {
      GridSpan area_span = direction == kForRows
                               ? named_grid_area_entry.value.rows
                               : named_grid_area_entry.value.columns;
      {
        NamedGridLinesMap::AddResult start_result = named_grid_lines.insert(
            named_grid_area_entry.key + "-start", Vector<size_t>());
        start_result.stored_value->value.push_back(area_span.StartLine());
        std::sort(start_result.stored_value->value.begin(),
                  start_result.stored_value->value.end());
      }
      {
        NamedGridLinesMap::AddResult end_result = named_grid_lines.insert(
            named_grid_area_entry.key + "-end", Vector<size_t>());
        end_result.stored_value->value.push_back(area_span.EndLine());
        std::sort(end_result.stored_value->value.begin(),
                  end_result.stored_value->value.end());
      }
    }
  }
  
  LayoutUnit StyleBuilderConverter::ConvertBorderWidth(StyleResolverState& state,
                                                       const CSSValue& value) {
    double result = 0;
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      switch (identifier_value->GetValueID()) {
        case CSSValueID::kThin:
          result = 1;
          break;
        case CSSValueID::kMedium:
          result = 3;
          break;
        case CSSValueID::kThick:
          result = 5;
          break;
        default:
          NOTREACHED();
          break;
      }
      result = state.CssToLengthConversionData().ZoomedComputedPixels(
          result, CSSPrimitiveValue::UnitType::kPixels);
    } else {
      const auto& primitive_value = To<CSSPrimitiveValue>(value);
      result =
          primitive_value.ComputeLength<float>(state.CssToLengthConversionData());
    }
    if (result > 0.0 && result < 1.0)
      return LayoutUnit(1);
    return LayoutUnit(result);
  }
  
  LayoutUnit StyleBuilderConverter::ConvertLayoutUnit(StyleResolverState& state,
                                                      const CSSValue& value) {
    return LayoutUnit::Clamp(ConvertComputedLength<float>(state, value));
  }
  
  absl::optional<Length> StyleBuilderConverter::ConvertGapLength(
      const StyleResolverState& state,
      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kNormal)
      return absl::nullopt;
  
    return ConvertLength(state, value);
  }
  
  Length StyleBuilderConverter::ConvertLength(const StyleResolverState& state,
                                              const CSSValue& value) {
    return To<CSSPrimitiveValue>(value).ConvertToLength(
        state.CssToLengthConversionData());
  }
  
  UnzoomedLength StyleBuilderConverter::ConvertUnzoomedLength(
      const StyleResolverState& state,
      const CSSValue& value) {
    return UnzoomedLength(To<CSSPrimitiveValue>(value).ConvertToLength(
        state.UnzoomedLengthConversionData()));
  }
  
  float StyleBuilderConverter::ConvertZoom(const StyleResolverState& state,
                                           const CSSValue& value) {
    SECURITY_DCHECK(value.IsPrimitiveValue() || value.IsIdentifierValue());
  
    if (const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      if (identifier_value->GetValueID() == CSSValueID::kNormal)
        return ComputedStyleInitialValues::InitialZoom();
    } else if (const auto* primitive_value =
                   DynamicTo<CSSPrimitiveValue>(value)) {
      if (primitive_value->IsPercentage()) {
        float percent = primitive_value->GetFloatValue();
        return percent ? (percent / 100.0f) : 1.0f;
      } else if (primitive_value->IsNumber()) {
        float number = primitive_value->GetFloatValue();
        return number ? number : 1.0f;
      }
    }
  
    NOTREACHED();
    return 1.0f;
  }
  
  Length StyleBuilderConverter::ConvertLengthOrAuto(
      const StyleResolverState& state,
      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kAuto)
      return Length::Auto();
    return To<CSSPrimitiveValue>(value).ConvertToLength(
        state.CssToLengthConversionData());
  }
  
  Length StyleBuilderConverter::ConvertLengthSizing(StyleResolverState& state,
                                                    const CSSValue& value) {
    const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (!identifier_value)
      return ConvertLength(state, value);
  
    switch (identifier_value->GetValueID()) {
      case CSSValueID::kMinContent:
      case CSSValueID::kWebkitMinContent:
        return Length::MinContent();
      case CSSValueID::kMaxContent:
      case CSSValueID::kWebkitMaxContent:
        return Length::MaxContent();
      case CSSValueID::kWebkitFillAvailable:
        return Length::FillAvailable();
      case CSSValueID::kWebkitFitContent:
      case CSSValueID::kFitContent:
        return Length::FitContent();
      case CSSValueID::kAuto:
        return Length::Auto();
      default:
        NOTREACHED();
        return Length();
    }
  }
  
  Length StyleBuilderConverter::ConvertLengthMaxSizing(StyleResolverState& state,
                                                       const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kNone)
      return Length::None();
    return ConvertLengthSizing(state, value);
  }
  
  TabSize StyleBuilderConverter::ConvertLengthOrTabSpaces(
      StyleResolverState& state,
      const CSSValue& value) {
    const auto& primitive_value = To<CSSPrimitiveValue>(value);
    if (primitive_value.IsNumber())
      return TabSize(primitive_value.GetFloatValue(), TabSizeValueType::kSpace);
    return TabSize(
        primitive_value.ComputeLength<float>(state.CssToLengthConversionData()),
        TabSizeValueType::kLength);
  }
  
  static CSSToLengthConversionData LineHeightToLengthConversionData(
      StyleResolverState& state) {
    float multiplier = state.Style()->EffectiveZoom();
    if (LocalFrame* frame = state.GetDocument().GetFrame())
      multiplier *= frame->TextZoomFactor();
    return state.CssToLengthConversionData().CopyWithAdjustedZoom(multiplier);
  }
  
  Length StyleBuilderConverter::ConvertLineHeight(StyleResolverState& state,
                                                  const CSSValue& value) {
    if (const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value)) {
      if (primitive_value->IsLength()) {
        return primitive_value->ComputeLength<Length>(
            LineHeightToLengthConversionData(state));
      }
      if (primitive_value->IsPercentage()) {
        return Length::Fixed(
            (state.Style()->ComputedFontSize() * primitive_value->GetIntValue()) /
            100.0);
      }
      if (primitive_value->IsNumber()) {
        return Length::Percent(
            clampTo<float>(primitive_value->GetDoubleValue() * 100.0));
      }
      if (primitive_value->IsCalculated()) {
        Length zoomed_length =
            Length(To<CSSMathFunctionValue>(primitive_value)
                       ->ToCalcValue(LineHeightToLengthConversionData(state)));
        return Length::Fixed(ValueForLength(
            zoomed_length, LayoutUnit(state.Style()->ComputedFontSize())));
      }
    }
  
    if (value.IsPendingSystemFontValue())
      return ComputedStyleInitialValues::InitialLineHeight();
  
    DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kNormal);
    return ComputedStyleInitialValues::InitialLineHeight();
  }
  
  float StyleBuilderConverter::ConvertNumberOrPercentage(
      StyleResolverState& state,
      const CSSValue& value) {
    const auto& primitive_value = To<CSSPrimitiveValue>(value);
    DCHECK(primitive_value.IsNumber() || primitive_value.IsPercentage());
    if (primitive_value.IsNumber())
      return primitive_value.GetFloatValue();
    return primitive_value.GetFloatValue() / 100.0f;
  }
  
  float StyleBuilderConverter::ConvertAlpha(StyleResolverState& state,
                                            const CSSValue& value) {
    return clampTo<float>(ConvertNumberOrPercentage(state, value), 0, 1);
  }
  
  StyleOffsetRotation StyleBuilderConverter::ConvertOffsetRotate(
      StyleResolverState&,
      const CSSValue& value) {
    return ConvertOffsetRotate(value);
  }
  
  StyleOffsetRotation StyleBuilderConverter::ConvertOffsetRotate(
      const CSSValue& value) {
    StyleOffsetRotation result(0, OffsetRotationType::kFixed);
  
    const auto& list = To<CSSValueList>(value);
    DCHECK(list.length() == 1 || list.length() == 2);
    for (const auto& item : list) {
      auto* identifier_value = DynamicTo<CSSIdentifierValue>(item.Get());
      if (identifier_value &&
          identifier_value->GetValueID() == CSSValueID::kAuto) {
        result.type = OffsetRotationType::kAuto;
      } else if (identifier_value &&
                 identifier_value->GetValueID() == CSSValueID::kReverse) {
        result.type = OffsetRotationType::kAuto;
        result.angle = clampTo<float>(result.angle + 180);
      } else {
        const auto& primitive_value = To<CSSPrimitiveValue>(*item);
        result.angle =
            clampTo<float>(result.angle + primitive_value.ComputeDegrees());
      }
    }
  
    return result;
  }
  
  LengthPoint StyleBuilderConverter::ConvertPosition(StyleResolverState& state,
                                                     const CSSValue& value) {
    const auto& pair = To<CSSValuePair>(value);
    return LengthPoint(
        ConvertPositionLength<CSSValueID::kLeft, CSSValueID::kRight>(
            state, pair.First()),
        ConvertPositionLength<CSSValueID::kTop, CSSValueID::kBottom>(
            state, pair.Second()));
  }
  
  LengthPoint StyleBuilderConverter::ConvertPositionOrAuto(
      StyleResolverState& state,
      const CSSValue& value) {
    if (value.IsValuePair())
      return ConvertPosition(state, value);
    DCHECK(To<CSSIdentifierValue>(value).GetValueID() == CSSValueID::kAuto);
    return LengthPoint(Length::Auto(), Length::Auto());
  }
  
  static float ConvertPerspectiveLength(
      StyleResolverState& state,
      const CSSPrimitiveValue& primitive_value) {
    return std::max(
        primitive_value.ComputeLength<float>(state.CssToLengthConversionData()),
        0.0f);
  }
  
  float StyleBuilderConverter::ConvertPerspective(StyleResolverState& state,
                                                  const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kNone)
      return ComputedStyleInitialValues::InitialPerspective();
    return ConvertPerspectiveLength(state, To<CSSPrimitiveValue>(value));
  }
  
  EPaintOrder StyleBuilderConverter::ConvertPaintOrder(
      StyleResolverState&,
      const CSSValue& css_paint_order) {
    if (const auto* order_type_list = DynamicTo<CSSValueList>(css_paint_order)) {
      switch (To<CSSIdentifierValue>(order_type_list->Item(0)).GetValueID()) {
        case CSSValueID::kFill:
          return order_type_list->length() > 1 ? kPaintOrderFillMarkersStroke
                                               : kPaintOrderFillStrokeMarkers;
        case CSSValueID::kStroke:
          return order_type_list->length() > 1 ? kPaintOrderStrokeMarkersFill
                                               : kPaintOrderStrokeFillMarkers;
        case CSSValueID::kMarkers:
          return order_type_list->length() > 1 ? kPaintOrderMarkersStrokeFill
                                               : kPaintOrderMarkersFillStroke;
        default:
          NOTREACHED();
          return kPaintOrderNormal;
      }
    }
  
    return kPaintOrderNormal;
  }
  
  Length StyleBuilderConverter::ConvertQuirkyLength(StyleResolverState& state,
                                                    const CSSValue& value) {
    Length length = ConvertLengthOrAuto(state, value);
    // This is only for margins which use __qem
    auto* numeric_literal = DynamicTo<CSSNumericLiteralValue>(value);
    length.SetQuirk(numeric_literal && numeric_literal->IsQuirkyEms());
    return length;
  }
  
  scoped_refptr<QuotesData> StyleBuilderConverter::ConvertQuotes(
      StyleResolverState&,
      const CSSValue& value) {
    if (const auto* list = DynamicTo<CSSValueList>(value)) {
      scoped_refptr<QuotesData> quotes = QuotesData::Create();
      for (wtf_size_t i = 0; i < list->length(); i += 2) {
        String start_quote = To<CSSStringValue>(list->Item(i)).Value();
        String end_quote = To<CSSStringValue>(list->Item(i + 1)).Value();
        quotes->AddPair(std::make_pair(start_quote, end_quote));
      }
      return quotes;
    }
    if (To<CSSIdentifierValue>(value).GetValueID() == CSSValueID::kNone)
      return QuotesData::Create();
    DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kAuto);
    return nullptr;
  }
  
  LengthSize StyleBuilderConverter::ConvertRadius(StyleResolverState& state,
                                                  const CSSValue& value) {
    const auto& pair = To<CSSValuePair>(value);
    Length radius_width = To<CSSPrimitiveValue>(pair.First())
                              .ConvertToLength(state.CssToLengthConversionData());
    Length radius_height =
        To<CSSPrimitiveValue>(pair.Second())
            .ConvertToLength(state.CssToLengthConversionData());
    return LengthSize(radius_width, radius_height);
  }
  
  ShadowData StyleBuilderConverter::ConvertShadow(
      const CSSToLengthConversionData& conversion_data,
      StyleResolverState* state,
      const CSSValue& value) {
    const auto& shadow = To<CSSShadowValue>(value);
    float x = shadow.x->ComputeLength<float>(conversion_data);
    float y = shadow.y->ComputeLength<float>(conversion_data);
    float blur =
        shadow.blur ? shadow.blur->ComputeLength<float>(conversion_data) : 0;
    float spread =
        shadow.spread ? shadow.spread->ComputeLength<float>(conversion_data) : 0;
    ShadowStyle shadow_style =
        shadow.style && shadow.style->GetValueID() == CSSValueID::kInset
            ? ShadowStyle::kInset
            : ShadowStyle::kNormal;
    StyleColor color = StyleColor::CurrentColor();
    if (shadow.color) {
      if (state) {
        color = ConvertStyleColor(*state, *shadow.color);
      } else {
        // For OffScreen canvas, we default to black and only parse non
        // Document dependent CSS colors.
        color = StyleColor(Color::kBlack);
        if (auto* color_value =
                DynamicTo<cssvalue::CSSColor>(shadow.color.Get())) {
          color = StyleColor(color_value->Value());
        } else {
          CSSValueID value_id =
              To<CSSIdentifierValue>(*shadow.color).GetValueID();
          switch (value_id) {
            case CSSValueID::kInvalid:
              NOTREACHED();
              FALLTHROUGH;
            case CSSValueID::kInternalQuirkInherit:
            case CSSValueID::kWebkitLink:
            case CSSValueID::kWebkitActivelink:
            case CSSValueID::kWebkitFocusRingColor:
            case CSSValueID::kCurrentcolor:
              break;
            default:
              color = StyleColor(StyleColor::ColorFromKeyword(
                  value_id, mojom::blink::ColorScheme::kLight));
          }
        }
      }
    }
  
    return ShadowData(FloatPoint(x, y), blur, spread, shadow_style, color);
  }
  
  scoped_refptr<ShadowList> StyleBuilderConverter::ConvertShadowList(
      StyleResolverState& state,
      const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return scoped_refptr<ShadowList>();
    }
  
    ShadowDataVector shadows;
    for (const auto& item : To<CSSValueList>(value)) {
      shadows.push_back(
          ConvertShadow(state.CssToLengthConversionData(), &state, *item));
    }
  
    return ShadowList::Adopt(shadows);
  }
  
  ShapeValue* StyleBuilderConverter::ConvertShapeValue(StyleResolverState& state,
                                                       const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return nullptr;
    }
  
    if (value.IsImageValue() || value.IsImageGeneratorValue() ||
        value.IsImageSetValue()) {
      return MakeGarbageCollected<ShapeValue>(
          state.GetStyleImage(CSSPropertyID::kShapeOutside, value));
    }
  
    scoped_refptr<BasicShape> shape;
    CSSBoxType css_box = CSSBoxType::kMissing;
    const auto& value_list = To<CSSValueList>(value);
    for (unsigned i = 0; i < value_list.length(); ++i) {
      const CSSValue& item_value = value_list.Item(i);
      if (item_value.IsBasicShapeValue()) {
        shape = BasicShapeForValue(state, item_value);
      } else {
        css_box = To<CSSIdentifierValue>(item_value).ConvertTo<CSSBoxType>();
      }
    }
  
    if (shape)
      return MakeGarbageCollected<ShapeValue>(std::move(shape), css_box);
  
    DCHECK_NE(css_box, CSSBoxType::kMissing);
    return MakeGarbageCollected<ShapeValue>(css_box);
  }
  
  float StyleBuilderConverter::ConvertSpacing(StyleResolverState& state,
                                              const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kNormal)
      return 0;
    return To<CSSPrimitiveValue>(value).ComputeLength<float>(
        state.CssToLengthConversionData());
  }
  
  scoped_refptr<SVGDashArray> StyleBuilderConverter::ConvertStrokeDasharray(
      StyleResolverState& state,
      const CSSValue& value) {
    const auto* dashes = DynamicTo<CSSValueList>(value);
    if (!dashes)
      return EmptyDashArray();
  
    scoped_refptr<SVGDashArray> array = base::MakeRefCounted<SVGDashArray>();
  
    wtf_size_t length = dashes->length();
    for (wtf_size_t i = 0; i < length; ++i) {
      array->data.push_back(
          ConvertLength(state, To<CSSPrimitiveValue>(dashes->Item(i))));
    }
  
    return array;
  }
  
  void StyleBuilderConverter::CountSystemColorComputeToSelfUsage(
      const StyleResolverState& state) {
    // Count cases where a system color keyword is used on an element whose
    // color-scheme is different from its parent.
    // This is a superset of when the feature will change the resolved color
    // (inheriting the keyword is also required) but it should be a reasonable
    // approximation for use counting purposes.
    if (state.Style()->ComputedColorScheme() !=
        state.ParentStyle()->ComputedColorScheme()) {
      UseCounter::Count(state.GetDocument(),
                        WebFeature::kCSSSystemColorComputeToSelf);
    }
  }
  
  StyleColor StyleBuilderConverter::ConvertStyleColor(StyleResolverState& state,
                                                      const CSSValue& value,
                                                      bool for_visited_link) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value) {
      CSSValueID value_id = identifier_value->GetValueID();
      if (value_id == CSSValueID::kCurrentcolor)
        return StyleColor::CurrentColor();
      if (StyleColor::IsSystemColor(value_id)) {
        CountSystemColorComputeToSelfUsage(state);
        return StyleColor(
            state.GetDocument().GetTextLinkColors().ColorFromCSSValue(
                value, Color(), state.Style()->UsedColorScheme(),
                for_visited_link),
            value_id);
      }
    }
    return StyleColor(state.GetDocument().GetTextLinkColors().ColorFromCSSValue(
        value, Color(), state.Style()->UsedColorScheme(), for_visited_link));
  }
  
  StyleAutoColor StyleBuilderConverter::ConvertStyleAutoColor(
      StyleResolverState& state,
      const CSSValue& value,
      bool for_visited_link) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      CSSValueID value_id = identifier_value->GetValueID();
      if (value_id == CSSValueID::kCurrentcolor)
        return StyleAutoColor::CurrentColor();
      if (value_id == CSSValueID::kAuto)
        return StyleAutoColor::AutoColor();
      if (StyleColor::IsSystemColor(value_id)) {
        CountSystemColorComputeToSelfUsage(state);
        return StyleAutoColor(
            state.GetDocument().GetTextLinkColors().ColorFromCSSValue(
                value, Color(), state.Style()->UsedColorScheme(),
                for_visited_link),
            value_id);
      }
    }
    return StyleAutoColor(
        state.GetDocument().GetTextLinkColors().ColorFromCSSValue(
            value, Color(), state.Style()->UsedColorScheme(), for_visited_link));
  }
  
  SVGPaint StyleBuilderConverter::ConvertSVGPaint(StyleResolverState& state,
                                                  const CSSValue& value,
                                                  bool for_visited_link) {
    const CSSValue* local_value = &value;
    SVGPaint paint;
    if (const auto* list = DynamicTo<CSSValueList>(value)) {
      DCHECK_EQ(list->length(), 2u);
      paint.resource = ConvertElementReference(state, list->Item(0));
      local_value = &list->Item(1);
    }
  
    if (local_value->IsURIValue()) {
      paint.type = SVG_PAINTTYPE_URI;
      paint.resource = ConvertElementReference(state, *local_value);
    } else {
      auto* local_identifier_value = DynamicTo<CSSIdentifierValue>(local_value);
      if (local_identifier_value &&
          local_identifier_value->GetValueID() == CSSValueID::kNone) {
        paint.type =
            !paint.resource ? SVG_PAINTTYPE_NONE : SVG_PAINTTYPE_URI_NONE;
      } else {
        // TODO(fs): Pass along |for_visited_link|.
        paint.color = ConvertStyleColor(state, *local_value);
        paint.type =
            !paint.resource ? SVG_PAINTTYPE_COLOR : SVG_PAINTTYPE_URI_COLOR;
      }
    }
    return paint;
  }
  
  TextDecorationThickness StyleBuilderConverter::ConvertTextDecorationThickness(
      StyleResolverState& state,
      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value &&
        identifier_value->GetValueID() == CSSValueID::kFromFont)
      return TextDecorationThickness(identifier_value->GetValueID());
  
    return TextDecorationThickness(ConvertLengthOrAuto(state, value));
  }
  
  TextEmphasisPosition StyleBuilderConverter::ConvertTextTextEmphasisPosition(
      StyleResolverState& state,
      const CSSValue& value) {
    const auto& list = To<CSSValueList>(value);
    CSSValueID first = To<CSSIdentifierValue>(list.Item(0)).GetValueID();
    CSSValueID second = To<CSSIdentifierValue>(list.Item(1)).GetValueID();
    if (first == CSSValueID::kOver && second == CSSValueID::kRight)
      return TextEmphasisPosition::kOverRight;
    if (first == CSSValueID::kOver && second == CSSValueID::kLeft)
      return TextEmphasisPosition::kOverLeft;
    if (first == CSSValueID::kUnder && second == CSSValueID::kRight)
      return TextEmphasisPosition::kUnderRight;
    if (first == CSSValueID::kUnder && second == CSSValueID::kLeft)
      return TextEmphasisPosition::kUnderLeft;
    return TextEmphasisPosition::kOverRight;
  }
  
  float StyleBuilderConverter::ConvertTextStrokeWidth(StyleResolverState& state,
                                                      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && IsValidCSSValueID(identifier_value->GetValueID())) {
      float multiplier = ConvertLineWidth<float>(state, value);
      return CSSNumericLiteralValue::Create(multiplier / 48,
                                            CSSPrimitiveValue::UnitType::kEms)
          ->ComputeLength<float>(state.CssToLengthConversionData());
    }
    return To<CSSPrimitiveValue>(value).ComputeLength<float>(
        state.CssToLengthConversionData());
  }
  
  TextSizeAdjust StyleBuilderConverter::ConvertTextSizeAdjust(
      StyleResolverState& state,
      const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      if (identifier_value->GetValueID() == CSSValueID::kNone)
        return TextSizeAdjust::AdjustNone();
      if (identifier_value->GetValueID() == CSSValueID::kAuto)
        return TextSizeAdjust::AdjustAuto();
    }
    const CSSPrimitiveValue& primitive_value = To<CSSPrimitiveValue>(value);
    DCHECK(primitive_value.IsPercentage());
    return TextSizeAdjust(primitive_value.GetFloatValue() / 100.0f);
  }
  
  TextUnderlinePosition StyleBuilderConverter::ConvertTextUnderlinePosition(
      StyleResolverState& state,
      const CSSValue& value) {
    TextUnderlinePosition flags = kTextUnderlinePositionAuto;
  
    auto process = [&flags](const CSSValue& identifier) {
      flags |=
          To<CSSIdentifierValue>(identifier).ConvertTo<TextUnderlinePosition>();
    };
  
    if (auto* value_list = DynamicTo<CSSValueList>(value)) {
      for (auto& entry : *value_list) {
        process(*entry);
      }
    } else {
      process(value);
    }
    return flags;
  }
  
  Length StyleBuilderConverter::ConvertTextUnderlineOffset(
      StyleResolverState& state,
      const CSSValue& value) {
    return ConvertLengthOrAuto(state, value);
  }
  
  TransformOperations StyleBuilderConverter::ConvertTransformOperations(
      StyleResolverState& state,
      const CSSValue& value) {
    return TransformBuilder::CreateTransformOperations(
        value, state.CssToLengthConversionData());
  }
  
  TransformOrigin StyleBuilderConverter::ConvertTransformOrigin(
      StyleResolverState& state,
      const CSSValue& value) {
    const auto& list = To<CSSValueList>(value);
    DCHECK_GE(list.length(), 2u);
    DCHECK(list.Item(0).IsPrimitiveValue() || list.Item(0).IsIdentifierValue());
    DCHECK(list.Item(1).IsPrimitiveValue() || list.Item(1).IsIdentifierValue());
    float z = 0;
    if (list.length() == 3) {
      DCHECK(list.Item(2).IsPrimitiveValue());
      z = StyleBuilderConverter::ConvertComputedLength<float>(state,
                                                              list.Item(2));
    }
  
    return TransformOrigin(
        ConvertPositionLength<CSSValueID::kLeft, CSSValueID::kRight>(
            state, list.Item(0)),
        ConvertPositionLength<CSSValueID::kTop, CSSValueID::kBottom>(
            state, list.Item(1)),
        z);
  }
  
  cc::ScrollSnapType StyleBuilderConverter::ConvertSnapType(
      StyleResolverState&,
      const CSSValue& value) {
    cc::ScrollSnapType snapType =
        ComputedStyleInitialValues::InitialScrollSnapType();
    if (const auto* pair = DynamicTo<CSSValuePair>(value)) {
      snapType.is_none = false;
      snapType.axis =
          To<CSSIdentifierValue>(pair->First()).ConvertTo<cc::SnapAxis>();
      snapType.strictness =
          To<CSSIdentifierValue>(pair->Second()).ConvertTo<cc::SnapStrictness>();
      return snapType;
    }
  
    if (To<CSSIdentifierValue>(value).GetValueID() == CSSValueID::kNone) {
      snapType.is_none = true;
      return snapType;
    }
  
    snapType.is_none = false;
    snapType.axis = To<CSSIdentifierValue>(value).ConvertTo<cc::SnapAxis>();
    return snapType;
  }
  
  cc::ScrollSnapAlign StyleBuilderConverter::ConvertSnapAlign(
      StyleResolverState&,
      const CSSValue& value) {
    cc::ScrollSnapAlign snapAlign =
        ComputedStyleInitialValues::InitialScrollSnapAlign();
    if (const auto* pair = DynamicTo<CSSValuePair>(value)) {
      snapAlign.alignment_block =
          To<CSSIdentifierValue>(pair->First()).ConvertTo<cc::SnapAlignment>();
      snapAlign.alignment_inline =
          To<CSSIdentifierValue>(pair->Second()).ConvertTo<cc::SnapAlignment>();
    } else {
      snapAlign.alignment_block =
          To<CSSIdentifierValue>(value).ConvertTo<cc::SnapAlignment>();
      snapAlign.alignment_inline = snapAlign.alignment_block;
    }
    return snapAlign;
  }
  
  scoped_refptr<TranslateTransformOperation>
  StyleBuilderConverter::ConvertTranslate(StyleResolverState& state,
                                          const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return nullptr;
    }
    const auto& list = To<CSSValueList>(value);
    DCHECK_LE(list.length(), 3u);
    Length tx = ConvertLength(state, list.Item(0));
    Length ty = Length::Fixed(0);
    double tz = 0;
    if (list.length() >= 2)
      ty = ConvertLength(state, list.Item(1));
    if (list.length() == 3)
      tz = To<CSSPrimitiveValue>(list.Item(2))
               .ComputeLength<double>(state.CssToLengthConversionData());
  
    return TranslateTransformOperation::Create(tx, ty, tz,
                                               TransformOperation::kTranslate3D);
  }
  
  Rotation StyleBuilderConverter::ConvertRotation(const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return Rotation(FloatPoint3D(0, 0, 1), 0);
    }
  
    const auto& list = To<CSSValueList>(value);
    DCHECK(list.length() == 1 || list.length() == 2);
    double x = 0;
    double y = 0;
    double z = 1;
    if (list.length() == 2) {
      // axis angle
      const cssvalue::CSSAxisValue& axis =
          To<cssvalue::CSSAxisValue>(list.Item(0));
      x = axis.X();
      y = axis.Y();
      z = axis.Z();
    }
    double angle =
        To<CSSPrimitiveValue>(list.Item(list.length() - 1)).ComputeDegrees();
    return Rotation(FloatPoint3D(x, y, z), angle);
  }
  
  scoped_refptr<RotateTransformOperation> StyleBuilderConverter::ConvertRotate(
      StyleResolverState& state,
      const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return nullptr;
    }
  
    return RotateTransformOperation::Create(ConvertRotation(value),
                                            TransformOperation::kRotate3D);
  }
  
  scoped_refptr<ScaleTransformOperation> StyleBuilderConverter::ConvertScale(
      StyleResolverState& state,
      const CSSValue& value) {
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      DCHECK_EQ(identifier_value->GetValueID(), CSSValueID::kNone);
      return nullptr;
    }
  
    const auto& list = To<CSSValueList>(value);
    DCHECK_LE(list.length(), 3u);
    double sx = To<CSSPrimitiveValue>(list.Item(0)).GetDoubleValue();
    double sy = sx;
    double sz = 1;
    if (list.length() >= 2)
      sy = To<CSSPrimitiveValue>(list.Item(1)).GetDoubleValue();
    if (list.length() == 3)
      sz = To<CSSPrimitiveValue>(list.Item(2)).GetDoubleValue();
  
    return ScaleTransformOperation::Create(sx, sy, sz,
                                           TransformOperation::kScale3D);
  }
  
  RespectImageOrientationEnum StyleBuilderConverter::ConvertImageOrientation(
      StyleResolverState& state,
      const CSSValue& value) {
    // The default is kFromImage, so branch on the only other valid value, kNone
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    return identifier_value && identifier_value->GetValueID() == CSSValueID::kNone
               ? kDoNotRespectImageOrientation
               : kRespectImageOrientation;
  }
  
  scoped_refptr<StylePath> StyleBuilderConverter::ConvertPathOrNone(
      StyleResolverState& state,
      const CSSValue& value) {
    if (auto* path_value = DynamicTo<cssvalue::CSSPathValue>(value))
      return path_value->GetStylePath();
    DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kNone);
    return nullptr;
  }
  
  scoped_refptr<BasicShape> StyleBuilderConverter::ConvertOffsetPath(
      StyleResolverState& state,
      const CSSValue& value) {
    if (value.IsRayValue())
      return BasicShapeForValue(state, value);
    return ConvertPathOrNone(state, value);
  }
  
  static const CSSValue& ComputeRegisteredPropertyValue(
      const Document& document,
      const StyleResolverState* state,
      const CSSToLengthConversionData& css_to_length_conversion_data,
      const CSSValue& value,
      const String& base_url,
      const WTF::TextEncoding& charset) {
    // TODO(timloh): Images values can also contain lengths.
    if (const auto* function_value = DynamicTo<CSSFunctionValue>(value)) {
      CSSFunctionValue* new_function =
          MakeGarbageCollected<CSSFunctionValue>(function_value->FunctionType());
      for (const CSSValue* inner_value : To<CSSValueList>(value)) {
        new_function->Append(ComputeRegisteredPropertyValue(
            document, state, css_to_length_conversion_data, *inner_value,
            base_url, charset));
      }
      return *new_function;
    }
  
    if (const auto* old_list = DynamicTo<CSSValueList>(value)) {
      CSSValueList* new_list = CSSValueList::CreateWithSeparatorFrom(*old_list);
      for (const CSSValue* inner_value : *old_list) {
        new_list->Append(ComputeRegisteredPropertyValue(
            document, state, css_to_length_conversion_data, *inner_value,
            base_url, charset));
      }
      return *new_list;
    }
  
    if (const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value)) {
      // For simple (non-calculated) px or percentage values, we do not need to
      // convert, as the value already has the proper computed form.
      if (!primitive_value->IsCalculated() &&
          (primitive_value->IsPx() || primitive_value->IsPercentage())) {
        return value;
      }
  
      if (primitive_value->IsLength() || primitive_value->IsPercentage() ||
          primitive_value->IsCalculatedPercentageWithLength()) {
        // Instead of the actual zoom, use 1 to avoid potential rounding errors
        Length length = primitive_value->ConvertToLength(
            css_to_length_conversion_data.CopyWithAdjustedZoom(1));
        return *CSSPrimitiveValue::CreateFromLength(length, 1);
      }
  
      // If we encounter a calculated number that was not resolved during
      // parsing, it means that a calc()-expression was allowed in place of
      // an integer. Such calc()-for-integers must be rounded at computed value
      // time.
      // https://drafts.csswg.org/css-values-4/#calc-type-checking
      if (primitive_value->IsCalculated()) {
        const CSSMathFunctionValue& math_value =
            To<CSSMathFunctionValue>(*primitive_value);
        if (math_value.IsNumber()) {
          double double_value = math_value.GetDoubleValue();
          auto unit_type = CSSPrimitiveValue::UnitType::kInteger;
          return *CSSNumericLiteralValue::Create(std::round(double_value),
                                                 unit_type);
        }
      }
  
      if (primitive_value->IsAngle()) {
        return *CSSNumericLiteralValue::Create(
            primitive_value->ComputeDegrees(),
            CSSPrimitiveValue::UnitType::kDegrees);
      }
  
      if (primitive_value->IsTime()) {
        return *CSSNumericLiteralValue::Create(
            primitive_value->ComputeSeconds(),
            CSSPrimitiveValue::UnitType::kSeconds);
      }
  
      if (primitive_value->IsResolution()) {
        return *CSSNumericLiteralValue::Create(
            primitive_value->ComputeDotsPerPixel(),
            CSSPrimitiveValue::UnitType::kDotsPerPixel);
      }
    }
  
    if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      CSSValueID value_id = identifier_value->GetValueID();
      if (value_id == CSSValueID::kCurrentcolor)
        return value;
      if (StyleColor::IsColorKeyword(value_id)) {
        mojom::blink::ColorScheme scheme =
            state ? state->Style()->UsedColorScheme()
                  : mojom::blink::ColorScheme::kLight;
        Color color = document.GetTextLinkColors().ColorFromCSSValue(
            value, Color(), scheme, false);
        return *cssvalue::CSSColor::Create(color.Rgb());
      }
    }
  
    if (const auto* uri_value = DynamicTo<cssvalue::CSSURIValue>(value))
      return *uri_value->ValueWithURLMadeAbsolute(KURL(base_url), charset);
  
    return value;
  }
  
  const CSSValue& StyleBuilderConverter::ConvertRegisteredPropertyInitialValue(
      const Document& document,
      const CSSValue& value) {
    return ComputeRegisteredPropertyValue(
        document, nullptr /* state */, CSSToLengthConversionData(), value,
        document.BaseURL(), document.Encoding());
  }
  
  const CSSValue& StyleBuilderConverter::ConvertRegisteredPropertyValue(
      const StyleResolverState& state,
      const CSSValue& value,
      const String& base_url,
      const WTF::TextEncoding& charset) {
    return ComputeRegisteredPropertyValue(state.GetDocument(), &state,
                                          state.CssToLengthConversionData(),
                                          value, base_url, charset);
  }
  
  // Registered properties need to substitute as absolute values. This means
  // that 'em' units (for instance) are converted to 'px ' and calc()-expressions
  // are resolved. This function creates new tokens equivalent to the computed
  // value of the registered property.
  //
  // This is necessary to make things like font-relative units in inherited
  // (and registered) custom properties work correctly.
  //
  // https://drafts.css-houdini.org/css-properties-values-api-1/#substitution
  scoped_refptr<CSSVariableData>
  StyleBuilderConverter::ConvertRegisteredPropertyVariableData(
      const CSSValue& value,
      bool is_animation_tainted) {
    // TODO(andruud): Produce tokens directly from CSSValue.
    String text = value.CssText();
  
    CSSTokenizer tokenizer(text);
    Vector<CSSParserToken> tokens;
    tokens.AppendVector(tokenizer.TokenizeToEOF());
  
    Vector<String> backing_strings;
    backing_strings.push_back(text);
  
    const bool has_font_units = false;
    const bool has_root_font_units = false;
  
    return CSSVariableData::CreateResolved(
        std::move(tokens), std::move(backing_strings), is_animation_tainted,
        has_font_units, has_root_font_units, g_null_atom, WTF::TextEncoding());
  }
  
  LengthSize StyleBuilderConverter::ConvertIntrinsicSize(
      StyleResolverState& state,
      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kAuto)
      return LengthSize(Length::Auto(), Length::Auto());
    const CSSValuePair& pair = To<CSSValuePair>(value);
    Length width = ConvertLength(state, pair.First());
    Length height = ConvertLength(state, pair.Second());
    return LengthSize(width, height);
  }
  
  namespace {
  FloatSize GetRatioFromList(const CSSValueList& list) {
    auto* ratio_list = DynamicTo<CSSValueList>(list.Item(0));
    if (!ratio_list) {
      DCHECK_EQ(list.length(), 2u);
      ratio_list = DynamicTo<CSSValueList>(list.Item(1));
    }
    DCHECK(ratio_list);
    DCHECK_GE(ratio_list->length(), 1u);
    DCHECK_LE(ratio_list->length(), 2u);
    float width = To<CSSPrimitiveValue>(ratio_list->Item(0)).GetFloatValue();
    float height = 1;
    if (ratio_list->length() == 2u)
      height = To<CSSPrimitiveValue>(ratio_list->Item(1)).GetFloatValue();
    return FloatSize(width, height);
  }
  
  bool ListHasAuto(const CSSValueList& list) {
    // If there's only one entry, it needs to be a ratio.
    // (A single auto is handled separately)
    if (list.length() == 1u)
      return false;
    auto* auto_value = DynamicTo<CSSIdentifierValue>(list.Item(0));
    if (!auto_value)
      auto_value = DynamicTo<CSSIdentifierValue>(list.Item(1));
    DCHECK(auto_value) << "If we have two items, one of them must be auto";
    DCHECK_EQ(auto_value->GetValueID(), CSSValueID::kAuto);
    return true;
  }
  }  // namespace
  
  StyleAspectRatio StyleBuilderConverter::ConvertAspectRatio(
      const StyleResolverState& state,
      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    if (identifier_value && identifier_value->GetValueID() == CSSValueID::kAuto)
      return StyleAspectRatio(EAspectRatioType::kAuto, FloatSize());
  
    // (auto, (1, 2)) or ((1, 2), auto) or ((1, 2))
    const CSSValueList& list = To<CSSValueList>(value);
    DCHECK_GE(list.length(), 1u);
    DCHECK_LE(list.length(), 2u);
  
    bool has_auto = ListHasAuto(list);
    EAspectRatioType type =
        has_auto ? EAspectRatioType::kAutoAndRatio : EAspectRatioType::kRatio;
    FloatSize ratio = GetRatioFromList(list);
    return StyleAspectRatio(type, ratio);
  }
  
  bool StyleBuilderConverter::ConvertInternalAlignSelfBlock(
      StyleResolverState&,
      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    return identifier_value &&
           identifier_value->GetValueID() == CSSValueID::kCenter;
  }
  
  bool StyleBuilderConverter::ConvertInternalEmptyLineHeight(
      StyleResolverState&,
      const CSSValue& value) {
    auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
    return identifier_value &&
           identifier_value->GetValueID() == CSSValueID::kFabricated;
  }
  
  AtomicString StyleBuilderConverter::ConvertPage(StyleResolverState& state,
                                                  const CSSValue& value) {
    if (auto* custom_ident_value = DynamicTo<CSSCustomIdentValue>(value))
      return AtomicString(custom_ident_value->Value());
    DCHECK(DynamicTo<CSSIdentifierValue>(value));
    DCHECK_EQ(DynamicTo<CSSIdentifierValue>(value)->GetValueID(),
              CSSValueID::kAuto);
    return AtomicString();
  }
  
  RubyPosition StyleBuilderConverter::ConvertRubyPosition(
      StyleResolverState& state,
      const CSSValue& value) {
    if (const auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
      CSSValueID value_id = identifier_value->GetValueID();
      if (value_id == CSSValueID::kOver)
        return RubyPosition::kBefore;
      if (value_id == CSSValueID::kUnder)
        return RubyPosition::kAfter;
      return identifier_value->ConvertTo<blink::RubyPosition>();
    }
    NOTREACHED();
    return RubyPosition::kBefore;
  }
  
  ScrollbarGutter StyleBuilderConverter::ConvertScrollbarGutter(
      StyleResolverState& state,
      const CSSValue& value) {
    ScrollbarGutter flags = kScrollbarGutterAuto;
  
    auto process = [&flags](const CSSValue& identifier) {
      flags |= To<CSSIdentifierValue>(identifier).ConvertTo<ScrollbarGutter>();
    };
  
    if (auto* value_list = DynamicTo<CSSValueList>(value)) {
      for (auto& entry : *value_list)
        process(*entry);
    } else {
      process(value);
    }
    return flags;
  }
  
  AtomicString StyleBuilderConverter::ConvertContainerName(
      StyleResolverState& state,
      const CSSValue& value) {
    if (auto* custom_ident_value = DynamicTo<CSSCustomIdentValue>(value))
      return AtomicString(custom_ident_value->Value());
    DCHECK_EQ(To<CSSIdentifierValue>(value).GetValueID(), CSSValueID::kNone);
    return AtomicString();
  }
  
  }  // namespace blink
  