| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h" |
| |
| #include "third_party/blink/renderer/core/css/basic_shape_functions.h" |
| #include "third_party/blink/renderer/core/css/css_border_image.h" |
| #include "third_party/blink/renderer/core/css/css_border_image_slice_value.h" |
| #include "third_party/blink/renderer/core/css/css_color_value.h" |
| #include "third_party/blink/renderer/core/css/css_counter_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_style_range_value.h" |
| #include "third_party/blink/renderer/core/css/css_function_value.h" |
| #include "third_party/blink/renderer/core/css/css_grid_auto_repeat_value.h" |
| #include "third_party/blink/renderer/core/css/css_grid_line_names_value.h" |
| #include "third_party/blink/renderer/core/css/css_initial_value.h" |
| #include "third_party/blink/renderer/core/css/css_numeric_literal_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_string_value.h" |
| #include "third_party/blink/renderer/core/css/css_timing_function_value.h" |
| #include "third_party/blink/renderer/core/css/css_uri_value.h" |
| #include "third_party/blink/renderer/core/css/css_value.h" |
| #include "third_party/blink/renderer/core/css/css_value_list.h" |
| #include "third_party/blink/renderer/core/css/css_value_pair.h" |
| #include "third_party/blink/renderer/core/css/cssom/cross_thread_color_value.h" |
| #include "third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.h" |
| #include "third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.h" |
| #include "third_party/blink/renderer/core/css/cssom/cross_thread_unparsed_value.h" |
| #include "third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.h" |
| #include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h" |
| #include "third_party/blink/renderer/core/css/cssom/css_unit_value.h" |
| #include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h" |
| #include "third_party/blink/renderer/core/css/cssom/css_unsupported_color_value.h" |
| #include "third_party/blink/renderer/core/css/style_color.h" |
| #include "third_party/blink/renderer/core/layout/layout_block.h" |
| #include "third_party/blink/renderer/core/layout/layout_box.h" |
| #include "third_party/blink/renderer/core/layout/layout_grid.h" |
| #include "third_party/blink/renderer/core/layout/svg/transform_helper.h" |
| #include "third_party/blink/renderer/core/style/computed_style_constants.h" |
| #include "third_party/blink/renderer/core/style/style_svg_resource.h" |
| #include "third_party/blink/renderer/core/style_property_shorthand.h" |
| #include "third_party/blink/renderer/core/svg_element_type_helpers.h" |
| #include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h" |
| #include "third_party/blink/renderer/platform/transforms/matrix_transform_operation.h" |
| #include "third_party/blink/renderer/platform/transforms/perspective_transform_operation.h" |
| #include "third_party/blink/renderer/platform/transforms/skew_transform_operation.h" |
| |
| namespace blink { |
| |
| // TODO(rjwright): make this const |
| CSSValue* ComputedStyleUtils::ZoomAdjustedPixelValueForLength( |
| const Length& length, |
| const ComputedStyle& style) { |
| if (length.IsFixed()) |
| return ZoomAdjustedPixelValue(length.Value(), style); |
| return CSSValue::Create(length, style.EffectiveZoom()); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForPosition(const LengthPoint& position, |
| const ComputedStyle& style) { |
| DCHECK_EQ(position.X().IsAuto(), position.Y().IsAuto()); |
| if (position.X().IsAuto()) |
| return CSSIdentifierValue::Create(CSSValueID::kAuto); |
| |
| return MakeGarbageCollected<CSSValuePair>( |
| ZoomAdjustedPixelValueForLength(position.X(), style), |
| ZoomAdjustedPixelValueForLength(position.Y(), style), |
| CSSValuePair::kKeepIdenticalValues); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForOffset(const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) { |
| CSSValue* position = ValueForPosition(style.OffsetPosition(), style); |
| auto* position_identifier_value = DynamicTo<CSSIdentifierValue>(position); |
| if (!position_identifier_value) |
| list->Append(*position); |
| else |
| DCHECK(position_identifier_value->GetValueID() == CSSValueID::kAuto); |
| } |
| |
| static const CSSProperty* longhands[3] = {&GetCSSPropertyOffsetPath(), |
| &GetCSSPropertyOffsetDistance(), |
| &GetCSSPropertyOffsetRotate()}; |
| for (const CSSProperty* longhand : longhands) { |
| const CSSValue* value = longhand->CSSValueFromComputedStyle( |
| style, layout_object, allow_visited_style); |
| DCHECK(value); |
| list->Append(*value); |
| } |
| |
| if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) { |
| CSSValue* anchor = ValueForPosition(style.OffsetAnchor(), style); |
| auto* anchor_identifier_value = DynamicTo<CSSIdentifierValue>(anchor); |
| if (!anchor_identifier_value) { |
| // Add a slash before anchor. |
| CSSValueList* result = CSSValueList::CreateSlashSeparated(); |
| result->Append(*list); |
| result->Append(*anchor); |
| return result; |
| } |
| DCHECK(anchor_identifier_value->GetValueID() == CSSValueID::kAuto); |
| } |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::CurrentColorOrValidColor( |
| const ComputedStyle& style, |
| const StyleColor& color) { |
| // This function does NOT look at visited information, so that computed style |
| // doesn't expose that. |
| return cssvalue::CSSColorValue::Create(color.Resolve(style.GetColor()).Rgb()); |
| } |
| |
| const blink::Color ComputedStyleUtils::BorderSideColor( |
| const ComputedStyle& style, |
| const StyleColor& color, |
| EBorderStyle border_style, |
| bool visited_link) { |
| if (!color.IsCurrentColor()) |
| return color.GetColor(); |
| // FIXME: Treating styled borders with initial color differently causes |
| // problems, see crbug.com/316559, crbug.com/276231 |
| if (!visited_link && (border_style == EBorderStyle::kInset || |
| border_style == EBorderStyle::kOutset || |
| border_style == EBorderStyle::kRidge || |
| border_style == EBorderStyle::kGroove)) |
| return blink::Color(238, 238, 238); |
| return visited_link ? style.InternalVisitedColor() : style.GetColor(); |
| } |
| |
| const CSSValue* ComputedStyleUtils::BackgroundImageOrWebkitMaskImage( |
| const ComputedStyle& style, |
| bool allow_visited_style, |
| const FillLayer& fill_layer) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| const FillLayer* curr_layer = &fill_layer; |
| for (; curr_layer; curr_layer = curr_layer->Next()) { |
| if (curr_layer->GetImage()) { |
| list->Append(*curr_layer->GetImage()->ComputedCSSValue( |
| style, allow_visited_style)); |
| } else { |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone)); |
| } |
| } |
| return list; |
| } |
| |
| const CSSValue* ComputedStyleUtils::ValueForFillSize( |
| const FillSize& fill_size, |
| const ComputedStyle& style) { |
| if (fill_size.type == EFillSizeType::kContain) |
| return CSSIdentifierValue::Create(CSSValueID::kContain); |
| |
| if (fill_size.type == EFillSizeType::kCover) |
| return CSSIdentifierValue::Create(CSSValueID::kCover); |
| |
| if (fill_size.size.Height().IsAuto()) { |
| return ZoomAdjustedPixelValueForLength(fill_size.size.Width(), style); |
| } |
| |
| return MakeGarbageCollected<CSSValuePair>( |
| ZoomAdjustedPixelValueForLength(fill_size.size.Width(), style), |
| ZoomAdjustedPixelValueForLength(fill_size.size.Height(), style), |
| CSSValuePair::kKeepIdenticalValues); |
| } |
| |
| const CSSValue* ComputedStyleUtils::BackgroundImageOrWebkitMaskSize( |
| const ComputedStyle& style, |
| const FillLayer& fill_layer) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| const FillLayer* curr_layer = &fill_layer; |
| for (; curr_layer; curr_layer = curr_layer->Next()) |
| list->Append(*ValueForFillSize(curr_layer->Size(), style)); |
| return list; |
| } |
| |
| const CSSValueList* ComputedStyleUtils::CreatePositionListForLayer( |
| const CSSProperty& property, |
| const FillLayer& layer, |
| const ComputedStyle& style) { |
| CSSValueList* position_list = CSSValueList::CreateSpaceSeparated(); |
| if (layer.IsBackgroundXOriginSet()) { |
| DCHECK(property.IDEquals(CSSPropertyID::kBackgroundPosition) || |
| property.IDEquals(CSSPropertyID::kWebkitMaskPosition)); |
| position_list->Append( |
| *CSSIdentifierValue::Create(layer.BackgroundXOrigin())); |
| } |
| position_list->Append( |
| *ZoomAdjustedPixelValueForLength(layer.PositionX(), style)); |
| if (layer.IsBackgroundYOriginSet()) { |
| DCHECK(property.IDEquals(CSSPropertyID::kBackgroundPosition) || |
| property.IDEquals(CSSPropertyID::kWebkitMaskPosition)); |
| position_list->Append( |
| *CSSIdentifierValue::Create(layer.BackgroundYOrigin())); |
| } |
| position_list->Append( |
| *ZoomAdjustedPixelValueForLength(layer.PositionY(), style)); |
| return position_list; |
| } |
| |
| const CSSValue* ComputedStyleUtils::ValueForFillRepeat(EFillRepeat x_repeat, |
| EFillRepeat y_repeat) { |
| // For backwards compatibility, if both values are equal, just return one of |
| // them. And if the two values are equivalent to repeat-x or repeat-y, just |
| // return the shorthand. |
| if (x_repeat == y_repeat) |
| return CSSIdentifierValue::Create(x_repeat); |
| if (x_repeat == EFillRepeat::kRepeatFill && |
| y_repeat == EFillRepeat::kNoRepeatFill) |
| return CSSIdentifierValue::Create(CSSValueID::kRepeatX); |
| if (x_repeat == EFillRepeat::kNoRepeatFill && |
| y_repeat == EFillRepeat::kRepeatFill) |
| return CSSIdentifierValue::Create(CSSValueID::kRepeatY); |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| list->Append(*CSSIdentifierValue::Create(x_repeat)); |
| list->Append(*CSSIdentifierValue::Create(y_repeat)); |
| return list; |
| } |
| |
| const CSSValueList* ComputedStyleUtils::ValuesForBackgroundShorthand( |
| const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| CSSValueList* result = CSSValueList::CreateCommaSeparated(); |
| const FillLayer* curr_layer = &style.BackgroundLayers(); |
| for (; curr_layer; curr_layer = curr_layer->Next()) { |
| CSSValueList* list = CSSValueList::CreateSlashSeparated(); |
| CSSValueList* before_slash = CSSValueList::CreateSpaceSeparated(); |
| if (!curr_layer->Next()) { // color only for final layer |
| const CSSValue* value = |
| GetCSSPropertyBackgroundColor().CSSValueFromComputedStyle( |
| style, layout_object, allow_visited_style); |
| DCHECK(value); |
| before_slash->Append(*value); |
| } |
| before_slash->Append(curr_layer->GetImage() |
| ? *curr_layer->GetImage()->ComputedCSSValue( |
| style, allow_visited_style) |
| : *CSSIdentifierValue::Create(CSSValueID::kNone)); |
| before_slash->Append( |
| *ValueForFillRepeat(curr_layer->RepeatX(), curr_layer->RepeatY())); |
| before_slash->Append(*CSSIdentifierValue::Create(curr_layer->Attachment())); |
| before_slash->Append(*CreatePositionListForLayer( |
| GetCSSPropertyBackgroundPosition(), *curr_layer, style)); |
| list->Append(*before_slash); |
| CSSValueList* after_slash = CSSValueList::CreateSpaceSeparated(); |
| after_slash->Append(*ValueForFillSize(curr_layer->Size(), style)); |
| after_slash->Append(*CSSIdentifierValue::Create(curr_layer->Origin())); |
| after_slash->Append(*CSSIdentifierValue::Create(curr_layer->Clip())); |
| list->Append(*after_slash); |
| result->Append(*list); |
| } |
| return result; |
| } |
| |
| const CSSValue* ComputedStyleUtils::BackgroundRepeatOrWebkitMaskRepeat( |
| const FillLayer* curr_layer) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| for (; curr_layer; curr_layer = curr_layer->Next()) { |
| list->Append( |
| *ValueForFillRepeat(curr_layer->RepeatX(), curr_layer->RepeatY())); |
| } |
| return list; |
| } |
| |
| const CSSValue* ComputedStyleUtils::BackgroundPositionOrWebkitMaskPosition( |
| const CSSProperty& resolved_property, |
| const ComputedStyle& style, |
| const FillLayer* curr_layer) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| for (; curr_layer; curr_layer = curr_layer->Next()) { |
| list->Append( |
| *CreatePositionListForLayer(resolved_property, *curr_layer, style)); |
| } |
| return list; |
| } |
| |
| const CSSValue* ComputedStyleUtils::BackgroundPositionXOrWebkitMaskPositionX( |
| const ComputedStyle& style, |
| const FillLayer* curr_layer) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| for (; curr_layer; curr_layer = curr_layer->Next()) { |
| const Length& from_edge = curr_layer->PositionX(); |
| if (curr_layer->BackgroundXOrigin() == BackgroundEdgeOrigin::kRight) { |
| // TODO(crbug.com/610627): This should use two-value syntax once the |
| // parser accepts it. |
| list->Append(*ZoomAdjustedPixelValueForLength( |
| from_edge.SubtractFromOneHundredPercent(), style)); |
| } else { |
| list->Append(*ZoomAdjustedPixelValueForLength(from_edge, style)); |
| } |
| } |
| return list; |
| } |
| |
| const CSSValue* ComputedStyleUtils::BackgroundPositionYOrWebkitMaskPositionY( |
| const ComputedStyle& style, |
| const FillLayer* curr_layer) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| for (; curr_layer; curr_layer = curr_layer->Next()) { |
| const Length& from_edge = curr_layer->PositionY(); |
| if (curr_layer->BackgroundYOrigin() == BackgroundEdgeOrigin::kBottom) { |
| // TODO(crbug.com/610627): This should use two-value syntax once the |
| // parser accepts it. |
| list->Append(*ZoomAdjustedPixelValueForLength( |
| from_edge.SubtractFromOneHundredPercent(), style)); |
| } else { |
| list->Append(*ZoomAdjustedPixelValueForLength(from_edge, style)); |
| } |
| } |
| return list; |
| } |
| |
| cssvalue::CSSBorderImageSliceValue* |
| ComputedStyleUtils::ValueForNinePieceImageSlice(const NinePieceImage& image) { |
| // Create the slices. |
| CSSPrimitiveValue* top = nullptr; |
| CSSPrimitiveValue* right = nullptr; |
| CSSPrimitiveValue* bottom = nullptr; |
| CSSPrimitiveValue* left = nullptr; |
| |
| // TODO(alancutter): Make this code aware of calc lengths. |
| if (image.ImageSlices().Top().IsPercentOrCalc()) { |
| top = CSSNumericLiteralValue::Create( |
| image.ImageSlices().Top().Value(), |
| CSSPrimitiveValue::UnitType::kPercentage); |
| } else { |
| top = CSSNumericLiteralValue::Create(image.ImageSlices().Top().Value(), |
| CSSPrimitiveValue::UnitType::kNumber); |
| } |
| |
| if (image.ImageSlices().Right() == image.ImageSlices().Top() && |
| image.ImageSlices().Bottom() == image.ImageSlices().Top() && |
| image.ImageSlices().Left() == image.ImageSlices().Top()) { |
| right = top; |
| bottom = top; |
| left = top; |
| } else { |
| if (image.ImageSlices().Right().IsPercentOrCalc()) { |
| right = CSSNumericLiteralValue::Create( |
| image.ImageSlices().Right().Value(), |
| CSSPrimitiveValue::UnitType::kPercentage); |
| } else { |
| right = |
| CSSNumericLiteralValue::Create(image.ImageSlices().Right().Value(), |
| CSSPrimitiveValue::UnitType::kNumber); |
| } |
| |
| if (image.ImageSlices().Bottom() == image.ImageSlices().Top() && |
| image.ImageSlices().Right() == image.ImageSlices().Left()) { |
| bottom = top; |
| left = right; |
| } else { |
| if (image.ImageSlices().Bottom().IsPercentOrCalc()) { |
| bottom = CSSNumericLiteralValue::Create( |
| image.ImageSlices().Bottom().Value(), |
| CSSPrimitiveValue::UnitType::kPercentage); |
| } else { |
| bottom = CSSNumericLiteralValue::Create( |
| image.ImageSlices().Bottom().Value(), |
| CSSPrimitiveValue::UnitType::kNumber); |
| } |
| |
| if (image.ImageSlices().Left() == image.ImageSlices().Right()) { |
| left = right; |
| } else { |
| if (image.ImageSlices().Left().IsPercentOrCalc()) { |
| left = CSSNumericLiteralValue::Create( |
| image.ImageSlices().Left().Value(), |
| CSSPrimitiveValue::UnitType::kPercentage); |
| } else { |
| left = CSSNumericLiteralValue::Create( |
| image.ImageSlices().Left().Value(), |
| CSSPrimitiveValue::UnitType::kNumber); |
| } |
| } |
| } |
| } |
| |
| return MakeGarbageCollected<cssvalue::CSSBorderImageSliceValue>( |
| MakeGarbageCollected<CSSQuadValue>(top, right, bottom, left, |
| CSSQuadValue::kSerializeAsQuad), |
| image.Fill()); |
| } |
| |
| CSSValue* ValueForBorderImageLength( |
| const BorderImageLength& border_image_length, |
| const ComputedStyle& style) { |
| if (border_image_length.IsNumber()) { |
| return CSSNumericLiteralValue::Create(border_image_length.Number(), |
| CSSPrimitiveValue::UnitType::kNumber); |
| } |
| return CSSValue::Create(border_image_length.length(), style.EffectiveZoom()); |
| } |
| |
| CSSQuadValue* ComputedStyleUtils::ValueForNinePieceImageQuad( |
| const BorderImageLengthBox& box, |
| const ComputedStyle& style) { |
| // Create the slices. |
| CSSValue* top = nullptr; |
| CSSValue* right = nullptr; |
| CSSValue* bottom = nullptr; |
| CSSValue* left = nullptr; |
| |
| top = ValueForBorderImageLength(box.Top(), style); |
| |
| if (box.Right() == box.Top() && box.Bottom() == box.Top() && |
| box.Left() == box.Top()) { |
| right = top; |
| bottom = top; |
| left = top; |
| } else { |
| right = ValueForBorderImageLength(box.Right(), style); |
| |
| if (box.Bottom() == box.Top() && box.Right() == box.Left()) { |
| bottom = top; |
| left = right; |
| } else { |
| bottom = ValueForBorderImageLength(box.Bottom(), style); |
| |
| if (box.Left() == box.Right()) |
| left = right; |
| else |
| left = ValueForBorderImageLength(box.Left(), style); |
| } |
| } |
| return MakeGarbageCollected<CSSQuadValue>(top, right, bottom, left, |
| CSSQuadValue::kSerializeAsQuad); |
| } |
| |
| CSSValueID ValueForRepeatRule(int rule) { |
| switch (rule) { |
| case kRepeatImageRule: |
| return CSSValueID::kRepeat; |
| case kRoundImageRule: |
| return CSSValueID::kRound; |
| case kSpaceImageRule: |
| return CSSValueID::kSpace; |
| default: |
| return CSSValueID::kStretch; |
| } |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForNinePieceImageRepeat( |
| const NinePieceImage& image) { |
| CSSIdentifierValue* horizontal_repeat = nullptr; |
| CSSIdentifierValue* vertical_repeat = nullptr; |
| |
| horizontal_repeat = |
| CSSIdentifierValue::Create(ValueForRepeatRule(image.HorizontalRule())); |
| if (image.HorizontalRule() == image.VerticalRule()) { |
| vertical_repeat = horizontal_repeat; |
| } else { |
| vertical_repeat = |
| CSSIdentifierValue::Create(ValueForRepeatRule(image.VerticalRule())); |
| } |
| return MakeGarbageCollected<CSSValuePair>(horizontal_repeat, vertical_repeat, |
| CSSValuePair::kDropIdenticalValues); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForNinePieceImage( |
| const NinePieceImage& image, |
| const ComputedStyle& style, |
| bool allow_visited_style) { |
| if (!image.HasImage()) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| // Image first. |
| CSSValue* image_value = nullptr; |
| if (image.GetImage()) { |
| image_value = |
| image.GetImage()->ComputedCSSValue(style, allow_visited_style); |
| } |
| |
| // Create the image slice. |
| cssvalue::CSSBorderImageSliceValue* image_slices = |
| ValueForNinePieceImageSlice(image); |
| |
| // Create the border area slices. |
| CSSValue* border_slices = |
| ValueForNinePieceImageQuad(image.BorderSlices(), style); |
| |
| // Create the border outset. |
| CSSValue* outset = ValueForNinePieceImageQuad(image.Outset(), style); |
| |
| // Create the repeat rules. |
| CSSValue* repeat = ValueForNinePieceImageRepeat(image); |
| |
| return CreateBorderImageValue(image_value, image_slices, border_slices, |
| outset, repeat); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForReflection( |
| const StyleReflection* reflection, |
| const ComputedStyle& style, |
| bool allow_visited_style) { |
| if (!reflection) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| CSSPrimitiveValue* offset = nullptr; |
| // TODO(alancutter): Make this work correctly for calc lengths. |
| if (reflection->Offset().IsPercentOrCalc()) { |
| offset = CSSNumericLiteralValue::Create( |
| reflection->Offset().Percent(), |
| CSSPrimitiveValue::UnitType::kPercentage); |
| } else { |
| offset = ZoomAdjustedPixelValue(reflection->Offset().Value(), style); |
| } |
| |
| CSSIdentifierValue* direction = nullptr; |
| switch (reflection->Direction()) { |
| case kReflectionBelow: |
| direction = CSSIdentifierValue::Create(CSSValueID::kBelow); |
| break; |
| case kReflectionAbove: |
| direction = CSSIdentifierValue::Create(CSSValueID::kAbove); |
| break; |
| case kReflectionLeft: |
| direction = CSSIdentifierValue::Create(CSSValueID::kLeft); |
| break; |
| case kReflectionRight: |
| direction = CSSIdentifierValue::Create(CSSValueID::kRight); |
| break; |
| } |
| |
| return MakeGarbageCollected<cssvalue::CSSReflectValue>( |
| direction, offset, |
| ValueForNinePieceImage(reflection->Mask(), style, allow_visited_style)); |
| } |
| |
| CSSValue* ComputedStyleUtils::MinWidthOrMinHeightAuto( |
| const ComputedStyle& style) { |
| if (style.IsFlexOrGridOrCustomItem() && !style.IsEnsuredInDisplayNone()) |
| return CSSIdentifierValue::Create(CSSValueID::kAuto); |
| return ZoomAdjustedPixelValue(0, style); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForPositionOffset( |
| const ComputedStyle& style, |
| const CSSProperty& property, |
| const LayoutObject* layout_object) { |
| std::pair<const Length*, const Length*> positions; |
| bool is_horizontal_property; |
| switch (property.PropertyID()) { |
| case CSSPropertyID::kLeft: |
| positions = std::make_pair(&style.Left(), &style.Right()); |
| is_horizontal_property = true; |
| break; |
| case CSSPropertyID::kRight: |
| positions = std::make_pair(&style.Right(), &style.Left()); |
| is_horizontal_property = true; |
| break; |
| case CSSPropertyID::kTop: |
| positions = std::make_pair(&style.Top(), &style.Bottom()); |
| is_horizontal_property = false; |
| break; |
| case CSSPropertyID::kBottom: |
| positions = std::make_pair(&style.Bottom(), &style.Top()); |
| is_horizontal_property = false; |
| break; |
| default: |
| NOTREACHED(); |
| return nullptr; |
| } |
| DCHECK(positions.first && positions.second); |
| |
| const Length& offset = *positions.first; |
| const Length& opposite = *positions.second; |
| |
| if (offset.IsPercentOrCalc() && layout_object && layout_object->IsBox() && |
| layout_object->IsPositioned()) { |
| LayoutUnit containing_block_size; |
| if (layout_object->IsStickyPositioned()) { |
| const LayoutBox& enclosing_scrollport_box = |
| ToLayoutBox(layout_object)->EnclosingScrollportBox(); |
| bool use_inline_size = is_horizontal_property == |
| enclosing_scrollport_box.IsHorizontalWritingMode(); |
| containing_block_size = |
| use_inline_size ? enclosing_scrollport_box.ContentLogicalWidth() |
| : enclosing_scrollport_box.ContentLogicalHeight(); |
| } else { |
| containing_block_size = |
| is_horizontal_property == |
| layout_object->ContainingBlock()->IsHorizontalWritingMode() |
| ? ToLayoutBox(layout_object) |
| ->ContainingBlockLogicalWidthForContent() |
| : ToLayoutBox(layout_object) |
| ->ContainingBlockLogicalHeightForGetComputedStyle(); |
| } |
| |
| return ZoomAdjustedPixelValue(ValueForLength(offset, containing_block_size), |
| style); |
| } |
| |
| if (offset.IsAuto() && layout_object) { |
| // If the property applies to a positioned element and the resolved value of |
| // the display property is not none, the resolved value is the used value. |
| // Position offsets have special meaning for position sticky so we return |
| // auto when offset.isAuto() on a sticky position object: |
| // https://crbug.com/703816. |
| if (layout_object->IsRelPositioned()) { |
| // If e.g. left is auto and right is not auto, then left's computed value |
| // is negative right. So we get the opposite length unit and see if it is |
| // auto. |
| if (opposite.IsAuto()) { |
| return CSSNumericLiteralValue::Create( |
| 0, CSSPrimitiveValue::UnitType::kPixels); |
| } |
| |
| if (opposite.IsPercentOrCalc()) { |
| if (layout_object->IsBox()) { |
| LayoutUnit containing_block_size = |
| is_horizontal_property == layout_object->ContainingBlock() |
| ->IsHorizontalWritingMode() |
| ? ToLayoutBox(layout_object) |
| ->ContainingBlockLogicalWidthForContent() |
| : ToLayoutBox(layout_object) |
| ->ContainingBlockLogicalHeightForGetComputedStyle(); |
| return ZoomAdjustedPixelValue( |
| -FloatValueForLength(opposite, containing_block_size), style); |
| } |
| // FIXME: fall back to auto for position:relative, display:inline |
| return CSSIdentifierValue::Create(CSSValueID::kAuto); |
| } |
| |
| // Length doesn't provide operator -, so multiply by -1. |
| Length negated_opposite = opposite; |
| negated_opposite *= -1.f; |
| return ZoomAdjustedPixelValueForLength(negated_opposite, style); |
| } |
| |
| if (layout_object->IsOutOfFlowPositioned() && layout_object->IsBox()) { |
| // For fixed and absolute positioned elements, the top, left, bottom, and |
| // right are defined relative to the corresponding sides of the containing |
| // block. |
| LayoutBlock* container = layout_object->ContainingBlock(); |
| const LayoutBox* layout_box = ToLayoutBox(layout_object); |
| |
| // clientOffset is the distance from this object's border edge to the |
| // container's padding edge. Thus it includes margins which we subtract |
| // below. |
| const LayoutSize client_offset = |
| layout_box->LocationOffset() - |
| LayoutSize(container->ClientLeft(), container->ClientTop()); |
| LayoutUnit position; |
| |
| switch (property.PropertyID()) { |
| case CSSPropertyID::kLeft: |
| position = client_offset.Width() - layout_box->MarginLeft(); |
| break; |
| case CSSPropertyID::kTop: |
| position = client_offset.Height() - layout_box->MarginTop(); |
| break; |
| case CSSPropertyID::kRight: |
| position = container->ClientWidth() - layout_box->MarginRight() - |
| (layout_box->OffsetWidth() + client_offset.Width()); |
| break; |
| case CSSPropertyID::kBottom: |
| position = container->ClientHeight() - layout_box->MarginBottom() - |
| (layout_box->OffsetHeight() + client_offset.Height()); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| return ZoomAdjustedPixelValue(position, style); |
| } |
| } |
| |
| if (offset.IsAuto()) |
| return CSSIdentifierValue::Create(CSSValueID::kAuto); |
| |
| return ZoomAdjustedPixelValueForLength(offset, style); |
| } |
| |
| CSSValueList* ComputedStyleUtils::ValueForItemPositionWithOverflowAlignment( |
| const StyleSelfAlignmentData& data) { |
| CSSValueList* result = CSSValueList::CreateSpaceSeparated(); |
| if (data.PositionType() == ItemPositionType::kLegacy) |
| result->Append(*CSSIdentifierValue::Create(CSSValueID::kLegacy)); |
| if (data.GetPosition() == ItemPosition::kBaseline) { |
| result->Append(*MakeGarbageCollected<CSSValuePair>( |
| CSSIdentifierValue::Create(CSSValueID::kBaseline), |
| CSSIdentifierValue::Create(CSSValueID::kBaseline), |
| CSSValuePair::kDropIdenticalValues)); |
| } else if (data.GetPosition() == ItemPosition::kLastBaseline) { |
| result->Append(*MakeGarbageCollected<CSSValuePair>( |
| CSSIdentifierValue::Create(CSSValueID::kLast), |
| CSSIdentifierValue::Create(CSSValueID::kBaseline), |
| CSSValuePair::kDropIdenticalValues)); |
| } else { |
| if (data.GetPosition() >= ItemPosition::kCenter && |
| data.Overflow() != OverflowAlignment::kDefault) |
| result->Append(*CSSIdentifierValue::Create(data.Overflow())); |
| if (data.GetPosition() == ItemPosition::kLegacy) |
| result->Append(*CSSIdentifierValue::Create(CSSValueID::kNormal)); |
| else |
| result->Append(*CSSIdentifierValue::Create(data.GetPosition())); |
| } |
| DCHECK_LE(result->length(), 2u); |
| return result; |
| } |
| |
| CSSValueList* |
| ComputedStyleUtils::ValueForContentPositionAndDistributionWithOverflowAlignment( |
| const StyleContentAlignmentData& data) { |
| CSSValueList* result = CSSValueList::CreateSpaceSeparated(); |
| // Handle content-distribution values |
| if (data.Distribution() != ContentDistributionType::kDefault) |
| result->Append(*CSSIdentifierValue::Create(data.Distribution())); |
| |
| // Handle content-position values (either as fallback or actual value) |
| switch (data.GetPosition()) { |
| case ContentPosition::kNormal: |
| // Handle 'normal' value, not valid as content-distribution fallback. |
| if (data.Distribution() == ContentDistributionType::kDefault) { |
| result->Append(*CSSIdentifierValue::Create(CSSValueID::kNormal)); |
| } |
| break; |
| case ContentPosition::kLastBaseline: |
| result->Append(*MakeGarbageCollected<CSSValuePair>( |
| CSSIdentifierValue::Create(CSSValueID::kLast), |
| CSSIdentifierValue::Create(CSSValueID::kBaseline), |
| CSSValuePair::kDropIdenticalValues)); |
| break; |
| default: |
| // Handle overflow-alignment (only allowed for content-position values) |
| if ((data.GetPosition() >= ContentPosition::kCenter || |
| data.Distribution() != ContentDistributionType::kDefault) && |
| data.Overflow() != OverflowAlignment::kDefault) |
| result->Append(*CSSIdentifierValue::Create(data.Overflow())); |
| result->Append(*CSSIdentifierValue::Create(data.GetPosition())); |
| } |
| |
| DCHECK_GT(result->length(), 0u); |
| DCHECK_LE(result->length(), 3u); |
| return result; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForLineHeight(const ComputedStyle& style) { |
| const Length& length = style.LineHeight(); |
| if (length.IsNegative()) |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| |
| return ZoomAdjustedPixelValue( |
| FloatValueForLength(length, style.GetFontDescription().ComputedSize()), |
| style); |
| } |
| |
| CSSValue* ComputedStyleUtils::ComputedValueForLineHeight( |
| const ComputedStyle& style) { |
| const Length& length = style.LineHeight(); |
| if (length.IsNegative()) |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| |
| if (length.IsPercent()) { |
| return CSSNumericLiteralValue::Create(length.GetFloatValue() / 100.0, |
| CSSPrimitiveValue::UnitType::kNumber); |
| } else { |
| return ZoomAdjustedPixelValue( |
| FloatValueForLength(length, style.GetFontDescription().ComputedSize()), |
| style); |
| } |
| } |
| |
| CSSValueID IdentifierForFamily(const AtomicString& family) { |
| if (family == font_family_names::kWebkitCursive) |
| return CSSValueID::kCursive; |
| if (family == font_family_names::kWebkitFantasy) |
| return CSSValueID::kFantasy; |
| if (family == font_family_names::kWebkitMonospace) |
| return CSSValueID::kMonospace; |
| if (family == font_family_names::kWebkitSansSerif) |
| return CSSValueID::kSansSerif; |
| if (family == font_family_names::kWebkitSerif) |
| return CSSValueID::kSerif; |
| return CSSValueID::kInvalid; |
| } |
| |
| CSSValue* ValueForFamily(const AtomicString& family) { |
| CSSValueID family_identifier = IdentifierForFamily(family); |
| if (IsValidCSSValueID(family_identifier)) |
| return CSSIdentifierValue::Create(family_identifier); |
| return CSSFontFamilyValue::Create(family.GetString()); |
| } |
| |
| CSSValueList* ComputedStyleUtils::ValueForFontFamily( |
| const ComputedStyle& style) { |
| const FontFamily& first_family = style.GetFontDescription().Family(); |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| for (const FontFamily* family = &first_family; family; |
| family = family->Next()) |
| list->Append(*ValueForFamily(family->Family())); |
| return list; |
| } |
| |
| CSSPrimitiveValue* ComputedStyleUtils::ValueForFontSize( |
| const ComputedStyle& style) { |
| return ZoomAdjustedPixelValue(style.GetFontDescription().ComputedSize(), |
| style); |
| } |
| |
| CSSPrimitiveValue* ComputedStyleUtils::ValueForFontStretch( |
| const ComputedStyle& style) { |
| return CSSNumericLiteralValue::Create( |
| style.GetFontDescription().Stretch(), |
| CSSPrimitiveValue::UnitType::kPercentage); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForFontStyle(const ComputedStyle& style) { |
| FontSelectionValue angle = style.GetFontDescription().Style(); |
| if (angle == NormalSlopeValue()) { |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| } |
| |
| if (angle == ItalicSlopeValue()) { |
| return CSSIdentifierValue::Create(CSSValueID::kItalic); |
| } |
| |
| // The spec says: 'The lack of a number represents an angle of |
| // "20deg"', but since we compute that to 'italic' (handled above), |
| // we don't perform any special treatment of that value here. |
| CSSValueList* oblique_values = CSSValueList::CreateSpaceSeparated(); |
| oblique_values->Append(*CSSNumericLiteralValue::Create( |
| angle, CSSPrimitiveValue::UnitType::kDegrees)); |
| return MakeGarbageCollected<cssvalue::CSSFontStyleRangeValue>( |
| *CSSIdentifierValue::Create(CSSValueID::kOblique), *oblique_values); |
| } |
| |
| CSSNumericLiteralValue* ComputedStyleUtils::ValueForFontWeight( |
| const ComputedStyle& style) { |
| return CSSNumericLiteralValue::Create(style.GetFontDescription().Weight(), |
| CSSPrimitiveValue::UnitType::kNumber); |
| } |
| |
| CSSIdentifierValue* ComputedStyleUtils::ValueForFontVariantCaps( |
| const ComputedStyle& style) { |
| FontDescription::FontVariantCaps variant_caps = |
| style.GetFontDescription().VariantCaps(); |
| switch (variant_caps) { |
| case FontDescription::kCapsNormal: |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| case FontDescription::kSmallCaps: |
| return CSSIdentifierValue::Create(CSSValueID::kSmallCaps); |
| case FontDescription::kAllSmallCaps: |
| return CSSIdentifierValue::Create(CSSValueID::kAllSmallCaps); |
| case FontDescription::kPetiteCaps: |
| return CSSIdentifierValue::Create(CSSValueID::kPetiteCaps); |
| case FontDescription::kAllPetiteCaps: |
| return CSSIdentifierValue::Create(CSSValueID::kAllPetiteCaps); |
| case FontDescription::kUnicase: |
| return CSSIdentifierValue::Create(CSSValueID::kUnicase); |
| case FontDescription::kTitlingCaps: |
| return CSSIdentifierValue::Create(CSSValueID::kTitlingCaps); |
| default: |
| NOTREACHED(); |
| return nullptr; |
| } |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForFontVariantLigatures( |
| const ComputedStyle& style) { |
| FontDescription::LigaturesState common_ligatures_state = |
| style.GetFontDescription().CommonLigaturesState(); |
| FontDescription::LigaturesState discretionary_ligatures_state = |
| style.GetFontDescription().DiscretionaryLigaturesState(); |
| FontDescription::LigaturesState historical_ligatures_state = |
| style.GetFontDescription().HistoricalLigaturesState(); |
| FontDescription::LigaturesState contextual_ligatures_state = |
| style.GetFontDescription().ContextualLigaturesState(); |
| if (common_ligatures_state == FontDescription::kNormalLigaturesState && |
| discretionary_ligatures_state == FontDescription::kNormalLigaturesState && |
| historical_ligatures_state == FontDescription::kNormalLigaturesState && |
| contextual_ligatures_state == FontDescription::kNormalLigaturesState) |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| |
| if (common_ligatures_state == FontDescription::kDisabledLigaturesState && |
| discretionary_ligatures_state == |
| FontDescription::kDisabledLigaturesState && |
| historical_ligatures_state == FontDescription::kDisabledLigaturesState && |
| contextual_ligatures_state == FontDescription::kDisabledLigaturesState) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| CSSValueList* value_list = CSSValueList::CreateSpaceSeparated(); |
| if (common_ligatures_state != FontDescription::kNormalLigaturesState) { |
| value_list->Append(*CSSIdentifierValue::Create( |
| common_ligatures_state == FontDescription::kDisabledLigaturesState |
| ? CSSValueID::kNoCommonLigatures |
| : CSSValueID::kCommonLigatures)); |
| } |
| if (discretionary_ligatures_state != FontDescription::kNormalLigaturesState) { |
| value_list->Append(*CSSIdentifierValue::Create( |
| discretionary_ligatures_state == |
| FontDescription::kDisabledLigaturesState |
| ? CSSValueID::kNoDiscretionaryLigatures |
| : CSSValueID::kDiscretionaryLigatures)); |
| } |
| if (historical_ligatures_state != FontDescription::kNormalLigaturesState) { |
| value_list->Append(*CSSIdentifierValue::Create( |
| historical_ligatures_state == FontDescription::kDisabledLigaturesState |
| ? CSSValueID::kNoHistoricalLigatures |
| : CSSValueID::kHistoricalLigatures)); |
| } |
| if (contextual_ligatures_state != FontDescription::kNormalLigaturesState) { |
| value_list->Append(*CSSIdentifierValue::Create( |
| contextual_ligatures_state == FontDescription::kDisabledLigaturesState |
| ? CSSValueID::kNoContextual |
| : CSSValueID::kContextual)); |
| } |
| return value_list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForFontVariantNumeric( |
| const ComputedStyle& style) { |
| FontVariantNumeric variant_numeric = |
| style.GetFontDescription().VariantNumeric(); |
| if (variant_numeric.IsAllNormal()) |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| |
| CSSValueList* value_list = CSSValueList::CreateSpaceSeparated(); |
| if (variant_numeric.NumericFigureValue() != |
| FontVariantNumeric::kNormalFigure) { |
| value_list->Append(*CSSIdentifierValue::Create( |
| variant_numeric.NumericFigureValue() == FontVariantNumeric::kLiningNums |
| ? CSSValueID::kLiningNums |
| : CSSValueID::kOldstyleNums)); |
| } |
| if (variant_numeric.NumericSpacingValue() != |
| FontVariantNumeric::kNormalSpacing) { |
| value_list->Append(*CSSIdentifierValue::Create( |
| variant_numeric.NumericSpacingValue() == |
| FontVariantNumeric::kProportionalNums |
| ? CSSValueID::kProportionalNums |
| : CSSValueID::kTabularNums)); |
| } |
| if (variant_numeric.NumericFractionValue() != |
| FontVariantNumeric::kNormalFraction) { |
| value_list->Append(*CSSIdentifierValue::Create( |
| variant_numeric.NumericFractionValue() == |
| FontVariantNumeric::kDiagonalFractions |
| ? CSSValueID::kDiagonalFractions |
| : CSSValueID::kStackedFractions)); |
| } |
| if (variant_numeric.OrdinalValue() == FontVariantNumeric::kOrdinalOn) |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kOrdinal)); |
| if (variant_numeric.SlashedZeroValue() == FontVariantNumeric::kSlashedZeroOn) |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kSlashedZero)); |
| |
| return value_list; |
| } |
| |
| CSSIdentifierValue* ValueForFontStretchAsKeyword(const ComputedStyle& style) { |
| FontSelectionValue stretch_value = style.GetFontDescription().Stretch(); |
| CSSValueID value_id = CSSValueID::kInvalid; |
| if (stretch_value == UltraCondensedWidthValue()) |
| value_id = CSSValueID::kUltraCondensed; |
| if (stretch_value == UltraCondensedWidthValue()) |
| value_id = CSSValueID::kUltraCondensed; |
| if (stretch_value == ExtraCondensedWidthValue()) |
| value_id = CSSValueID::kExtraCondensed; |
| if (stretch_value == CondensedWidthValue()) |
| value_id = CSSValueID::kCondensed; |
| if (stretch_value == SemiCondensedWidthValue()) |
| value_id = CSSValueID::kSemiCondensed; |
| if (stretch_value == NormalWidthValue()) |
| value_id = CSSValueID::kNormal; |
| if (stretch_value == SemiExpandedWidthValue()) |
| value_id = CSSValueID::kSemiExpanded; |
| if (stretch_value == ExpandedWidthValue()) |
| value_id = CSSValueID::kExpanded; |
| if (stretch_value == ExtraExpandedWidthValue()) |
| value_id = CSSValueID::kExtraExpanded; |
| if (stretch_value == UltraExpandedWidthValue()) |
| value_id = CSSValueID::kUltraExpanded; |
| |
| if (IsValidCSSValueID(value_id)) |
| return CSSIdentifierValue::Create(value_id); |
| return nullptr; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForFontVariantEastAsian( |
| const ComputedStyle& style) { |
| FontVariantEastAsian east_asian = |
| style.GetFontDescription().VariantEastAsian(); |
| if (east_asian.IsAllNormal()) |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| |
| CSSValueList* value_list = CSSValueList::CreateSpaceSeparated(); |
| switch (east_asian.Form()) { |
| case FontVariantEastAsian::kNormalForm: |
| break; |
| case FontVariantEastAsian::kJis78: |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kJis78)); |
| break; |
| case FontVariantEastAsian::kJis83: |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kJis83)); |
| break; |
| case FontVariantEastAsian::kJis90: |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kJis90)); |
| break; |
| case FontVariantEastAsian::kJis04: |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kJis04)); |
| break; |
| case FontVariantEastAsian::kSimplified: |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kSimplified)); |
| break; |
| case FontVariantEastAsian::kTraditional: |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kTraditional)); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| switch (east_asian.Width()) { |
| case FontVariantEastAsian::kNormalWidth: |
| break; |
| case FontVariantEastAsian::kFullWidth: |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kFullWidth)); |
| break; |
| case FontVariantEastAsian::kProportionalWidth: |
| value_list->Append( |
| *CSSIdentifierValue::Create(CSSValueID::kProportionalWidth)); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| if (east_asian.Ruby()) |
| value_list->Append(*CSSIdentifierValue::Create(CSSValueID::kRuby)); |
| return value_list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForFont(const ComputedStyle& style) { |
| auto AppendIfNotNormal = [](CSSValueList* list, const CSSValue& value) { |
| auto* identifier_value = DynamicTo<CSSIdentifierValue>(value); |
| if (identifier_value && |
| identifier_value->GetValueID() == CSSValueID::kNormal) { |
| return; |
| } |
| |
| list->Append(value); |
| }; |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| AppendIfNotNormal(list, *ValueForFontStyle(style)); |
| |
| // Check that non-initial font-variant subproperties are not conflicting with |
| // this serialization. |
| CSSValue* ligatures_value = ValueForFontVariantLigatures(style); |
| CSSValue* numeric_value = ValueForFontVariantNumeric(style); |
| CSSValue* east_asian_value = ValueForFontVariantEastAsian(style); |
| // FIXME: Use DataEquivalent<CSSValue>(...) once http://crbug.com/729447 is |
| // resolved. |
| if (!DataEquivalent(ligatures_value, |
| static_cast<CSSValue*>( |
| CSSIdentifierValue::Create(CSSValueID::kNormal))) || |
| !DataEquivalent(numeric_value, |
| static_cast<CSSValue*>( |
| CSSIdentifierValue::Create(CSSValueID::kNormal))) || |
| !DataEquivalent(east_asian_value, |
| static_cast<CSSValue*>( |
| CSSIdentifierValue::Create(CSSValueID::kNormal)))) |
| return nullptr; |
| |
| if (!ValueForFontStretchAsKeyword(style)) |
| return nullptr; |
| |
| CSSIdentifierValue* caps_value = ValueForFontVariantCaps(style); |
| if (caps_value->GetValueID() != CSSValueID::kNormal && |
| caps_value->GetValueID() != CSSValueID::kSmallCaps) |
| return nullptr; |
| AppendIfNotNormal(list, *caps_value); |
| |
| { |
| CSSNumericLiteralValue* font_weight = ValueForFontWeight(style); |
| if (font_weight->DoubleValue() != NormalWeightValue()) |
| list->Append(*font_weight); |
| } |
| |
| AppendIfNotNormal(list, *ValueForFontStretchAsKeyword(style)); |
| |
| { |
| CSSValue* line_height = ValueForLineHeight(style); |
| auto* identifier_line_height = DynamicTo<CSSIdentifierValue>(line_height); |
| if (identifier_line_height && |
| identifier_line_height->GetValueID() == CSSValueID::kNormal) { |
| list->Append(*ValueForFontSize(style)); |
| } else { |
| // Add a slash between size and line-height. |
| CSSValueList* size_and_line_height = CSSValueList::CreateSlashSeparated(); |
| size_and_line_height->Append(*ValueForFontSize(style)); |
| size_and_line_height->Append(*line_height); |
| |
| list->Append(*size_and_line_height); |
| } |
| } |
| |
| list->Append(*ValueForFontFamily(style)); |
| |
| return list; |
| } |
| |
| CSSValue* SpecifiedValueForGridTrackBreadth(const GridLength& track_breadth, |
| const ComputedStyle& style) { |
| if (!track_breadth.IsLength()) { |
| return CSSNumericLiteralValue::Create( |
| track_breadth.Flex(), CSSPrimitiveValue::UnitType::kFraction); |
| } |
| |
| const Length& track_breadth_length = track_breadth.length(); |
| if (track_breadth_length.IsAuto()) |
| return CSSIdentifierValue::Create(CSSValueID::kAuto); |
| return ComputedStyleUtils::ZoomAdjustedPixelValueForLength( |
| track_breadth_length, style); |
| } |
| |
| CSSValue* ComputedStyleUtils::SpecifiedValueForGridTrackSize( |
| const GridTrackSize& track_size, |
| const ComputedStyle& style) { |
| switch (track_size.GetType()) { |
| case kLengthTrackSizing: |
| return SpecifiedValueForGridTrackBreadth(track_size.MinTrackBreadth(), |
| style); |
| case kMinMaxTrackSizing: { |
| if (track_size.MinTrackBreadth().IsAuto() && |
| track_size.MaxTrackBreadth().IsFlex()) { |
| return CSSNumericLiteralValue::Create( |
| track_size.MaxTrackBreadth().Flex(), |
| CSSPrimitiveValue::UnitType::kFraction); |
| } |
| |
| auto* min_max_track_breadths = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMinmax); |
| min_max_track_breadths->Append(*SpecifiedValueForGridTrackBreadth( |
| track_size.MinTrackBreadth(), style)); |
| min_max_track_breadths->Append(*SpecifiedValueForGridTrackBreadth( |
| track_size.MaxTrackBreadth(), style)); |
| return min_max_track_breadths; |
| } |
| case kFitContentTrackSizing: { |
| auto* fit_content_track_breadth = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kFitContent); |
| fit_content_track_breadth->Append(*SpecifiedValueForGridTrackBreadth( |
| track_size.FitContentTrackBreadth(), style)); |
| return fit_content_track_breadth; |
| } |
| } |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| class OrderedNamedLinesCollector { |
| STACK_ALLOCATED(); |
| |
| public: |
| OrderedNamedLinesCollector(const ComputedStyle& style, bool is_row_axis) |
| : ordered_named_grid_lines_(is_row_axis |
| ? style.OrderedNamedGridColumnLines() |
| : style.OrderedNamedGridRowLines()), |
| ordered_named_auto_repeat_grid_lines_( |
| is_row_axis ? style.AutoRepeatOrderedNamedGridColumnLines() |
| : style.AutoRepeatOrderedNamedGridRowLines()) {} |
| virtual ~OrderedNamedLinesCollector() = default; |
| |
| bool IsEmpty() const { |
| return ordered_named_grid_lines_.IsEmpty() && |
| ordered_named_auto_repeat_grid_lines_.IsEmpty(); |
| } |
| virtual void CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue&, |
| size_t index) const; |
| |
| protected: |
| enum NamedLinesType { kNamedLines, kAutoRepeatNamedLines }; |
| void AppendLines(cssvalue::CSSGridLineNamesValue&, |
| size_t index, |
| NamedLinesType) const; |
| |
| const OrderedNamedGridLines& ordered_named_grid_lines_; |
| const OrderedNamedGridLines& ordered_named_auto_repeat_grid_lines_; |
| DISALLOW_COPY_AND_ASSIGN(OrderedNamedLinesCollector); |
| }; |
| |
| class OrderedNamedLinesCollectorInsideRepeat |
| : public OrderedNamedLinesCollector { |
| public: |
| OrderedNamedLinesCollectorInsideRepeat(const ComputedStyle& style, |
| bool is_row_axis) |
| : OrderedNamedLinesCollector(style, is_row_axis) {} |
| void CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue&, |
| size_t index) const override; |
| }; |
| |
| class OrderedNamedLinesCollectorInGridLayout |
| : public OrderedNamedLinesCollector { |
| public: |
| OrderedNamedLinesCollectorInGridLayout(const ComputedStyle& style, |
| bool is_row_axis, |
| size_t auto_repeat_tracks_count, |
| size_t auto_repeat_track_list_length) |
| : OrderedNamedLinesCollector(style, is_row_axis), |
| insertion_point_(is_row_axis |
| ? style.GridAutoRepeatColumnsInsertionPoint() |
| : style.GridAutoRepeatRowsInsertionPoint()), |
| auto_repeat_total_tracks_(auto_repeat_tracks_count), |
| auto_repeat_track_list_length_(auto_repeat_track_list_length) {} |
| void CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue&, |
| size_t index) const override; |
| |
| private: |
| size_t insertion_point_; |
| size_t auto_repeat_total_tracks_; |
| size_t auto_repeat_track_list_length_; |
| }; |
| |
| // RJW |
| void OrderedNamedLinesCollector::AppendLines( |
| cssvalue::CSSGridLineNamesValue& line_names_value, |
| size_t index, |
| NamedLinesType type) const { |
| auto iter = type == kNamedLines |
| ? ordered_named_grid_lines_.find(index) |
| : ordered_named_auto_repeat_grid_lines_.find(index); |
| auto end_iter = type == kNamedLines |
| ? ordered_named_grid_lines_.end() |
| : ordered_named_auto_repeat_grid_lines_.end(); |
| if (iter == end_iter) |
| return; |
| |
| for (auto line_name : iter->value) { |
| line_names_value.Append( |
| *MakeGarbageCollected<CSSCustomIdentValue>(AtomicString(line_name))); |
| } |
| } |
| |
| void OrderedNamedLinesCollector::CollectLineNamesForIndex( |
| cssvalue::CSSGridLineNamesValue& line_names_value, |
| size_t i) const { |
| DCHECK(!IsEmpty()); |
| AppendLines(line_names_value, i, kNamedLines); |
| } |
| |
| void OrderedNamedLinesCollectorInsideRepeat::CollectLineNamesForIndex( |
| cssvalue::CSSGridLineNamesValue& line_names_value, |
| size_t i) const { |
| DCHECK(!IsEmpty()); |
| AppendLines(line_names_value, i, kAutoRepeatNamedLines); |
| } |
| |
| // RJW |
| void OrderedNamedLinesCollectorInGridLayout::CollectLineNamesForIndex( |
| cssvalue::CSSGridLineNamesValue& line_names_value, |
| size_t i) const { |
| DCHECK(!IsEmpty()); |
| if (auto_repeat_track_list_length_ == 0LU || i < insertion_point_) { |
| AppendLines(line_names_value, i, kNamedLines); |
| return; |
| } |
| |
| DCHECK(auto_repeat_total_tracks_); |
| |
| if (i > insertion_point_ + auto_repeat_total_tracks_) { |
| AppendLines(line_names_value, i - (auto_repeat_total_tracks_ - 1), |
| kNamedLines); |
| return; |
| } |
| |
| if (i == insertion_point_) { |
| AppendLines(line_names_value, i, kNamedLines); |
| AppendLines(line_names_value, 0, kAutoRepeatNamedLines); |
| return; |
| } |
| |
| if (i == insertion_point_ + auto_repeat_total_tracks_) { |
| AppendLines(line_names_value, auto_repeat_track_list_length_, |
| kAutoRepeatNamedLines); |
| AppendLines(line_names_value, insertion_point_ + 1, kNamedLines); |
| return; |
| } |
| |
| size_t auto_repeat_index_in_first_repetition = |
| (i - insertion_point_) % auto_repeat_track_list_length_; |
| if (!auto_repeat_index_in_first_repetition && i > insertion_point_) { |
| AppendLines(line_names_value, auto_repeat_track_list_length_, |
| kAutoRepeatNamedLines); |
| } |
| AppendLines(line_names_value, auto_repeat_index_in_first_repetition, |
| kAutoRepeatNamedLines); |
| } |
| |
| void AddValuesForNamedGridLinesAtIndex(OrderedNamedLinesCollector& collector, |
| size_t i, |
| CSSValueList& list) { |
| if (collector.IsEmpty()) |
| return; |
| |
| auto* line_names = MakeGarbageCollected<cssvalue::CSSGridLineNamesValue>(); |
| collector.CollectLineNamesForIndex(*line_names, i); |
| if (line_names->length()) |
| list.Append(*line_names); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForGridTrackSizeList( |
| GridTrackSizingDirection direction, |
| const ComputedStyle& style) { |
| const Vector<GridTrackSize>& auto_track_sizes = |
| direction == kForColumns ? style.GridAutoColumns() : style.GridAutoRows(); |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| for (auto& track_size : auto_track_sizes) { |
| list->Append(*SpecifiedValueForGridTrackSize(track_size, style)); |
| } |
| return list; |
| } |
| |
| template <typename T, typename F> |
| void PopulateGridTrackList(CSSValueList* list, |
| OrderedNamedLinesCollector& collector, |
| const Vector<T>& tracks, |
| F getTrackSize, |
| int start, |
| int end, |
| int offset = 0) { |
| DCHECK_LE(0, start); |
| DCHECK_LE(start, end); |
| DCHECK_LE((unsigned)end, tracks.size()); |
| for (int i = start; i < end; ++i) { |
| if (i + offset >= 0) |
| AddValuesForNamedGridLinesAtIndex(collector, i + offset, *list); |
| list->Append(*getTrackSize(tracks[i])); |
| } |
| if (end + offset >= 0) |
| AddValuesForNamedGridLinesAtIndex(collector, end + offset, *list); |
| } |
| |
| template <typename T, typename F> |
| void PopulateGridTrackList(CSSValueList* list, |
| OrderedNamedLinesCollector& collector, |
| const Vector<T>& tracks, |
| F getTrackSize, |
| int offset = 0) { |
| PopulateGridTrackList<T>(list, collector, tracks, getTrackSize, 0, |
| tracks.size(), offset); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForGridTrackList( |
| GridTrackSizingDirection direction, |
| const LayoutObject* layout_object, |
| const ComputedStyle& style) { |
| bool is_row_axis = direction == kForColumns; |
| const Vector<GridTrackSize>& track_sizes = |
| is_row_axis ? style.GridTemplateColumns() : style.GridTemplateRows(); |
| const Vector<GridTrackSize>& auto_repeat_track_sizes = |
| is_row_axis ? style.GridAutoRepeatColumns() : style.GridAutoRepeatRows(); |
| bool is_layout_grid = layout_object && layout_object->IsLayoutGrid(); |
| |
| // Handle the 'none' case. |
| bool track_list_is_empty = |
| track_sizes.IsEmpty() && auto_repeat_track_sizes.IsEmpty(); |
| if (is_layout_grid && track_list_is_empty) { |
| // For grids we should consider every listed track, whether implicitly or |
| // explicitly created. Empty grids have a sole grid line per axis. |
| auto& positions = is_row_axis |
| ? ToLayoutGrid(layout_object)->ColumnPositions() |
| : ToLayoutGrid(layout_object)->RowPositions(); |
| track_list_is_empty = positions.size() == 1; |
| } |
| |
| if (track_list_is_empty) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| |
| // If the element is a grid container, the resolved value is the used value, |
| // specifying track sizes in pixels and expanding the repeat() notation. |
| if (is_layout_grid) { |
| const auto* grid = ToLayoutGrid(layout_object); |
| OrderedNamedLinesCollectorInGridLayout collector( |
| style, is_row_axis, grid->AutoRepeatCountForDirection(direction), |
| auto_repeat_track_sizes.size()); |
| // Named grid line indices are relative to the explicit grid, but we are |
| // including all tracks. So we need to subtract the number of leading |
| // implicit tracks in order to get the proper line index. |
| int offset = -grid->ExplicitGridStartForDirection(direction); |
| PopulateGridTrackList( |
| list, collector, grid->TrackSizesForComputedStyle(direction), |
| [&](const LayoutUnit& v) { return ZoomAdjustedPixelValue(v, style); }, |
| offset); |
| return list; |
| } |
| |
| // Otherwise, the resolved value is the computed value, preserving repeat(). |
| OrderedNamedLinesCollector collector(style, is_row_axis); |
| auto getTrackSize = [&](const GridTrackSize& v) { |
| return SpecifiedValueForGridTrackSize(v, style); |
| }; |
| |
| if (auto_repeat_track_sizes.IsEmpty()) { |
| // If there's no auto repeat(), just add all the line names and track sizes. |
| PopulateGridTrackList(list, collector, track_sizes, getTrackSize); |
| return list; |
| } |
| |
| // Add the line names and track sizes that precede the auto repeat(). |
| size_t auto_repeat_insertion_point = |
| is_row_axis ? style.GridAutoRepeatColumnsInsertionPoint() |
| : style.GridAutoRepeatRowsInsertionPoint(); |
| PopulateGridTrackList(list, collector, track_sizes, getTrackSize, 0, |
| auto_repeat_insertion_point); |
| |
| // Add a CSSGridAutoRepeatValue with the contents of the auto repeat(). |
| AutoRepeatType auto_repeat_type = is_row_axis |
| ? style.GridAutoRepeatColumnsType() |
| : style.GridAutoRepeatRowsType(); |
| CSSValueList* repeated_values = |
| MakeGarbageCollected<cssvalue::CSSGridAutoRepeatValue>( |
| auto_repeat_type == AutoRepeatType::kAutoFill ? CSSValueID::kAutoFill |
| : CSSValueID::kAutoFit); |
| OrderedNamedLinesCollectorInsideRepeat repeat_collector(style, is_row_axis); |
| PopulateGridTrackList(repeated_values, repeat_collector, |
| auto_repeat_track_sizes, getTrackSize); |
| list->Append(*repeated_values); |
| |
| // Add the line names and track sizes that follow the auto repeat(). |
| PopulateGridTrackList(list, collector, track_sizes, getTrackSize, |
| auto_repeat_insertion_point, track_sizes.size(), 1); |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForGridPosition( |
| const GridPosition& position) { |
| if (position.IsAuto()) |
| return CSSIdentifierValue::Create(CSSValueID::kAuto); |
| |
| if (position.IsNamedGridArea()) |
| return MakeGarbageCollected<CSSCustomIdentValue>(position.NamedGridLine()); |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| if (position.IsSpan()) { |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kSpan)); |
| list->Append(*CSSNumericLiteralValue::Create( |
| position.SpanPosition(), CSSPrimitiveValue::UnitType::kNumber)); |
| } else { |
| list->Append(*CSSNumericLiteralValue::Create( |
| position.IntegerPosition(), CSSPrimitiveValue::UnitType::kNumber)); |
| } |
| |
| if (!position.NamedGridLine().IsNull()) { |
| list->Append( |
| *MakeGarbageCollected<CSSCustomIdentValue>(position.NamedGridLine())); |
| } |
| return list; |
| } |
| |
| static bool IsSVGObjectWithWidthAndHeight(const LayoutObject& layout_object) { |
| DCHECK(layout_object.IsSVGChild()); |
| return layout_object.IsSVGImage() || layout_object.IsSVGForeignObject() || |
| (layout_object.IsSVGShape() && |
| IsA<SVGRectElement>(layout_object.GetNode())); |
| } |
| |
| FloatSize ComputedStyleUtils::UsedBoxSize(const LayoutObject& layout_object) { |
| if (layout_object.IsSVGChild() && |
| IsSVGObjectWithWidthAndHeight(layout_object)) { |
| FloatSize size(layout_object.ObjectBoundingBox().Size()); |
| // The object bounding box does not have zoom applied. Multiply with zoom |
| // here since we'll divide by it when we produce the CSS value. |
| size.Scale(layout_object.StyleRef().EffectiveZoom()); |
| return size; |
| } |
| if (!layout_object.IsBox()) |
| return FloatSize(); |
| const LayoutBox& box = ToLayoutBox(layout_object); |
| return FloatSize(box.StyleRef().BoxSizing() == EBoxSizing::kBorderBox |
| ? box.BorderBoxRect().Size() |
| : box.ComputedCSSContentBoxRect().Size()); |
| } |
| |
| CSSValue* ComputedStyleUtils::RenderTextDecorationFlagsToCSSValue( |
| TextDecoration text_decoration) { |
| // Blink value is ignored. |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| if (EnumHasFlags(text_decoration, TextDecoration::kUnderline)) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kUnderline)); |
| if (EnumHasFlags(text_decoration, TextDecoration::kOverline)) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kOverline)); |
| if (EnumHasFlags(text_decoration, TextDecoration::kLineThrough)) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kLineThrough)); |
| |
| if (!list->length()) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForTextDecorationStyle( |
| ETextDecorationStyle text_decoration_style) { |
| switch (text_decoration_style) { |
| case ETextDecorationStyle::kSolid: |
| return CSSIdentifierValue::Create(CSSValueID::kSolid); |
| case ETextDecorationStyle::kDouble: |
| return CSSIdentifierValue::Create(CSSValueID::kDouble); |
| case ETextDecorationStyle::kDotted: |
| return CSSIdentifierValue::Create(CSSValueID::kDotted); |
| case ETextDecorationStyle::kDashed: |
| return CSSIdentifierValue::Create(CSSValueID::kDashed); |
| case ETextDecorationStyle::kWavy: |
| return CSSIdentifierValue::Create(CSSValueID::kWavy); |
| } |
| |
| NOTREACHED(); |
| return CSSInitialValue::Create(); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForTextDecorationSkipInk( |
| ETextDecorationSkipInk text_decoration_skip_ink) { |
| if (text_decoration_skip_ink == ETextDecorationSkipInk::kNone) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| return CSSIdentifierValue::Create(CSSValueID::kAuto); |
| } |
| |
| CSSValue* ComputedStyleUtils::TouchActionFlagsToCSSValue( |
| TouchAction touch_action) { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| if (touch_action == TouchAction::kAuto) { |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kAuto)); |
| } else if (touch_action == TouchAction::kNone) { |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone)); |
| } else if (touch_action == TouchAction::kManipulation) { |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kManipulation)); |
| } else { |
| if ((touch_action & TouchAction::kPanX) == TouchAction::kPanX) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanX)); |
| else if ((touch_action & TouchAction::kPanLeft) != TouchAction::kNone) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanLeft)); |
| else if ((touch_action & TouchAction::kPanRight) != TouchAction::kNone) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanRight)); |
| if ((touch_action & TouchAction::kPanY) == TouchAction::kPanY) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanY)); |
| else if ((touch_action & TouchAction::kPanUp) != TouchAction::kNone) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanUp)); |
| else if ((touch_action & TouchAction::kPanDown) != TouchAction::kNone) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanDown)); |
| |
| if ((touch_action & TouchAction::kPinchZoom) == TouchAction::kPinchZoom) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPinchZoom)); |
| } |
| |
| DCHECK(list->length()); |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForWillChange( |
| const Vector<CSSPropertyID>& will_change_properties, |
| bool will_change_contents, |
| bool will_change_scroll_position) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| if (will_change_contents) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kContents)); |
| if (will_change_scroll_position) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kScrollPosition)); |
| for (wtf_size_t i = 0; i < will_change_properties.size(); ++i) { |
| list->Append( |
| *MakeGarbageCollected<CSSCustomIdentValue>(will_change_properties[i])); |
| } |
| if (!list->length()) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kAuto)); |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForAnimationDelay( |
| const CSSTimingData* timing_data) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| if (timing_data) { |
| for (wtf_size_t i = 0; i < timing_data->DelayList().size(); ++i) { |
| list->Append(*CSSNumericLiteralValue::Create( |
| timing_data->DelayList()[i], CSSPrimitiveValue::UnitType::kSeconds)); |
| } |
| } else { |
| list->Append(*CSSNumericLiteralValue::Create( |
| CSSTimingData::InitialDelay(), CSSPrimitiveValue::UnitType::kSeconds)); |
| } |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForAnimationDirection( |
| Timing::PlaybackDirection direction) { |
| switch (direction) { |
| case Timing::PlaybackDirection::NORMAL: |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| case Timing::PlaybackDirection::ALTERNATE_NORMAL: |
| return CSSIdentifierValue::Create(CSSValueID::kAlternate); |
| case Timing::PlaybackDirection::REVERSE: |
| return CSSIdentifierValue::Create(CSSValueID::kReverse); |
| case Timing::PlaybackDirection::ALTERNATE_REVERSE: |
| return CSSIdentifierValue::Create(CSSValueID::kAlternateReverse); |
| default: |
| NOTREACHED(); |
| return nullptr; |
| } |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForAnimationDuration( |
| const CSSTimingData* timing_data) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| if (timing_data) { |
| for (wtf_size_t i = 0; i < timing_data->DurationList().size(); ++i) { |
| list->Append(*CSSNumericLiteralValue::Create( |
| timing_data->DurationList()[i], |
| CSSPrimitiveValue::UnitType::kSeconds)); |
| } |
| } else { |
| list->Append( |
| *CSSNumericLiteralValue::Create(CSSTimingData::InitialDuration(), |
| CSSPrimitiveValue::UnitType::kSeconds)); |
| } |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForAnimationFillMode( |
| Timing::FillMode fill_mode) { |
| switch (fill_mode) { |
| case Timing::FillMode::NONE: |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| case Timing::FillMode::FORWARDS: |
| return CSSIdentifierValue::Create(CSSValueID::kForwards); |
| case Timing::FillMode::BACKWARDS: |
| return CSSIdentifierValue::Create(CSSValueID::kBackwards); |
| case Timing::FillMode::BOTH: |
| return CSSIdentifierValue::Create(CSSValueID::kBoth); |
| default: |
| NOTREACHED(); |
| return nullptr; |
| } |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForAnimationIterationCount( |
| double iteration_count) { |
| if (iteration_count == std::numeric_limits<double>::infinity()) |
| return CSSIdentifierValue::Create(CSSValueID::kInfinite); |
| return CSSNumericLiteralValue::Create(iteration_count, |
| CSSPrimitiveValue::UnitType::kNumber); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForAnimationPlayState( |
| EAnimPlayState play_state) { |
| if (play_state == EAnimPlayState::kPlaying) |
| return CSSIdentifierValue::Create(CSSValueID::kRunning); |
| DCHECK_EQ(play_state, EAnimPlayState::kPaused); |
| return CSSIdentifierValue::Create(CSSValueID::kPaused); |
| } |
| |
| CSSValue* ComputedStyleUtils::CreateTimingFunctionValue( |
| const TimingFunction* timing_function) { |
| switch (timing_function->GetType()) { |
| case TimingFunction::Type::CUBIC_BEZIER: { |
| const auto* bezier_timing_function = |
| To<CubicBezierTimingFunction>(timing_function); |
| if (bezier_timing_function->GetEaseType() != |
| CubicBezierTimingFunction::EaseType::CUSTOM) { |
| CSSValueID value_id = CSSValueID::kInvalid; |
| switch (bezier_timing_function->GetEaseType()) { |
| case CubicBezierTimingFunction::EaseType::EASE: |
| value_id = CSSValueID::kEase; |
| break; |
| case CubicBezierTimingFunction::EaseType::EASE_IN: |
| value_id = CSSValueID::kEaseIn; |
| break; |
| case CubicBezierTimingFunction::EaseType::EASE_OUT: |
| value_id = CSSValueID::kEaseOut; |
| break; |
| case CubicBezierTimingFunction::EaseType::EASE_IN_OUT: |
| value_id = CSSValueID::kEaseInOut; |
| break; |
| default: |
| NOTREACHED(); |
| return nullptr; |
| } |
| return CSSIdentifierValue::Create(value_id); |
| } |
| return MakeGarbageCollected<cssvalue::CSSCubicBezierTimingFunctionValue>( |
| bezier_timing_function->X1(), bezier_timing_function->Y1(), |
| bezier_timing_function->X2(), bezier_timing_function->Y2()); |
| } |
| |
| case TimingFunction::Type::STEPS: { |
| const auto* steps_timing_function = |
| To<StepsTimingFunction>(timing_function); |
| StepsTimingFunction::StepPosition position = |
| steps_timing_function->GetStepPosition(); |
| int steps = steps_timing_function->NumberOfSteps(); |
| |
| // Canonical form of step timing function is step(n, type) or step(n) even |
| // if initially parsed as step-start or step-end. |
| return MakeGarbageCollected<cssvalue::CSSStepsTimingFunctionValue>( |
| steps, position); |
| } |
| |
| default: |
| return CSSIdentifierValue::Create(CSSValueID::kLinear); |
| } |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForAnimationTimingFunction( |
| const CSSTimingData* timing_data) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| if (timing_data) { |
| for (wtf_size_t i = 0; i < timing_data->TimingFunctionList().size(); ++i) { |
| list->Append(*CreateTimingFunctionValue( |
| timing_data->TimingFunctionList()[i].get())); |
| } |
| } else { |
| list->Append(*CreateTimingFunctionValue( |
| CSSTimingData::InitialTimingFunction().get())); |
| } |
| return list; |
| } |
| |
| CSSValueList* ComputedStyleUtils::ValuesForBorderRadiusCorner( |
| const LengthSize& radius, |
| const ComputedStyle& style) { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| if (radius.Width().IsPercent()) { |
| list->Append(*CSSNumericLiteralValue::Create( |
| radius.Width().Percent(), CSSPrimitiveValue::UnitType::kPercentage)); |
| } else { |
| list->Append(*ZoomAdjustedPixelValueForLength(radius.Width(), style)); |
| } |
| if (radius.Height().IsPercent()) { |
| list->Append(*CSSNumericLiteralValue::Create( |
| radius.Height().Percent(), CSSPrimitiveValue::UnitType::kPercentage)); |
| } else { |
| list->Append(*ZoomAdjustedPixelValueForLength(radius.Height(), style)); |
| } |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForBorderRadiusCorner( |
| const LengthSize& radius, |
| const ComputedStyle& style) { |
| return MakeGarbageCollected<CSSValuePair>( |
| ZoomAdjustedPixelValueForLength(radius.Width(), style), |
| ZoomAdjustedPixelValueForLength(radius.Height(), style), |
| CSSValuePair::kDropIdenticalValues); |
| } |
| |
| CSSFunctionValue* ComputedStyleUtils::ValueForTransformationMatrix( |
| const TransformationMatrix& matrix, |
| float zoom, |
| bool force_matrix3d) { |
| if (matrix.IsAffine() && !force_matrix3d) { |
| auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMatrix); |
| // CSS matrix values are returned in column-major order. |
| double values[6] = {matrix.A(), matrix.B(), // |
| matrix.C(), matrix.D(), // |
| // E and F are pixel lengths so unzoom |
| matrix.E() / zoom, matrix.F() / zoom}; |
| for (double value : values) { |
| result->Append(*CSSNumericLiteralValue::Create( |
| value, CSSPrimitiveValue::UnitType::kNumber)); |
| } |
| return result; |
| } else { |
| CSSFunctionValue* result = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMatrix3d); |
| // CSS matrix values are returned in column-major order. |
| double values[16] = { |
| // Note that the transformation matrix operates on (Length^3 * R). |
| // Each column contains 3 scalars followed by a reciprocal length |
| // (with a value in 1/px) which must be unzoomed accordingly. |
| matrix.M11(), matrix.M12(), matrix.M13(), matrix.M14() * zoom, |
| matrix.M21(), matrix.M22(), matrix.M23(), matrix.M24() * zoom, |
| matrix.M31(), matrix.M32(), matrix.M33(), matrix.M34() * zoom, |
| // Last column has 3 pixel lengths and a scalar |
| matrix.M41() / zoom, matrix.M42() / zoom, matrix.M43() / zoom, |
| matrix.M44()}; |
| for (double value : values) { |
| result->Append(*CSSNumericLiteralValue::Create( |
| value, CSSPrimitiveValue::UnitType::kNumber)); |
| } |
| return result; |
| } |
| } |
| |
| // We collapse functions like translateX into translate, since we will reify |
| // them as a translate anyway. |
| CSSFunctionValue* ComputedStyleUtils::ValueForTransformOperation( |
| const TransformOperation& operation, |
| float zoom, |
| FloatSize box_size) { |
| switch (operation.GetType()) { |
| case TransformOperation::kScaleX: |
| case TransformOperation::kScaleY: |
| case TransformOperation::kScaleZ: |
| case TransformOperation::kScale: |
| case TransformOperation::kScale3D: { |
| const auto& scale = To<ScaleTransformOperation>(operation); |
| CSSFunctionValue* result = MakeGarbageCollected<CSSFunctionValue>( |
| operation.Is3DOperation() ? CSSValueID::kScale3d |
| : CSSValueID::kScale); |
| result->Append(*CSSNumericLiteralValue::Create( |
| scale.X(), CSSPrimitiveValue::UnitType::kNumber)); |
| result->Append(*CSSNumericLiteralValue::Create( |
| scale.Y(), CSSPrimitiveValue::UnitType::kNumber)); |
| if (operation.Is3DOperation()) { |
| result->Append(*CSSNumericLiteralValue::Create( |
| scale.Z(), CSSPrimitiveValue::UnitType::kNumber)); |
| } |
| return result; |
| } |
| case TransformOperation::kTranslateX: |
| case TransformOperation::kTranslateY: |
| case TransformOperation::kTranslateZ: |
| case TransformOperation::kTranslate: |
| case TransformOperation::kTranslate3D: { |
| const auto& translate = To<TranslateTransformOperation>(operation); |
| CSSFunctionValue* result = MakeGarbageCollected<CSSFunctionValue>( |
| operation.Is3DOperation() ? CSSValueID::kTranslate3d |
| : CSSValueID::kTranslate); |
| result->Append(*CSSPrimitiveValue::CreateFromLength(translate.X(), zoom)); |
| result->Append(*CSSPrimitiveValue::CreateFromLength(translate.Y(), zoom)); |
| if (operation.Is3DOperation()) { |
| // Since this is pixel length, we must unzoom (CreateFromLength above |
| // does the division internally). |
| result->Append(*CSSNumericLiteralValue::Create( |
| translate.Z() / zoom, CSSPrimitiveValue::UnitType::kPixels)); |
| } |
| return result; |
| } |
| case TransformOperation::kRotateX: |
| case TransformOperation::kRotateY: |
| case TransformOperation::kRotate3D: { |
| const auto& rotate = To<RotateTransformOperation>(operation); |
| CSSFunctionValue* result = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kRotate3d); |
| result->Append(*CSSNumericLiteralValue::Create( |
| rotate.X(), CSSPrimitiveValue::UnitType::kNumber)); |
| result->Append(*CSSNumericLiteralValue::Create( |
| rotate.Y(), CSSPrimitiveValue::UnitType::kNumber)); |
| result->Append(*CSSNumericLiteralValue::Create( |
| rotate.Z(), CSSPrimitiveValue::UnitType::kNumber)); |
| result->Append(*CSSNumericLiteralValue::Create( |
| rotate.Angle(), CSSPrimitiveValue::UnitType::kDegrees)); |
| return result; |
| } |
| case TransformOperation::kRotate: { |
| const auto& rotate = To<RotateTransformOperation>(operation); |
| auto* result = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kRotate); |
| result->Append(*CSSNumericLiteralValue::Create( |
| rotate.Angle(), CSSPrimitiveValue::UnitType::kDegrees)); |
| return result; |
| } |
| case TransformOperation::kRotateAroundOrigin: { |
| // TODO(https://github.com/w3c/csswg-drafts/issues/5011): |
| // Update this once there is consensus. |
| TransformationMatrix matrix; |
| operation.Apply(matrix, FloatSize(0, 0)); |
| return ValueForTransformationMatrix(matrix, zoom, |
| /*force_matrix3d=*/false); |
| } |
| case TransformOperation::kSkewX: { |
| const auto& skew = To<SkewTransformOperation>(operation); |
| auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkewX); |
| result->Append(*CSSNumericLiteralValue::Create( |
| skew.AngleX(), CSSPrimitiveValue::UnitType::kDegrees)); |
| return result; |
| } |
| case TransformOperation::kSkewY: { |
| const auto& skew = To<SkewTransformOperation>(operation); |
| auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkewY); |
| result->Append(*CSSNumericLiteralValue::Create( |
| skew.AngleY(), CSSPrimitiveValue::UnitType::kDegrees)); |
| return result; |
| } |
| case TransformOperation::kSkew: { |
| const auto& skew = To<SkewTransformOperation>(operation); |
| auto* result = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSkew); |
| result->Append(*CSSNumericLiteralValue::Create( |
| skew.AngleX(), CSSPrimitiveValue::UnitType::kDegrees)); |
| result->Append(*CSSNumericLiteralValue::Create( |
| skew.AngleY(), CSSPrimitiveValue::UnitType::kDegrees)); |
| return result; |
| } |
| case TransformOperation::kPerspective: { |
| const auto& perspective = To<PerspectiveTransformOperation>(operation); |
| auto* result = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kPerspective); |
| result->Append(*CSSNumericLiteralValue::Create( |
| perspective.Perspective() / zoom, |
| CSSPrimitiveValue::UnitType::kPixels)); |
| return result; |
| } |
| case TransformOperation::kMatrix: { |
| const auto& matrix = To<MatrixTransformOperation>(operation).Matrix(); |
| return ValueForTransformationMatrix(matrix, zoom, |
| /*force_matrix3d=*/false); |
| } |
| case TransformOperation::kMatrix3D: { |
| const auto& matrix = To<Matrix3DTransformOperation>(operation).Matrix(); |
| // Force matrix3d serialization |
| return ValueForTransformationMatrix(matrix, zoom, |
| /*force_matrix3d=*/true); |
| } |
| case TransformOperation::kInterpolated: |
| // TODO(https://github.com/w3c/csswg-drafts/issues/2854): |
| // Deferred interpolations are currently unreperesentable in CSS. |
| // This currently converts the operation to a matrix, using box_size if |
| // provided, 0x0 if not (returning all but the relative translate |
| // portion of the transform). Update this once the spec is updated. |
| TransformationMatrix matrix; |
| operation.Apply(matrix, box_size); |
| return ValueForTransformationMatrix(matrix, zoom, |
| /*force_matrix3d=*/false); |
| } |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForTransformList( |
| const TransformOperations& transform_list, |
| float zoom, |
| FloatSize box_size) { |
| if (!transform_list.Operations().size()) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| CSSValueList* components = CSSValueList::CreateSpaceSeparated(); |
| for (const auto& operation : transform_list.Operations()) { |
| CSSValue* op_value = ValueForTransformOperation(*operation, zoom, box_size); |
| components->Append(*op_value); |
| } |
| return components; |
| } |
| |
| FloatRect ComputedStyleUtils::ReferenceBoxForTransform( |
| const LayoutObject& layout_object, |
| UsePixelSnappedBox pixel_snap_box) { |
| if (layout_object.IsSVGChild()) |
| return TransformHelper::ComputeReferenceBox(layout_object); |
| if (layout_object.IsBox()) { |
| const auto& layout_box = ToLayoutBox(layout_object); |
| if (pixel_snap_box == kUsePixelSnappedBox) |
| return FloatRect(layout_box.PixelSnappedBorderBoxRect()); |
| return FloatRect(layout_box.BorderBoxRect()); |
| } |
| return FloatRect(); |
| } |
| |
| CSSValue* ComputedStyleUtils::ComputedTransformList( |
| const ComputedStyle& style, |
| const LayoutObject* layout_object) { |
| FloatSize box_size(0, 0); |
| if (layout_object) |
| box_size = ReferenceBoxForTransform(*layout_object).Size(); |
| |
| return ValueForTransformList(style.Transform(), style.EffectiveZoom(), |
| box_size); |
| } |
| |
| CSSValue* ComputedStyleUtils::ResolvedTransform( |
| const LayoutObject* layout_object, |
| const ComputedStyle& style) { |
| if (!layout_object || !style.HasTransform()) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| FloatRect reference_box = ReferenceBoxForTransform(*layout_object); |
| |
| TransformationMatrix transform; |
| style.ApplyTransform(transform, reference_box, |
| ComputedStyle::kExcludeTransformOrigin, |
| ComputedStyle::kExcludeMotionPath, |
| ComputedStyle::kExcludeIndependentTransformProperties); |
| |
| // FIXME: Need to print out individual functions |
| // (https://bugs.webkit.org/show_bug.cgi?id=23924) |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| list->Append(*ValueForTransformationMatrix(transform, style.EffectiveZoom(), |
| /*force_matrix3d=*/false)); |
| |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::CreateTransitionPropertyValue( |
| const CSSTransitionData::TransitionProperty& property) { |
| if (property.property_type == CSSTransitionData::kTransitionNone) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| if (property.property_type == CSSTransitionData::kTransitionUnknownProperty) |
| return MakeGarbageCollected<CSSCustomIdentValue>(property.property_string); |
| DCHECK_EQ(property.property_type, |
| CSSTransitionData::kTransitionKnownProperty); |
| return MakeGarbageCollected<CSSCustomIdentValue>( |
| CSSUnresolvedProperty::Get(property.unresolved_property) |
| .GetPropertyNameAtomicString()); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForTransitionProperty( |
| const CSSTransitionData* transition_data) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| if (transition_data) { |
| for (wtf_size_t i = 0; i < transition_data->PropertyList().size(); ++i) { |
| list->Append( |
| *CreateTransitionPropertyValue(transition_data->PropertyList()[i])); |
| } |
| } else { |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kAll)); |
| } |
| return list; |
| } |
| |
| CSSValueID ValueForQuoteType(const QuoteType quote_type) { |
| switch (quote_type) { |
| case QuoteType::kNoOpen: |
| return CSSValueID::kNoOpenQuote; |
| case QuoteType::kNoClose: |
| return CSSValueID::kNoCloseQuote; |
| case QuoteType::kClose: |
| return CSSValueID::kCloseQuote; |
| case QuoteType::kOpen: |
| return CSSValueID::kOpenQuote; |
| } |
| NOTREACHED(); |
| return CSSValueID::kInvalid; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForContentData(const ComputedStyle& style, |
| bool allow_visited_style) { |
| if (style.ContentPreventsBoxGeneration()) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| if (style.ContentBehavesAsNormal()) |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| |
| CSSValueList* outer_list = CSSValueList::CreateSlashSeparated(); |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| |
| // Alternative text optionally specified after a forward slash appearing after |
| // the last content list item. |
| CSSStringValue* alt_text = nullptr; |
| for (const ContentData* content_data = style.GetContentData(); content_data; |
| content_data = content_data->Next()) { |
| if (content_data->IsCounter()) { |
| const CounterContent* counter = |
| To<CounterContentData>(content_data)->Counter(); |
| DCHECK(counter); |
| auto* identifier = |
| MakeGarbageCollected<CSSCustomIdentValue>(counter->Identifier()); |
| auto* separator = |
| MakeGarbageCollected<CSSStringValue>(counter->Separator()); |
| CSSValueID list_style_ident = CSSValueID::kNone; |
| if (counter->ListStyle() != EListStyleType::kNone) { |
| // TODO(sashab): Change this to use a converter instead of |
| // CSSPrimitiveValueMappings. |
| list_style_ident = |
| CSSIdentifierValue::Create(counter->ListStyle())->GetValueID(); |
| } |
| CSSIdentifierValue* list_style = |
| CSSIdentifierValue::Create(list_style_ident); |
| list->Append(*MakeGarbageCollected<cssvalue::CSSCounterValue>( |
| identifier, list_style, separator)); |
| } else if (content_data->IsImage()) { |
| const StyleImage* image = To<ImageContentData>(content_data)->GetImage(); |
| DCHECK(image); |
| list->Append(*image->ComputedCSSValue(style, allow_visited_style)); |
| } else if (content_data->IsText()) { |
| list->Append(*MakeGarbageCollected<CSSStringValue>( |
| To<TextContentData>(content_data)->GetText())); |
| } else if (content_data->IsQuote()) { |
| const QuoteType quote_type = To<QuoteContentData>(content_data)->Quote(); |
| list->Append(*CSSIdentifierValue::Create(ValueForQuoteType(quote_type))); |
| } else if (content_data->IsAltText()) { |
| alt_text = MakeGarbageCollected<CSSStringValue>( |
| To<AltTextContentData>(content_data)->GetText()); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| DCHECK(list->length()); |
| |
| outer_list->Append(*list); |
| if (alt_text) |
| outer_list->Append(*alt_text); |
| return outer_list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForCounterDirectives( |
| const ComputedStyle& style, |
| bool is_increment) { |
| const CounterDirectiveMap* map = style.GetCounterDirectives(); |
| if (!map) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| for (const auto& item : *map) { |
| bool is_valid_counter_value = |
| is_increment ? item.value.IsIncrement() : item.value.IsReset(); |
| if (!is_valid_counter_value) |
| continue; |
| |
| list->Append(*MakeGarbageCollected<CSSCustomIdentValue>(item.key)); |
| int32_t number = |
| is_increment ? item.value.IncrementValue() : item.value.ResetValue(); |
| list->Append(*CSSNumericLiteralValue::Create( |
| (double)number, CSSPrimitiveValue::UnitType::kInteger)); |
| } |
| |
| if (!list->length()) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForShape(const ComputedStyle& style, |
| bool allow_visited_style, |
| ShapeValue* shape_value) { |
| if (!shape_value) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| if (shape_value->GetType() == ShapeValue::kBox) |
| return CSSIdentifierValue::Create(shape_value->CssBox()); |
| if (shape_value->GetType() == ShapeValue::kImage) { |
| if (shape_value->GetImage()) { |
| return shape_value->GetImage()->ComputedCSSValue(style, |
| allow_visited_style); |
| } |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| } |
| |
| DCHECK_EQ(shape_value->GetType(), ShapeValue::kShape); |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| list->Append(*ValueForBasicShape(style, shape_value->Shape())); |
| if (shape_value->CssBox() != CSSBoxType::kMissing) |
| list->Append(*CSSIdentifierValue::Create(shape_value->CssBox())); |
| return list; |
| } |
| |
| CSSValueList* ComputedStyleUtils::ValueForBorderRadiusShorthand( |
| const ComputedStyle& style) { |
| CSSValueList* list = CSSValueList::CreateSlashSeparated(); |
| |
| bool show_horizontal_bottom_left = style.BorderTopRightRadius().Width() != |
| style.BorderBottomLeftRadius().Width(); |
| bool show_horizontal_bottom_right = |
| show_horizontal_bottom_left || (style.BorderBottomRightRadius().Width() != |
| style.BorderTopLeftRadius().Width()); |
| bool show_horizontal_top_right = |
| show_horizontal_bottom_right || (style.BorderTopRightRadius().Width() != |
| style.BorderTopLeftRadius().Width()); |
| |
| bool show_vertical_bottom_left = style.BorderTopRightRadius().Height() != |
| style.BorderBottomLeftRadius().Height(); |
| bool show_vertical_bottom_right = |
| show_vertical_bottom_left || (style.BorderBottomRightRadius().Height() != |
| style.BorderTopLeftRadius().Height()); |
| bool show_vertical_top_right = |
| show_vertical_bottom_right || (style.BorderTopRightRadius().Height() != |
| style.BorderTopLeftRadius().Height()); |
| |
| CSSValueList* top_left_radius = |
| ValuesForBorderRadiusCorner(style.BorderTopLeftRadius(), style); |
| CSSValueList* top_right_radius = |
| ValuesForBorderRadiusCorner(style.BorderTopRightRadius(), style); |
| CSSValueList* bottom_right_radius = |
| ValuesForBorderRadiusCorner(style.BorderBottomRightRadius(), style); |
| CSSValueList* bottom_left_radius = |
| ValuesForBorderRadiusCorner(style.BorderBottomLeftRadius(), style); |
| |
| CSSValueList* horizontal_radii = CSSValueList::CreateSpaceSeparated(); |
| horizontal_radii->Append(top_left_radius->Item(0)); |
| if (show_horizontal_top_right) |
| horizontal_radii->Append(top_right_radius->Item(0)); |
| if (show_horizontal_bottom_right) |
| horizontal_radii->Append(bottom_right_radius->Item(0)); |
| if (show_horizontal_bottom_left) |
| horizontal_radii->Append(bottom_left_radius->Item(0)); |
| |
| list->Append(*horizontal_radii); |
| |
| CSSValueList* vertical_radii = CSSValueList::CreateSpaceSeparated(); |
| vertical_radii->Append(top_left_radius->Item(1)); |
| if (show_vertical_top_right) |
| vertical_radii->Append(top_right_radius->Item(1)); |
| if (show_vertical_bottom_right) |
| vertical_radii->Append(bottom_right_radius->Item(1)); |
| if (show_vertical_bottom_left) |
| vertical_radii->Append(bottom_left_radius->Item(1)); |
| |
| if (!vertical_radii->Equals(To<CSSValueList>(list->Item(0)))) |
| list->Append(*vertical_radii); |
| |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::StrokeDashArrayToCSSValueList( |
| const SVGDashArray& dashes, |
| const ComputedStyle& style) { |
| if (dashes.data.IsEmpty()) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| for (const Length& dash_length : dashes.data) { |
| list->Append(*ZoomAdjustedPixelValueForLength(dash_length, style)); |
| } |
| |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::AdjustSVGPaintForCurrentColor( |
| const SVGPaint& paint, |
| const Color& current_color) { |
| if (paint.type >= SVG_PAINTTYPE_URI_NONE) { |
| CSSValueList* values = CSSValueList::CreateSpaceSeparated(); |
| values->Append( |
| *MakeGarbageCollected<cssvalue::CSSURIValue>(paint.GetUrl())); |
| if (paint.type == SVG_PAINTTYPE_URI_NONE) |
| values->Append(*CSSIdentifierValue::Create(CSSValueID::kNone)); |
| else if (paint.type == SVG_PAINTTYPE_URI_CURRENTCOLOR) |
| values->Append(*cssvalue::CSSColorValue::Create(current_color.Rgb())); |
| else if (paint.type == SVG_PAINTTYPE_URI_RGBCOLOR) |
| values->Append(*cssvalue::CSSColorValue::Create(paint.GetColor().Rgb())); |
| return values; |
| } |
| if (paint.type == SVG_PAINTTYPE_NONE) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| if (paint.type == SVG_PAINTTYPE_CURRENTCOLOR) |
| return cssvalue::CSSColorValue::Create(current_color.Rgb()); |
| |
| return cssvalue::CSSColorValue::Create(paint.GetColor().Rgb()); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForSVGResource( |
| const StyleSVGResource* resource) { |
| if (resource) |
| return MakeGarbageCollected<cssvalue::CSSURIValue>(resource->Url()); |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForShadowData(const ShadowData& shadow, |
| const ComputedStyle& style, |
| bool use_spread) { |
| CSSPrimitiveValue* x = ZoomAdjustedPixelValue(shadow.X(), style); |
| CSSPrimitiveValue* y = ZoomAdjustedPixelValue(shadow.Y(), style); |
| CSSPrimitiveValue* blur = ZoomAdjustedPixelValue(shadow.Blur(), style); |
| CSSPrimitiveValue* spread = |
| use_spread ? ZoomAdjustedPixelValue(shadow.Spread(), style) : nullptr; |
| CSSIdentifierValue* shadow_style = |
| shadow.Style() == kNormal |
| ? nullptr |
| : CSSIdentifierValue::Create(CSSValueID::kInset); |
| CSSValue* color = CurrentColorOrValidColor(style, shadow.GetColor()); |
| return MakeGarbageCollected<CSSShadowValue>(x, y, blur, spread, shadow_style, |
| color); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForShadowList(const ShadowList* shadow_list, |
| const ComputedStyle& style, |
| bool use_spread) { |
| if (!shadow_list) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| wtf_size_t shadow_count = shadow_list->Shadows().size(); |
| for (wtf_size_t i = 0; i < shadow_count; ++i) { |
| list->Append( |
| *ValueForShadowData(shadow_list->Shadows()[i], style, use_spread)); |
| } |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForFilter( |
| const ComputedStyle& style, |
| const FilterOperations& filter_operations) { |
| if (filter_operations.Operations().IsEmpty()) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| |
| CSSFunctionValue* filter_value = nullptr; |
| |
| for (const auto& operation : filter_operations.Operations()) { |
| FilterOperation* filter_operation = operation.Get(); |
| switch (filter_operation->GetType()) { |
| case FilterOperation::REFERENCE: |
| filter_value = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kUrl); |
| filter_value->Append(*MakeGarbageCollected<CSSStringValue>( |
| To<ReferenceFilterOperation>(filter_operation)->Url())); |
| break; |
| case FilterOperation::GRAYSCALE: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kGrayscale); |
| filter_value->Append(*CSSNumericLiteralValue::Create( |
| To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(), |
| CSSPrimitiveValue::UnitType::kNumber)); |
| break; |
| case FilterOperation::SEPIA: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSepia); |
| filter_value->Append(*CSSNumericLiteralValue::Create( |
| To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(), |
| CSSPrimitiveValue::UnitType::kNumber)); |
| break; |
| case FilterOperation::SATURATE: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSaturate); |
| filter_value->Append(*CSSNumericLiteralValue::Create( |
| To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(), |
| CSSPrimitiveValue::UnitType::kNumber)); |
| break; |
| case FilterOperation::HUE_ROTATE: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kHueRotate); |
| filter_value->Append(*CSSNumericLiteralValue::Create( |
| To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(), |
| CSSPrimitiveValue::UnitType::kDegrees)); |
| break; |
| case FilterOperation::INVERT: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kInvert); |
| filter_value->Append(*CSSNumericLiteralValue::Create( |
| To<BasicComponentTransferFilterOperation>(filter_operation) |
| ->Amount(), |
| CSSPrimitiveValue::UnitType::kNumber)); |
| break; |
| case FilterOperation::OPACITY: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kOpacity); |
| filter_value->Append(*CSSNumericLiteralValue::Create( |
| To<BasicComponentTransferFilterOperation>(filter_operation) |
| ->Amount(), |
| CSSPrimitiveValue::UnitType::kNumber)); |
| break; |
| case FilterOperation::BRIGHTNESS: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kBrightness); |
| filter_value->Append(*CSSNumericLiteralValue::Create( |
| To<BasicComponentTransferFilterOperation>(filter_operation) |
| ->Amount(), |
| CSSPrimitiveValue::UnitType::kNumber)); |
| break; |
| case FilterOperation::CONTRAST: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kContrast); |
| filter_value->Append(*CSSNumericLiteralValue::Create( |
| To<BasicComponentTransferFilterOperation>(filter_operation) |
| ->Amount(), |
| CSSPrimitiveValue::UnitType::kNumber)); |
| break; |
| case FilterOperation::BLUR: |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kBlur); |
| filter_value->Append(*ZoomAdjustedPixelValue( |
| To<BlurFilterOperation>(filter_operation)->StdDeviation().Value(), |
| style)); |
| break; |
| case FilterOperation::DROP_SHADOW: { |
| const auto& drop_shadow_operation = |
| To<DropShadowFilterOperation>(*filter_operation); |
| filter_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kDropShadow); |
| // We want our computed style to look like that of a text shadow (has |
| // neither spread nor inset style). |
| filter_value->Append( |
| *ValueForShadowData(drop_shadow_operation.Shadow(), style, false)); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| break; |
| } |
| list->Append(*filter_value); |
| } |
| |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForScrollSnapType( |
| const cc::ScrollSnapType& type, |
| const ComputedStyle& style) { |
| if (!type.is_none) { |
| if (type.strictness == cc::SnapStrictness::kProximity) |
| return CSSIdentifierValue::Create(type.axis); |
| return MakeGarbageCollected<CSSValuePair>( |
| CSSIdentifierValue::Create(type.axis), |
| CSSIdentifierValue::Create(type.strictness), |
| CSSValuePair::kDropIdenticalValues); |
| } |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForScrollSnapAlign( |
| const cc::ScrollSnapAlign& align, |
| const ComputedStyle& style) { |
| return MakeGarbageCollected<CSSValuePair>( |
| CSSIdentifierValue::Create(align.alignment_block), |
| CSSIdentifierValue::Create(align.alignment_inline), |
| CSSValuePair::kDropIdenticalValues); |
| } |
| |
| // Returns a suitable value for the page-break-(before|after) property, given |
| // the computed value of the more general break-(before|after) property. |
| CSSValue* ComputedStyleUtils::ValueForPageBreakBetween( |
| EBreakBetween break_value) { |
| switch (break_value) { |
| case EBreakBetween::kAvoidColumn: |
| case EBreakBetween::kColumn: |
| case EBreakBetween::kRecto: |
| case EBreakBetween::kVerso: |
| case EBreakBetween::kAvoidPage: |
| return nullptr; |
| case EBreakBetween::kPage: |
| return CSSIdentifierValue::Create(CSSValueID::kAlways); |
| default: |
| return CSSIdentifierValue::Create(break_value); |
| } |
| } |
| |
| // Returns a suitable value for the -webkit-column-break-(before|after) |
| // property, given the computed value of the more general break-(before|after) |
| // property. |
| CSSValue* ComputedStyleUtils::ValueForWebkitColumnBreakBetween( |
| EBreakBetween break_value) { |
| switch (break_value) { |
| case EBreakBetween::kAvoidPage: |
| case EBreakBetween::kLeft: |
| case EBreakBetween::kPage: |
| case EBreakBetween::kRecto: |
| case EBreakBetween::kRight: |
| case EBreakBetween::kVerso: |
| return nullptr; |
| case EBreakBetween::kColumn: |
| return CSSIdentifierValue::Create(CSSValueID::kAlways); |
| case EBreakBetween::kAvoidColumn: |
| return CSSIdentifierValue::Create(CSSValueID::kAvoid); |
| default: |
| return CSSIdentifierValue::Create(break_value); |
| } |
| } |
| |
| // Returns a suitable value for the page-break-inside property, given the |
| // computed value of the more general break-inside property. |
| CSSValue* ComputedStyleUtils::ValueForPageBreakInside( |
| EBreakInside break_value) { |
| switch (break_value) { |
| case EBreakInside::kAvoidColumn: |
| return nullptr; |
| case EBreakInside::kAvoidPage: |
| return CSSIdentifierValue::Create(CSSValueID::kAvoid); |
| default: |
| return CSSIdentifierValue::Create(break_value); |
| } |
| } |
| |
| // Returns a suitable value for the -webkit-column-break-inside property, given |
| // the computed value of the more general break-inside property. |
| CSSValue* ComputedStyleUtils::ValueForWebkitColumnBreakInside( |
| EBreakInside break_value) { |
| switch (break_value) { |
| case EBreakInside::kAvoidPage: |
| return nullptr; |
| case EBreakInside::kAvoidColumn: |
| return CSSIdentifierValue::Create(CSSValueID::kAvoid); |
| default: |
| return CSSIdentifierValue::Create(break_value); |
| } |
| } |
| |
| // https://drafts.csswg.org/cssom/#resolved-value |
| // |
| // For 'width' and 'height': |
| // |
| // If the property applies to the element or pseudo-element and the resolved |
| // value of the display property is not none or contents, then the resolved |
| // value is the used value. Otherwise the resolved value is the computed value |
| // (https://drafts.csswg.org/css-cascade-4/#computed-value). |
| // |
| // (Note that the computed value exists even when the property does not apply.) |
| bool ComputedStyleUtils::WidthOrHeightShouldReturnUsedValue( |
| const LayoutObject* object) { |
| // The display property is 'none'. |
| if (!object) |
| return false; |
| // Non-root SVG objects return the resolved value except <image>, |
| // <rect> and <foreignObject> which return the used value. |
| if (object->IsSVGChild()) |
| return IsSVGObjectWithWidthAndHeight(*object); |
| // According to |
| // http://www.w3.org/TR/CSS2/visudet.html#the-width-property and |
| // http://www.w3.org/TR/CSS2/visudet.html#the-height-property, the "width" or |
| // "height" property does not apply to non-atomic inline elements. |
| return object->IsAtomicInlineLevel() || !object->IsInline(); |
| } |
| |
| CSSValueList* ComputedStyleUtils::ValuesForShorthandProperty( |
| const StylePropertyShorthand& shorthand, |
| const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| for (unsigned i = 0; i < shorthand.length(); ++i) { |
| const CSSValue* value = |
| shorthand.properties()[i]->CSSValueFromComputedStyle( |
| style, layout_object, allow_visited_style); |
| DCHECK(value); |
| list->Append(*value); |
| } |
| return list; |
| } |
| |
| CSSValuePair* ComputedStyleUtils::ValuesForGapShorthand( |
| const StylePropertyShorthand& shorthand, |
| const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| const CSSValue* row_gap_value = |
| shorthand.properties()[0]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| const CSSValue* column_gap_value = |
| shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| |
| return MakeGarbageCollected<CSSValuePair>(row_gap_value, column_gap_value, |
| CSSValuePair::kDropIdenticalValues); |
| } |
| |
| CSSValueList* ComputedStyleUtils::ValuesForGridShorthand( |
| const StylePropertyShorthand& shorthand, |
| const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| CSSValueList* list = CSSValueList::CreateSlashSeparated(); |
| for (unsigned i = 0; i < shorthand.length(); ++i) { |
| const CSSValue* value = |
| shorthand.properties()[i]->CSSValueFromComputedStyle( |
| style, layout_object, allow_visited_style); |
| DCHECK(value); |
| list->Append(*value); |
| } |
| return list; |
| } |
| |
| CSSValueList* ComputedStyleUtils::ValuesForSidesShorthand( |
| const StylePropertyShorthand& shorthand, |
| const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| // Assume the properties are in the usual order top, right, bottom, left. |
| const CSSValue* top_value = |
| shorthand.properties()[0]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| const CSSValue* right_value = |
| shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| const CSSValue* bottom_value = |
| shorthand.properties()[2]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| const CSSValue* left_value = |
| shorthand.properties()[3]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| |
| // All 4 properties must be specified. |
| if (!top_value || !right_value || !bottom_value || !left_value) |
| return nullptr; |
| |
| bool show_left = !DataEquivalent(right_value, left_value); |
| bool show_bottom = !DataEquivalent(top_value, bottom_value) || show_left; |
| bool show_right = !DataEquivalent(top_value, right_value) || show_bottom; |
| |
| list->Append(*top_value); |
| if (show_right) |
| list->Append(*right_value); |
| if (show_bottom) |
| list->Append(*bottom_value); |
| if (show_left) |
| list->Append(*left_value); |
| |
| return list; |
| } |
| |
| CSSValuePair* ComputedStyleUtils::ValuesForInlineBlockShorthand( |
| const StylePropertyShorthand& shorthand, |
| const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| const CSSValue* start_value = |
| shorthand.properties()[0]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| const CSSValue* end_value = |
| shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| // Both properties must be specified. |
| if (!start_value || !end_value) |
| return nullptr; |
| |
| auto* pair = MakeGarbageCollected<CSSValuePair>( |
| start_value, end_value, CSSValuePair::kDropIdenticalValues); |
| return pair; |
| } |
| |
| CSSValuePair* ComputedStyleUtils::ValuesForPlaceShorthand( |
| const StylePropertyShorthand& shorthand, |
| const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| const CSSValue* align_value = |
| shorthand.properties()[0]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| const CSSValue* justify_value = |
| shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| |
| return MakeGarbageCollected<CSSValuePair>(align_value, justify_value, |
| CSSValuePair::kDropIdenticalValues); |
| } |
| |
| static CSSValue* ExpandNoneLigaturesValue() { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kNoCommonLigatures)); |
| list->Append( |
| *CSSIdentifierValue::Create(CSSValueID::kNoDiscretionaryLigatures)); |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kNoHistoricalLigatures)); |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kNoContextual)); |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValuesForFontVariantProperty( |
| const ComputedStyle& style, |
| const LayoutObject* layout_object, |
| bool allow_visited_style) { |
| enum VariantShorthandCases { |
| kAllNormal, |
| kNoneLigatures, |
| kConcatenateNonNormal |
| }; |
| StylePropertyShorthand shorthand = fontVariantShorthand(); |
| VariantShorthandCases shorthand_case = kAllNormal; |
| for (unsigned i = 0; i < shorthand.length(); ++i) { |
| const CSSValue* value = |
| shorthand.properties()[i]->CSSValueFromComputedStyle( |
| style, layout_object, allow_visited_style); |
| |
| auto* identifier_value = DynamicTo<CSSIdentifierValue>(value); |
| if (shorthand_case == kAllNormal && identifier_value && |
| identifier_value->GetValueID() == CSSValueID::kNone && |
| shorthand.properties()[i]->IDEquals( |
| CSSPropertyID::kFontVariantLigatures)) { |
| shorthand_case = kNoneLigatures; |
| } else if (!(identifier_value && |
| identifier_value->GetValueID() == CSSValueID::kNormal)) { |
| shorthand_case = kConcatenateNonNormal; |
| break; |
| } |
| } |
| |
| switch (shorthand_case) { |
| case kAllNormal: |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| case kNoneLigatures: |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| case kConcatenateNonNormal: { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| for (unsigned i = 0; i < shorthand.length(); ++i) { |
| const CSSValue* value = |
| shorthand.properties()[i]->CSSValueFromComputedStyle( |
| style, layout_object, allow_visited_style); |
| DCHECK(value); |
| auto* identifier_value = DynamicTo<CSSIdentifierValue>(value); |
| if (identifier_value && |
| identifier_value->GetValueID() == CSSValueID::kNone) { |
| list->Append(*ExpandNoneLigaturesValue()); |
| } else if (!(identifier_value && |
| identifier_value->GetValueID() == CSSValueID::kNormal)) { |
| list->Append(*value); |
| } |
| } |
| return list; |
| } |
| default: |
| NOTREACHED(); |
| return nullptr; |
| } |
| } |
| |
| // Returns up to two values for 'scroll-customization' property. The values |
| // correspond to the customization values for 'x' and 'y' axes. |
| CSSValue* ComputedStyleUtils::ScrollCustomizationFlagsToCSSValue( |
| scroll_customization::ScrollDirection scroll_customization) { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| if (scroll_customization == scroll_customization::kScrollDirectionAuto) { |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kAuto)); |
| } else if (scroll_customization == |
| scroll_customization::kScrollDirectionNone) { |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone)); |
| } else { |
| if ((scroll_customization & scroll_customization::kScrollDirectionPanX) == |
| scroll_customization::kScrollDirectionPanX) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanX)); |
| else if (scroll_customization & |
| scroll_customization::kScrollDirectionPanLeft) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanLeft)); |
| else if (scroll_customization & |
| scroll_customization::kScrollDirectionPanRight) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanRight)); |
| if ((scroll_customization & scroll_customization::kScrollDirectionPanY) == |
| scroll_customization::kScrollDirectionPanY) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanY)); |
| else if (scroll_customization & scroll_customization::kScrollDirectionPanUp) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanUp)); |
| else if (scroll_customization & |
| scroll_customization::kScrollDirectionPanDown) |
| list->Append(*CSSIdentifierValue::Create(CSSValueID::kPanDown)); |
| } |
| |
| DCHECK(list->length()); |
| return list; |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForGapLength( |
| const base::Optional<Length>& gap_length, |
| const ComputedStyle& style) { |
| if (!gap_length) |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| return ZoomAdjustedPixelValueForLength(*gap_length, style); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForStyleName(const StyleName& name) { |
| if (name.IsCustomIdent()) |
| return MakeGarbageCollected<CSSCustomIdentValue>(name.GetValue()); |
| return MakeGarbageCollected<CSSStringValue>(name.GetValue()); |
| } |
| |
| CSSValue* ComputedStyleUtils::ValueForStyleNameOrKeyword( |
| const StyleNameOrKeyword& value) { |
| if (value.IsKeyword()) |
| return CSSIdentifierValue::Create(value.GetKeyword()); |
| return ValueForStyleName(value.GetName()); |
| } |
| |
| std::unique_ptr<CrossThreadStyleValue> |
| ComputedStyleUtils::CrossThreadStyleValueFromCSSStyleValue( |
| CSSStyleValue* style_value) { |
| switch (style_value->GetType()) { |
| case CSSStyleValue::StyleValueType::kKeywordType: |
| return std::make_unique<CrossThreadKeywordValue>( |
| To<CSSKeywordValue>(style_value)->value().IsolatedCopy()); |
| case CSSStyleValue::StyleValueType::kUnitType: |
| return std::make_unique<CrossThreadUnitValue>( |
| To<CSSUnitValue>(style_value)->value(), |
| To<CSSUnitValue>(style_value)->GetInternalUnit()); |
| case CSSStyleValue::StyleValueType::kUnsupportedColorType: |
| return std::make_unique<CrossThreadColorValue>( |
| To<CSSUnsupportedColorValue>(style_value)->Value()); |
| case CSSStyleValue::StyleValueType::kUnparsedType: |
| return std::make_unique<CrossThreadUnparsedValue>( |
| To<CSSUnparsedValue>(style_value)->ToString().IsolatedCopy()); |
| default: |
| // Make an isolated copy to ensure that it is safe to pass cross thread. |
| return std::make_unique<CrossThreadUnsupportedValue>( |
| style_value->toString().IsolatedCopy()); |
| } |
| } |
| |
| const CSSValue* ComputedStyleUtils::ComputedPropertyValue( |
| const CSSProperty& property, |
| const ComputedStyle& style, |
| const LayoutObject* layout_object) { |
| switch (property.PropertyID()) { |
| // Computed value is usually relative so that multiple fonts in child |
| // elements work properly, but resolved value is always a pixel length. |
| case CSSPropertyID::kLineHeight: |
| return ComputedStyleUtils::ComputedValueForLineHeight(style); |
| |
| // Returns a transform list instead of converting to a (resolved) matrix. |
| case CSSPropertyID::kTransform: |
| return ComputedStyleUtils::ComputedTransformList(style, layout_object); |
| |
| // For all other properties, the resolved value is either always the same |
| // as the computed value (most properties), or the same as the computed |
| // value when there is no layout box ('width' and friends). |
| default: |
| return property.CSSValueFromComputedStyle( |
| style, /*layout_object=*/nullptr, false); |
| } |
| } |
| |
| } // namespace blink |