| /* |
| * 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_value.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_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); |
| 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) { |
| 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(); |
| } |
| |
| 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); |
| } |
| |
| 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; |
| } |
| } |
| 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* 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* 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) { |
| 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 (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(); |
| } |
| |
| 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(); |
| } |
| |
| 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()); |
| } |
| } |
| } |
| |
| float 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 1.0; |
| return clampTo<float>(result, defaultMinimumForClamp<float>(), |
| defaultMaximumForClamp<float>()); |
| } |
| |
| LayoutUnit StyleBuilderConverter::ConvertLayoutUnit(StyleResolverState& state, |
| const CSSValue& value) { |
| return LayoutUnit::Clamp(ConvertComputedLength<float>(state, value)); |
| } |
| |
| base::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 base::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()))); |
| } |
| } |
| |
| 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::CSSColorValue>(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, ComputedStyle::InitialStyle().UsedColorScheme())); |
| } |
| } |
| } |
| } |
| |
| 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); |
| if (RuntimeEnabledFeatures::CSSSystemColorComputeToSelfEnabled()) |
| return StyleAutoColor(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::CSSColorValue::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; |
| } |
| |
| } // namespace blink |