|
@@ -197,252 +197,6 @@ static void build_paint_tree(Node& node, Painting::Paintable* parent_paintable =
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static Painting::BorderRadiiData normalized_border_radii_data(Layout::Node const& node, CSSPixelRect const& rect, CSS::BorderRadiusData top_left_radius, CSS::BorderRadiusData top_right_radius, CSS::BorderRadiusData bottom_right_radius, CSS::BorderRadiusData bottom_left_radius)
|
|
|
|
-{
|
|
|
|
- Painting::BorderRadiusData bottom_left_radius_px {};
|
|
|
|
- Painting::BorderRadiusData bottom_right_radius_px {};
|
|
|
|
- Painting::BorderRadiusData top_left_radius_px {};
|
|
|
|
- Painting::BorderRadiusData top_right_radius_px {};
|
|
|
|
-
|
|
|
|
- bottom_left_radius_px.horizontal_radius = bottom_left_radius.horizontal_radius.to_px(node, rect.width());
|
|
|
|
- bottom_right_radius_px.horizontal_radius = bottom_right_radius.horizontal_radius.to_px(node, rect.width());
|
|
|
|
- top_left_radius_px.horizontal_radius = top_left_radius.horizontal_radius.to_px(node, rect.width());
|
|
|
|
- top_right_radius_px.horizontal_radius = top_right_radius.horizontal_radius.to_px(node, rect.width());
|
|
|
|
-
|
|
|
|
- bottom_left_radius_px.vertical_radius = bottom_left_radius.vertical_radius.to_px(node, rect.height());
|
|
|
|
- bottom_right_radius_px.vertical_radius = bottom_right_radius.vertical_radius.to_px(node, rect.height());
|
|
|
|
- top_left_radius_px.vertical_radius = top_left_radius.vertical_radius.to_px(node, rect.height());
|
|
|
|
- top_right_radius_px.vertical_radius = top_right_radius.vertical_radius.to_px(node, rect.height());
|
|
|
|
-
|
|
|
|
- // Scale overlapping curves according to https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
|
|
|
|
- // Let f = min(Li/Si), where i ∈ {top, right, bottom, left},
|
|
|
|
- // Si is the sum of the two corresponding radii of the corners on side i,
|
|
|
|
- // and Ltop = Lbottom = the width of the box, and Lleft = Lright = the height of the box.
|
|
|
|
- auto l_top = rect.width();
|
|
|
|
- auto l_bottom = l_top;
|
|
|
|
- auto l_left = rect.height();
|
|
|
|
- auto l_right = l_left;
|
|
|
|
- auto s_top = (top_left_radius_px.horizontal_radius + top_right_radius_px.horizontal_radius);
|
|
|
|
- auto s_right = (top_right_radius_px.vertical_radius + bottom_right_radius_px.vertical_radius);
|
|
|
|
- auto s_bottom = (bottom_left_radius_px.horizontal_radius + bottom_right_radius_px.horizontal_radius);
|
|
|
|
- auto s_left = (top_left_radius_px.vertical_radius + bottom_left_radius_px.vertical_radius);
|
|
|
|
- CSSPixelFraction f = 1;
|
|
|
|
- f = (s_top != 0) ? min(f, l_top / s_top) : f;
|
|
|
|
- f = (s_right != 0) ? min(f, l_right / s_right) : f;
|
|
|
|
- f = (s_bottom != 0) ? min(f, l_bottom / s_bottom) : f;
|
|
|
|
- f = (s_left != 0) ? min(f, l_left / s_left) : f;
|
|
|
|
-
|
|
|
|
- // If f < 1, then all corner radii are reduced by multiplying them by f.
|
|
|
|
- if (f < 1) {
|
|
|
|
- top_left_radius_px.horizontal_radius *= f;
|
|
|
|
- top_left_radius_px.vertical_radius *= f;
|
|
|
|
- top_right_radius_px.horizontal_radius *= f;
|
|
|
|
- top_right_radius_px.vertical_radius *= f;
|
|
|
|
- bottom_right_radius_px.horizontal_radius *= f;
|
|
|
|
- bottom_right_radius_px.vertical_radius *= f;
|
|
|
|
- bottom_left_radius_px.horizontal_radius *= f;
|
|
|
|
- bottom_left_radius_px.vertical_radius *= f;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return Painting::BorderRadiiData { top_left_radius_px, top_right_radius_px, bottom_right_radius_px, bottom_left_radius_px };
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void LayoutState::resolve_layout_dependent_properties()
|
|
|
|
-{
|
|
|
|
- // Resolves layout-dependent properties not handled during layout and stores them in the paint tree.
|
|
|
|
- // Properties resolved include:
|
|
|
|
- // - Border radii
|
|
|
|
- // - Box shadows
|
|
|
|
- // - Text shadows
|
|
|
|
- // - Transforms
|
|
|
|
- // - Transform origins
|
|
|
|
-
|
|
|
|
- for (auto& it : used_values_per_layout_node) {
|
|
|
|
- auto& used_values = *it.value;
|
|
|
|
- auto& node = const_cast<NodeWithStyle&>(used_values.node());
|
|
|
|
-
|
|
|
|
- auto* paintable = node.paintable();
|
|
|
|
- auto const is_inline_paintable = paintable && paintable->is_inline_paintable();
|
|
|
|
- auto const is_paintable_box = paintable && paintable->is_paintable_box();
|
|
|
|
- auto const is_paintable_with_lines = paintable && paintable->is_paintable_with_lines();
|
|
|
|
-
|
|
|
|
- // Border radii
|
|
|
|
- if (is_inline_paintable) {
|
|
|
|
- auto& inline_paintable = static_cast<Painting::InlinePaintable&>(*paintable);
|
|
|
|
- auto& fragments = inline_paintable.fragments();
|
|
|
|
-
|
|
|
|
- auto const& top_left_border_radius = inline_paintable.computed_values().border_top_left_radius();
|
|
|
|
- auto const& top_right_border_radius = inline_paintable.computed_values().border_top_right_radius();
|
|
|
|
- auto const& bottom_right_border_radius = inline_paintable.computed_values().border_bottom_right_radius();
|
|
|
|
- auto const& bottom_left_border_radius = inline_paintable.computed_values().border_bottom_left_radius();
|
|
|
|
-
|
|
|
|
- auto containing_block_position_in_absolute_coordinates = inline_paintable.containing_block()->paintable_box()->absolute_position();
|
|
|
|
- for (size_t i = 0; i < fragments.size(); ++i) {
|
|
|
|
- auto is_first_fragment = i == 0;
|
|
|
|
- auto is_last_fragment = i == fragments.size() - 1;
|
|
|
|
- auto& fragment = fragments[i];
|
|
|
|
- CSSPixelRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() };
|
|
|
|
- if (is_first_fragment) {
|
|
|
|
- auto extra_start_width = inline_paintable.box_model().padding.left;
|
|
|
|
- absolute_fragment_rect.translate_by(-extra_start_width, 0);
|
|
|
|
- absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_start_width);
|
|
|
|
- }
|
|
|
|
- if (is_last_fragment) {
|
|
|
|
- auto extra_end_width = inline_paintable.box_model().padding.right;
|
|
|
|
- absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_end_width);
|
|
|
|
- }
|
|
|
|
- auto border_radii_data = normalized_border_radii_data(inline_paintable.layout_node(), absolute_fragment_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
|
|
|
|
- fragment.set_border_radii_data(border_radii_data);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Border radii
|
|
|
|
- if (is_paintable_box) {
|
|
|
|
- auto& paintable_box = static_cast<Painting::PaintableBox&>(*paintable);
|
|
|
|
-
|
|
|
|
- CSSPixelRect const border_rect { 0, 0, used_values.border_box_width(), used_values.border_box_height() };
|
|
|
|
-
|
|
|
|
- auto const& border_top_left_radius = node.computed_values().border_top_left_radius();
|
|
|
|
- auto const& border_top_right_radius = node.computed_values().border_top_right_radius();
|
|
|
|
- auto const& border_bottom_right_radius = node.computed_values().border_bottom_right_radius();
|
|
|
|
- auto const& border_bottom_left_radius = node.computed_values().border_bottom_left_radius();
|
|
|
|
-
|
|
|
|
- auto radii_data = normalized_border_radii_data(node, border_rect, border_top_left_radius, border_top_right_radius, border_bottom_right_radius, border_bottom_left_radius);
|
|
|
|
- paintable_box.set_border_radii_data(radii_data);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Box shadows
|
|
|
|
- auto const& box_shadow_data = node.computed_values().box_shadow();
|
|
|
|
- if (!box_shadow_data.is_empty()) {
|
|
|
|
- Vector<Painting::ShadowData> resolved_box_shadow_data;
|
|
|
|
- resolved_box_shadow_data.ensure_capacity(box_shadow_data.size());
|
|
|
|
- for (auto const& layer : box_shadow_data) {
|
|
|
|
- resolved_box_shadow_data.empend(
|
|
|
|
- layer.color,
|
|
|
|
- layer.offset_x.to_px(node),
|
|
|
|
- layer.offset_y.to_px(node),
|
|
|
|
- layer.blur_radius.to_px(node),
|
|
|
|
- layer.spread_distance.to_px(node),
|
|
|
|
- layer.placement == CSS::ShadowPlacement::Outer ? Painting::ShadowPlacement::Outer : Painting::ShadowPlacement::Inner);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (paintable && is<Painting::PaintableBox>(*paintable)) {
|
|
|
|
- auto& paintable_box = static_cast<Painting::PaintableBox&>(*paintable);
|
|
|
|
- paintable_box.set_box_shadow_data(move(resolved_box_shadow_data));
|
|
|
|
- } else if (paintable && is<Painting::InlinePaintable>(*paintable)) {
|
|
|
|
- auto& inline_paintable = static_cast<Painting::InlinePaintable&>(*paintable);
|
|
|
|
- inline_paintable.set_box_shadow_data(move(resolved_box_shadow_data));
|
|
|
|
- } else {
|
|
|
|
- VERIFY_NOT_REACHED();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Text shadows
|
|
|
|
- if (is_paintable_with_lines) {
|
|
|
|
- auto const& paintable_with_lines = static_cast<Painting::PaintableWithLines const&>(*paintable);
|
|
|
|
- for (auto const& fragment : paintable_with_lines.fragments()) {
|
|
|
|
- auto const& text_shadow = fragment.m_layout_node->computed_values().text_shadow();
|
|
|
|
- if (!text_shadow.is_empty()) {
|
|
|
|
- Vector<Painting::ShadowData> resolved_shadow_data;
|
|
|
|
- resolved_shadow_data.ensure_capacity(text_shadow.size());
|
|
|
|
- for (auto const& layer : text_shadow) {
|
|
|
|
- resolved_shadow_data.empend(
|
|
|
|
- layer.color,
|
|
|
|
- layer.offset_x.to_px(paintable_with_lines.layout_node()),
|
|
|
|
- layer.offset_y.to_px(paintable_with_lines.layout_node()),
|
|
|
|
- layer.blur_radius.to_px(paintable_with_lines.layout_node()),
|
|
|
|
- layer.spread_distance.to_px(paintable_with_lines.layout_node()),
|
|
|
|
- Painting::ShadowPlacement::Outer);
|
|
|
|
- }
|
|
|
|
- const_cast<Painting::PaintableFragment&>(fragment).set_shadows(move(resolved_shadow_data));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Transform and transform origin
|
|
|
|
- if (is_paintable_box) {
|
|
|
|
- auto& paintable_box = static_cast<Painting::PaintableBox&>(*paintable);
|
|
|
|
- auto const& transformations = paintable_box.computed_values().transformations();
|
|
|
|
- if (!transformations.is_empty()) {
|
|
|
|
- auto matrix = Gfx::FloatMatrix4x4::identity();
|
|
|
|
- for (auto const& transform : transformations)
|
|
|
|
- matrix = matrix * transform.to_matrix(paintable_box).release_value();
|
|
|
|
- paintable_box.set_transform(matrix);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- auto const& transform_origin = paintable_box.computed_values().transform_origin();
|
|
|
|
- // https://www.w3.org/TR/css-transforms-1/#transform-box
|
|
|
|
- auto transform_box = paintable_box.computed_values().transform_box();
|
|
|
|
- // For SVG elements without associated CSS layout box, the used value for content-box is fill-box and for
|
|
|
|
- // border-box is stroke-box.
|
|
|
|
- // FIXME: This currently detects any SVG element except the <svg> one. Is that correct?
|
|
|
|
- // And is it correct to use `else` below?
|
|
|
|
- if (is<Painting::SVGPaintable>(paintable_box)) {
|
|
|
|
- switch (transform_box) {
|
|
|
|
- case CSS::TransformBox::ContentBox:
|
|
|
|
- transform_box = CSS::TransformBox::FillBox;
|
|
|
|
- break;
|
|
|
|
- case CSS::TransformBox::BorderBox:
|
|
|
|
- transform_box = CSS::TransformBox::StrokeBox;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- // For elements with associated CSS layout box, the used value for fill-box is content-box and for
|
|
|
|
- // stroke-box and view-box is border-box.
|
|
|
|
- else {
|
|
|
|
- switch (transform_box) {
|
|
|
|
- case CSS::TransformBox::FillBox:
|
|
|
|
- transform_box = CSS::TransformBox::ContentBox;
|
|
|
|
- break;
|
|
|
|
- case CSS::TransformBox::StrokeBox:
|
|
|
|
- case CSS::TransformBox::ViewBox:
|
|
|
|
- transform_box = CSS::TransformBox::BorderBox;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- CSSPixelRect reference_box = [&]() {
|
|
|
|
- switch (transform_box) {
|
|
|
|
- case CSS::TransformBox::ContentBox:
|
|
|
|
- // Uses the content box as reference box.
|
|
|
|
- // FIXME: The reference box of a table is the border box of its table wrapper box, not its table box.
|
|
|
|
- return paintable_box.absolute_rect();
|
|
|
|
- case CSS::TransformBox::BorderBox:
|
|
|
|
- // Uses the border box as reference box.
|
|
|
|
- // FIXME: The reference box of a table is the border box of its table wrapper box, not its table box.
|
|
|
|
- return paintable_box.absolute_border_box_rect();
|
|
|
|
- case CSS::TransformBox::FillBox:
|
|
|
|
- // Uses the object bounding box as reference box.
|
|
|
|
- // FIXME: For now we're using the content rect as an approximation.
|
|
|
|
- return paintable_box.absolute_rect();
|
|
|
|
- case CSS::TransformBox::StrokeBox:
|
|
|
|
- // Uses the stroke bounding box as reference box.
|
|
|
|
- // FIXME: For now we're using the border rect as an approximation.
|
|
|
|
- return paintable_box.absolute_border_box_rect();
|
|
|
|
- case CSS::TransformBox::ViewBox:
|
|
|
|
- // Uses the nearest SVG viewport as reference box.
|
|
|
|
- // FIXME: If a viewBox attribute is specified for the SVG viewport creating element:
|
|
|
|
- // - The reference box is positioned at the origin of the coordinate system established by the viewBox attribute.
|
|
|
|
- // - The dimension of the reference box is set to the width and height values of the viewBox attribute.
|
|
|
|
- auto* svg_paintable = paintable_box.first_ancestor_of_type<Painting::SVGSVGPaintable>();
|
|
|
|
- if (!svg_paintable)
|
|
|
|
- return paintable_box.absolute_border_box_rect();
|
|
|
|
- return svg_paintable->absolute_rect();
|
|
|
|
- }
|
|
|
|
- VERIFY_NOT_REACHED();
|
|
|
|
- }();
|
|
|
|
- auto x = reference_box.left() + transform_origin.x.to_px(node, reference_box.width());
|
|
|
|
- auto y = reference_box.top() + transform_origin.y.to_px(node, reference_box.height());
|
|
|
|
- paintable_box.set_transform_origin({ x, y });
|
|
|
|
- paintable_box.set_transform_origin({ x, y });
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
void LayoutState::commit(Box& root)
|
|
void LayoutState::commit(Box& root)
|
|
{
|
|
{
|
|
// Only the top-level LayoutState should ever be committed.
|
|
// Only the top-level LayoutState should ever be committed.
|
|
@@ -599,8 +353,6 @@ void LayoutState::commit(Box& root)
|
|
auto const& box = static_cast<Layout::Box const&>(used_values.node());
|
|
auto const& box = static_cast<Layout::Box const&>(used_values.node());
|
|
measure_scrollable_overflow(box);
|
|
measure_scrollable_overflow(box);
|
|
}
|
|
}
|
|
-
|
|
|
|
- resolve_layout_dependent_properties();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void LayoutState::UsedValues::set_node(NodeWithStyle& node, UsedValues const* containing_block_used_values)
|
|
void LayoutState::UsedValues::set_node(NodeWithStyle& node, UsedValues const* containing_block_used_values)
|