| // 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" |
| |
| 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); |
| } |
| |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| list->Append(*ZoomAdjustedPixelValueForLength(fill_size.size.Width(), style)); |
| list->Append( |
| *ZoomAdjustedPixelValueForLength(fill_size.size.Height(), style)); |
| return list; |
| } |
| |
| 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()) { |
| list->Append( |
| *ZoomAdjustedPixelValueForLength(curr_layer->PositionX(), 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()) { |
| list->Append( |
| *ZoomAdjustedPixelValueForLength(curr_layer->PositionY(), 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); |
| } |
| |
| 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::kWebkitPictograph) |
| return CSSValueID::kWebkitPictograph; |
| 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, |
| wtf_size_t start, |
| wtf_size_t end, |
| size_t offset = 0) { |
| DCHECK_LE(end, tracks.size()); |
| for (wtf_size_t i = start; i < end; ++i) { |
| AddValuesForNamedGridLinesAtIndex(collector, i + offset, *list); |
| list->Append(*getTrackSize(tracks[i])); |
| } |
| AddValuesForNamedGridLinesAtIndex(collector, end + offset, *list); |
| } |
| |
| template <typename T, typename F> |
| void PopulateGridTrackList(CSSValueList* list, |
| OrderedNamedLinesCollector& collector, |
| const Vector<T>& tracks, |
| F getTrackSize) { |
| PopulateGridTrackList<T>(list, collector, tracks, getTrackSize, 0, |
| tracks.size()); |
| } |
| |
| 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. |
| if (track_sizes.IsEmpty() && auto_repeat_track_sizes.IsEmpty()) |
| 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()); |
| PopulateGridTrackList( |
| list, collector, grid->TrackSizesForComputedStyle(direction), |
| [&](const LayoutUnit& v) { return ZoomAdjustedPixelValue(v, style); }); |
| 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; |
| } |
| |
| const CSSValue& ComputedStyleUtils::ValueForBorderRadiusCorner( |
| const LengthSize& radius, |
| const ComputedStyle& style) { |
| CSSValueList& list = *ValuesForBorderRadiusCorner(radius, style); |
| if (list.Item(0) == list.Item(1)) |
| return list.Item(0); |
| return list; |
| } |
| |
| CSSFunctionValue* ValueForMatrixTransform( |
| const TransformationMatrix& transform_param, |
| const ComputedStyle& style) { |
| // Take TransformationMatrix by reference and then copy it because VC++ |
| // doesn't guarantee alignment of function parameters. |
| TransformationMatrix transform = transform_param; |
| CSSFunctionValue* transform_value = nullptr; |
| transform.Zoom(1 / style.EffectiveZoom()); |
| if (transform.IsAffine()) { |
| transform_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMatrix); |
| |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.A(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.B(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.C(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.D(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.E(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.F(), CSSPrimitiveValue::UnitType::kNumber)); |
| } else { |
| transform_value = |
| MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kMatrix3d); |
| |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M11(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M12(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M13(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M14(), CSSPrimitiveValue::UnitType::kNumber)); |
| |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M21(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M22(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M23(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M24(), CSSPrimitiveValue::UnitType::kNumber)); |
| |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M31(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M32(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M33(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M34(), CSSPrimitiveValue::UnitType::kNumber)); |
| |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M41(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M42(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M43(), CSSPrimitiveValue::UnitType::kNumber)); |
| transform_value->Append(*CSSNumericLiteralValue::Create( |
| transform.M44(), CSSPrimitiveValue::UnitType::kNumber)); |
| } |
| |
| return transform_value; |
| } |
| |
| 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::ComputedTransform( |
| 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(*ValueForMatrixTransform(transform, style)); |
| |
| 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) { |
| 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(); |
| } |
| } |
| if (!list->length()) { |
| PseudoId pseudoId = style.StyleType(); |
| if (pseudoId == kPseudoIdBefore || pseudoId == kPseudoIdAfter) |
| return CSSIdentifierValue::Create(CSSValueID::kNone); |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| } |
| |
| 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 GapLength& gap_length, |
| const ComputedStyle& style) { |
| if (gap_length.IsNormal()) |
| return CSSIdentifierValue::Create(CSSValueID::kNormal); |
| return ZoomAdjustedPixelValueForLength(gap_length.GetLength(), style); |
| } |
| |
| 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()); |
| } |
| } |
| |
| CSSValuePair* ComputedStyleUtils::ValuesForIntrinsicSizeShorthand( |
| 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); |
| if (!start_value) |
| return nullptr; |
| |
| const CSSValue* end_value = |
| shorthand.properties()[1]->CSSValueFromComputedStyle(style, layout_object, |
| allow_visited_style); |
| if (!end_value) |
| end_value = start_value; |
| return MakeGarbageCollected<CSSValuePair>(start_value, end_value, |
| CSSValuePair::kDropIdenticalValues); |
| } |
| |
| } // namespace blink |