mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
layout: Add initial support for clip-path: [<basic-shape> || <shape-box>]
(#33107)
* Turn on clip-path tests and add results Signed-off-by: Martin Robinson <mrobinson@igalia.com> * enhance: Add support for `clip-path: [<basic-shape> || <shape-box>]` Signed-off-by: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com> * Changes from review Signed-off-by: Martin Robinson <mrobinson@igalia.com> --------- Signed-off-by: Martin Robinson <mrobinson@igalia.com> Signed-off-by: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com> Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
f810983fd2
commit
590527176e
56 changed files with 450 additions and 89 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -1283,7 +1283,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "derive_common"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
|
@ -1472,7 +1472,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "dom"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
@ -4083,7 +4083,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "malloc_size_of"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"accountable-refcell",
|
||||
"app_units",
|
||||
|
@ -5777,7 +5777,7 @@ checksum = "0495e4577c672de8254beb68d01a9b62d0e8a13c099edecdbedccce3223cd29f"
|
|||
[[package]]
|
||||
name = "selectors"
|
||||
version = "0.24.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cssparser",
|
||||
|
@ -6091,7 +6091,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "servo_arc"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
|
@ -6100,7 +6100,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "servo_atoms"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
|
@ -6304,7 +6304,7 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
|||
[[package]]
|
||||
name = "size_of_test"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
@ -6445,7 +6445,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||
[[package]]
|
||||
name = "static_prefs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
|
||||
[[package]]
|
||||
name = "strck"
|
||||
|
@ -6498,7 +6498,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "style"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"app_units",
|
||||
"arrayvec",
|
||||
|
@ -6557,7 +6557,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "style_config"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
@ -6565,7 +6565,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "style_derive"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"derive_common",
|
||||
|
@ -6596,7 +6596,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "style_traits"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"app_units",
|
||||
"bitflags 2.6.0",
|
||||
|
@ -6945,7 +6945,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "to_shmem"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"servo_arc",
|
||||
|
@ -6958,7 +6958,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "to_shmem_derive"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#3359447b53f0d0bc4affe135313ca8d0e48cb876"
|
||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#65f8d1316a0966401bcfb9fa7dd5e3659a1605b2"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"derive_common",
|
||||
|
|
242
components/layout_2020/display_list/clip_path.rs
Normal file
242
components/layout_2020/display_list/clip_path.rs
Normal file
|
@ -0,0 +1,242 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use style::values::computed::basic_shape::{BasicShape, ClipPath};
|
||||
use style::values::computed::length::Length;
|
||||
use style::values::computed::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
|
||||
use style::values::computed::position::Position;
|
||||
use style::values::generics::basic_shape::{GenericShapeRadius, ShapeBox, ShapeGeometryBox};
|
||||
use style::values::generics::position::GenericPositionOrAuto;
|
||||
use webrender_api::units::{LayoutRect, LayoutSideOffsets, LayoutSize};
|
||||
use webrender_api::ClipChainId;
|
||||
use webrender_traits::display_list::ScrollTreeNodeId;
|
||||
|
||||
use super::{compute_margin_box_radius, normalize_radii, BuilderForBoxFragment, DisplayList};
|
||||
|
||||
pub(super) fn build_clip_path_clip_chain_if_necessary(
|
||||
clip_path: ClipPath,
|
||||
display_list: &mut DisplayList,
|
||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||
parent_clip_chain_id: &ClipChainId,
|
||||
fragment_builder: BuilderForBoxFragment,
|
||||
) -> Option<ClipChainId> {
|
||||
let geometry_box = match clip_path {
|
||||
ClipPath::Shape(_, ShapeGeometryBox::ShapeBox(shape_box)) => shape_box,
|
||||
ClipPath::Shape(_, ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox,
|
||||
ClipPath::Box(ShapeGeometryBox::ShapeBox(shape_box)) => shape_box,
|
||||
ClipPath::Box(ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox,
|
||||
_ => return None,
|
||||
};
|
||||
let layout_rect = match geometry_box {
|
||||
ShapeBox::BorderBox => fragment_builder.border_rect,
|
||||
ShapeBox::ContentBox => *fragment_builder.content_rect(),
|
||||
ShapeBox::PaddingBox => *fragment_builder.padding_rect(),
|
||||
ShapeBox::MarginBox => *fragment_builder.margin_rect(),
|
||||
};
|
||||
if let ClipPath::Shape(shape, _) = clip_path {
|
||||
match *shape {
|
||||
BasicShape::Circle(_) | BasicShape::Ellipse(_) | BasicShape::Rect(_) => {
|
||||
build_simple_shape(
|
||||
*shape,
|
||||
layout_rect,
|
||||
parent_scroll_node_id,
|
||||
parent_clip_chain_id,
|
||||
display_list,
|
||||
)
|
||||
},
|
||||
BasicShape::Polygon(_) | BasicShape::PathOrShape(_) => None,
|
||||
}
|
||||
} else {
|
||||
Some(create_rect_clip_chain(
|
||||
match geometry_box {
|
||||
ShapeBox::MarginBox => compute_margin_box_radius(
|
||||
fragment_builder.border_radius,
|
||||
layout_rect.size(),
|
||||
fragment_builder.fragment,
|
||||
),
|
||||
_ => fragment_builder.border_radius,
|
||||
},
|
||||
layout_rect,
|
||||
parent_scroll_node_id,
|
||||
parent_clip_chain_id,
|
||||
display_list,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn build_simple_shape(
|
||||
shape: BasicShape,
|
||||
layout_box: LayoutRect,
|
||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||
parent_clip_chain_id: &ClipChainId,
|
||||
display_list: &mut DisplayList,
|
||||
) -> Option<ClipChainId> {
|
||||
match shape {
|
||||
BasicShape::Rect(rect) => {
|
||||
let insets = LayoutSideOffsets::new(
|
||||
rect.rect.0.resolve(Length::new(layout_box.height())).px(),
|
||||
rect.rect.1.resolve(Length::new(layout_box.width())).px(),
|
||||
rect.rect.2.resolve(Length::new(layout_box.height())).px(),
|
||||
rect.rect.3.resolve(Length::new(layout_box.width())).px(),
|
||||
);
|
||||
|
||||
// `inner_rect()` will cause an assertion failure if the insets are larger than the
|
||||
// rectangle dimension.
|
||||
let shape_rect = if insets.left + insets.right >= layout_box.width() ||
|
||||
insets.top + insets.bottom > layout_box.height()
|
||||
{
|
||||
LayoutRect::from_origin_and_size(layout_box.min, LayoutSize::zero())
|
||||
} else {
|
||||
layout_box.to_rect().inner_rect(insets).to_box2d()
|
||||
};
|
||||
|
||||
let resolve = |radius: &LengthPercentage, box_size: f32| {
|
||||
radius.percentage_relative_to(Length::new(box_size)).px()
|
||||
};
|
||||
let corner = |corner: &style::values::computed::BorderCornerRadius| {
|
||||
LayoutSize::new(
|
||||
resolve(&corner.0.width.0, layout_box.size().width),
|
||||
resolve(&corner.0.height.0, layout_box.size().height),
|
||||
)
|
||||
};
|
||||
let mut radii = webrender_api::BorderRadius {
|
||||
top_left: corner(&rect.round.top_left),
|
||||
top_right: corner(&rect.round.top_right),
|
||||
bottom_left: corner(&rect.round.bottom_left),
|
||||
bottom_right: corner(&rect.round.bottom_right),
|
||||
};
|
||||
normalize_radii(&layout_box, &mut radii);
|
||||
Some(create_rect_clip_chain(
|
||||
radii,
|
||||
shape_rect,
|
||||
parent_scroll_node_id,
|
||||
parent_clip_chain_id,
|
||||
display_list,
|
||||
))
|
||||
},
|
||||
BasicShape::Circle(circle) => {
|
||||
let center = match circle.position {
|
||||
GenericPositionOrAuto::Position(position) => position,
|
||||
GenericPositionOrAuto::Auto => Position::center(),
|
||||
};
|
||||
let anchor_x = center.horizontal.resolve(Length::new(layout_box.width()));
|
||||
let anchor_y = center.vertical.resolve(Length::new(layout_box.height()));
|
||||
let center = layout_box
|
||||
.min
|
||||
.add_size(&LayoutSize::new(anchor_x.px(), anchor_y.px()));
|
||||
|
||||
let horizontal =
|
||||
compute_shape_radius(center.x, &circle.radius, layout_box.min.x, layout_box.max.x);
|
||||
let vertical =
|
||||
compute_shape_radius(center.y, &circle.radius, layout_box.min.y, layout_box.max.y);
|
||||
|
||||
// If the value is `Length` then both values should be the same at this point.
|
||||
let radius = match circle.radius {
|
||||
GenericShapeRadius::FarthestSide => horizontal.max(vertical),
|
||||
GenericShapeRadius::ClosestSide => horizontal.min(vertical),
|
||||
GenericShapeRadius::Length(_) => horizontal,
|
||||
};
|
||||
let radius = LayoutSize::new(radius, radius);
|
||||
let mut radii = webrender_api::BorderRadius {
|
||||
top_left: radius,
|
||||
top_right: radius,
|
||||
bottom_left: radius,
|
||||
bottom_right: radius,
|
||||
};
|
||||
let start = center.add_size(&-radius);
|
||||
let rect = LayoutRect::from_origin_and_size(start, radius * 2.);
|
||||
normalize_radii(&layout_box, &mut radii);
|
||||
Some(create_rect_clip_chain(
|
||||
radii,
|
||||
rect,
|
||||
parent_scroll_node_id,
|
||||
parent_clip_chain_id,
|
||||
display_list,
|
||||
))
|
||||
},
|
||||
BasicShape::Ellipse(ellipse) => {
|
||||
let center = match ellipse.position {
|
||||
GenericPositionOrAuto::Position(position) => position,
|
||||
GenericPositionOrAuto::Auto => Position::center(),
|
||||
};
|
||||
let anchor_x = center.horizontal.resolve(Length::new(layout_box.width()));
|
||||
let anchor_y = center.vertical.resolve(Length::new(layout_box.height()));
|
||||
let center = layout_box
|
||||
.min
|
||||
.add_size(&LayoutSize::new(anchor_x.px(), anchor_y.px()));
|
||||
|
||||
let width = compute_shape_radius(
|
||||
center.x,
|
||||
&ellipse.semiaxis_x,
|
||||
layout_box.min.x,
|
||||
layout_box.max.x,
|
||||
);
|
||||
let height = compute_shape_radius(
|
||||
center.y,
|
||||
&ellipse.semiaxis_y,
|
||||
layout_box.min.y,
|
||||
layout_box.max.y,
|
||||
);
|
||||
|
||||
let mut radii = webrender_api::BorderRadius {
|
||||
top_left: LayoutSize::new(width, height),
|
||||
top_right: LayoutSize::new(width, height),
|
||||
bottom_left: LayoutSize::new(width, height),
|
||||
bottom_right: LayoutSize::new(width, height),
|
||||
};
|
||||
let size = LayoutSize::new(width, height);
|
||||
let start = center.add_size(&-size);
|
||||
let rect = LayoutRect::from_origin_and_size(start, size * 2.);
|
||||
normalize_radii(&rect, &mut radii);
|
||||
Some(create_rect_clip_chain(
|
||||
radii,
|
||||
rect,
|
||||
parent_scroll_node_id,
|
||||
parent_clip_chain_id,
|
||||
display_list,
|
||||
))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_shape_radius(
|
||||
center: f32,
|
||||
radius: &GenericShapeRadius<NonNegativeLengthPercentage>,
|
||||
min_edge: f32,
|
||||
max_edge: f32,
|
||||
) -> f32 {
|
||||
let distance_from_min_edge = (min_edge - center).abs();
|
||||
let distance_from_max_edge = (max_edge - center).abs();
|
||||
match radius {
|
||||
GenericShapeRadius::FarthestSide => distance_from_min_edge.max(distance_from_max_edge),
|
||||
GenericShapeRadius::ClosestSide => distance_from_min_edge.min(distance_from_max_edge),
|
||||
GenericShapeRadius::Length(length) => {
|
||||
length.0.resolve(Length::new(max_edge - min_edge)).px()
|
||||
},
|
||||
}
|
||||
}
|
||||
fn create_rect_clip_chain(
|
||||
radii: webrender_api::BorderRadius,
|
||||
rect: LayoutRect,
|
||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||
parent_clip_chain_id: &ClipChainId,
|
||||
display_list: &mut DisplayList,
|
||||
) -> ClipChainId {
|
||||
let new_clip_id = if radii.is_zero() {
|
||||
display_list
|
||||
.wr
|
||||
.define_clip_rect(parent_scroll_node_id.spatial_id, rect)
|
||||
} else {
|
||||
display_list.wr.define_clip_rounded_rect(
|
||||
parent_scroll_node_id.spatial_id,
|
||||
webrender_api::ComplexClipRegion {
|
||||
rect,
|
||||
radii,
|
||||
mode: webrender_api::ClipMode::Clip,
|
||||
},
|
||||
)
|
||||
};
|
||||
display_list.define_clip_chain(*parent_clip_chain_id, [new_clip_id])
|
||||
}
|
|
@ -25,14 +25,15 @@ use style::properties::ComputedValues;
|
|||
use style::values::computed::image::Image;
|
||||
use style::values::computed::{
|
||||
BorderImageSideWidth, BorderImageWidth, BorderStyle, Color, Length, LengthPercentage,
|
||||
NonNegativeLengthOrNumber, NumberOrPercentage, OutlineStyle,
|
||||
LengthPercentageOrAuto, NonNegativeLengthOrNumber, NumberOrPercentage, OutlineStyle,
|
||||
};
|
||||
use style::values::generics::rect::Rect;
|
||||
use style::values::generics::NonNegative;
|
||||
use style::values::specified::text::TextDecorationLine;
|
||||
use style::values::specified::ui::CursorKind;
|
||||
use style::Zero;
|
||||
use style_traits::{CSSPixel, DevicePixel};
|
||||
use webrender_api::units::LayoutPixel;
|
||||
use webrender_api::units::{LayoutPixel, LayoutSize};
|
||||
use webrender_api::{
|
||||
self as wr, units, BorderDetails, BoxShadowClipMode, ClipChainId, CommonItemProperties,
|
||||
ImageRendering, NinePatchBorder, NinePatchBorderSource,
|
||||
|
@ -53,6 +54,7 @@ use crate::replaced::IntrinsicSizes;
|
|||
use crate::style_ext::ComputedValuesExt;
|
||||
|
||||
mod background;
|
||||
mod clip_path;
|
||||
mod conversions;
|
||||
mod gradient;
|
||||
mod stacking_context;
|
||||
|
@ -482,6 +484,7 @@ struct BuilderForBoxFragment<'a> {
|
|||
fragment: &'a BoxFragment,
|
||||
containing_block: &'a PhysicalRect<Au>,
|
||||
border_rect: units::LayoutRect,
|
||||
margin_rect: OnceCell<units::LayoutRect>,
|
||||
padding_rect: OnceCell<units::LayoutRect>,
|
||||
content_rect: OnceCell<units::LayoutRect>,
|
||||
border_radius: wr::BorderRadius,
|
||||
|
@ -514,23 +517,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
bottom_right: corner(&b.border_bottom_right_radius),
|
||||
bottom_left: corner(&b.border_bottom_left_radius),
|
||||
};
|
||||
// Normalize radii that add up to > 100%.
|
||||
// https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
|
||||
// > Let f = min(L_i/S_i), where i ∈ {top, right, bottom, left},
|
||||
// > S_i is the sum of the two corresponding radii of the corners on side i,
|
||||
// > and L_top = L_bottom = the width of the box,
|
||||
// > and L_left = L_right = the height of the box.
|
||||
// > If f < 1, then all corner radii are reduced by multiplying them by f.
|
||||
let f = (border_rect.width() / (radius.top_left.width + radius.top_right.width))
|
||||
.min(border_rect.width() / (radius.bottom_left.width + radius.bottom_right.width))
|
||||
.min(border_rect.height() / (radius.top_left.height + radius.bottom_left.height))
|
||||
.min(border_rect.height() / (radius.top_right.height + radius.bottom_right.height));
|
||||
if f < 1.0 {
|
||||
radius.top_left *= f;
|
||||
radius.top_right *= f;
|
||||
radius.bottom_right *= f;
|
||||
radius.bottom_left *= f;
|
||||
}
|
||||
normalize_radii(&border_rect, &mut radius);
|
||||
radius
|
||||
};
|
||||
|
||||
|
@ -539,6 +526,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
containing_block,
|
||||
border_rect,
|
||||
border_radius,
|
||||
margin_rect: OnceCell::new(),
|
||||
padding_rect: OnceCell::new(),
|
||||
content_rect: OnceCell::new(),
|
||||
border_edge_clip_chain_id: RefCell::new(None),
|
||||
|
@ -565,6 +553,15 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
fn margin_rect(&self) -> &units::LayoutRect {
|
||||
self.margin_rect.get_or_init(|| {
|
||||
self.fragment
|
||||
.margin_rect()
|
||||
.translate(self.containing_block.origin.to_vector())
|
||||
.to_webrender()
|
||||
})
|
||||
}
|
||||
|
||||
fn border_edge_clip(
|
||||
&self,
|
||||
builder: &mut DisplayListBuilder,
|
||||
|
@ -1300,3 +1297,84 @@ fn resolve_border_image_slice(
|
|||
resolve_percentage(border_image_slice.3, size.width),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn normalize_radii(rect: &units::LayoutRect, radius: &mut wr::BorderRadius) {
|
||||
// Normalize radii that add up to > 100%.
|
||||
// https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
|
||||
// > Let f = min(L_i/S_i), where i ∈ {top, right, bottom, left},
|
||||
// > S_i is the sum of the two corresponding radii of the corners on side i,
|
||||
// > and L_top = L_bottom = the width of the box,
|
||||
// > and L_left = L_right = the height of the box.
|
||||
// > If f < 1, then all corner radii are reduced by multiplying them by f.
|
||||
let f = (rect.width() / (radius.top_left.width + radius.top_right.width))
|
||||
.min(rect.width() / (radius.bottom_left.width + radius.bottom_right.width))
|
||||
.min(rect.height() / (radius.top_left.height + radius.bottom_left.height))
|
||||
.min(rect.height() / (radius.top_right.height + radius.bottom_right.height));
|
||||
if f < 1.0 {
|
||||
radius.top_left *= f;
|
||||
radius.top_right *= f;
|
||||
radius.bottom_right *= f;
|
||||
radius.bottom_left *= f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://drafts.csswg.org/css-shapes-1/#valdef-shape-box-margin-box>
|
||||
/// > The corner radii of this shape are determined by the corresponding
|
||||
/// > border-radius and margin values. If the ratio of border-radius/margin is 1 or more,
|
||||
/// > or margin is negative or zero, then the margin box corner radius is
|
||||
/// > max(border-radius + margin, 0). If the ratio of border-radius/margin is less than 1,
|
||||
/// > and margin is positive, then the margin box corner radius is
|
||||
/// > border-radius + margin * (1 + (ratio-1)^3).
|
||||
pub(super) fn compute_margin_box_radius(
|
||||
radius: wr::BorderRadius,
|
||||
layout_rect: LayoutSize,
|
||||
fragment: &BoxFragment,
|
||||
) -> wr::BorderRadius {
|
||||
let margin = fragment.style.get_margin();
|
||||
let adjust_radius = |radius: f32, margin: f32| -> f32 {
|
||||
if margin <= 0. || (radius / margin) >= 1. {
|
||||
(radius + margin).max(0.)
|
||||
} else {
|
||||
radius + (margin * (1. + (radius / margin - 1.).powf(3.)))
|
||||
}
|
||||
};
|
||||
let compute_margin_radius = |radius: LayoutSize,
|
||||
layout_rect: LayoutSize,
|
||||
margin: Size2D<LengthPercentageOrAuto, UnknownUnit>|
|
||||
-> LayoutSize {
|
||||
let width = margin
|
||||
.width
|
||||
.auto_is(LengthPercentage::zero)
|
||||
.resolve(Length::new(layout_rect.width));
|
||||
let height = margin
|
||||
.height
|
||||
.auto_is(LengthPercentage::zero)
|
||||
.resolve(Length::new(layout_rect.height));
|
||||
LayoutSize::new(
|
||||
adjust_radius(radius.width, width.px()),
|
||||
adjust_radius(radius.height, height.px()),
|
||||
)
|
||||
};
|
||||
wr::BorderRadius {
|
||||
top_left: compute_margin_radius(
|
||||
radius.top_left,
|
||||
layout_rect,
|
||||
Size2D::new(margin.margin_left.clone(), margin.margin_top.clone()),
|
||||
),
|
||||
top_right: compute_margin_radius(
|
||||
radius.top_right,
|
||||
layout_rect,
|
||||
Size2D::new(margin.margin_right.clone(), margin.margin_top.clone()),
|
||||
),
|
||||
bottom_left: compute_margin_radius(
|
||||
radius.bottom_left,
|
||||
layout_rect,
|
||||
Size2D::new(margin.margin_left.clone(), margin.margin_bottom.clone()),
|
||||
),
|
||||
bottom_right: compute_margin_radius(
|
||||
radius.bottom_right,
|
||||
layout_rect,
|
||||
Size2D::new(margin.margin_right.clone(), margin.margin_bottom.clone()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
|
|||
use style::computed_values::overflow_x::T as ComputedOverflow;
|
||||
use style::computed_values::position::T as ComputedPosition;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::basic_shape::ClipPath;
|
||||
use style::values::computed::{ClipRectOrAuto, Length};
|
||||
use style::values::generics::box_::Perspective;
|
||||
use style::values::generics::transform;
|
||||
|
@ -28,10 +29,11 @@ use webrender_traits::display_list::{ScrollSensitivity, ScrollTreeNodeId, Scroll
|
|||
use wr::units::{LayoutPixel, LayoutSize};
|
||||
use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds};
|
||||
|
||||
use super::clip_path::build_clip_path_clip_chain_if_necessary;
|
||||
use super::DisplayList;
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::display_list::conversions::{FilterToWebRender, ToWebRender};
|
||||
use crate::display_list::DisplayListBuilder;
|
||||
use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder};
|
||||
use crate::fragment_tree::{
|
||||
BoxFragment, ContainingBlockManager, Fragment, FragmentFlags, FragmentTree, PositioningFragment,
|
||||
};
|
||||
|
@ -488,7 +490,8 @@ impl StackingContext {
|
|||
if effects.filter.0.is_empty() &&
|
||||
effects.opacity == 1.0 &&
|
||||
effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
|
||||
!style.has_transform_or_perspective(FragmentFlags::empty())
|
||||
!style.has_transform_or_perspective(FragmentFlags::empty()) &&
|
||||
style.clone_clip_path() == ClipPath::None
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -654,8 +657,7 @@ impl StackingContext {
|
|||
}
|
||||
}
|
||||
|
||||
let mut fragment_builder =
|
||||
super::BuilderForBoxFragment::new(box_fragment, containing_block);
|
||||
let mut fragment_builder = BuilderForBoxFragment::new(box_fragment, containing_block);
|
||||
let painter = super::background::BackgroundPainter {
|
||||
style,
|
||||
painting_area_override: Some(painting_area),
|
||||
|
@ -1059,9 +1061,21 @@ impl BoxFragment {
|
|||
);
|
||||
}
|
||||
|
||||
// `clip-path` needs to be applied before filters and creates a stacking context, so it can be
|
||||
// applied directly to the stacking context itself.
|
||||
// before
|
||||
let stacking_context_clip_chain_id = build_clip_path_clip_chain_if_necessary(
|
||||
self.style.clone_clip_path(),
|
||||
display_list,
|
||||
&containing_block.scroll_node_id,
|
||||
&containing_block.clip_chain_id,
|
||||
BuilderForBoxFragment::new(self, &containing_block.rect),
|
||||
)
|
||||
.unwrap_or(containing_block.clip_chain_id);
|
||||
|
||||
let mut child_stacking_context = parent_stacking_context.create_descendant(
|
||||
containing_block.scroll_node_id.spatial_id,
|
||||
containing_block.clip_chain_id,
|
||||
stacking_context_clip_chain_id,
|
||||
self.style.clone(),
|
||||
self.base.flags,
|
||||
context_type,
|
||||
|
@ -1122,6 +1136,16 @@ impl BoxFragment {
|
|||
new_clip_chain_id = clip_chain_id;
|
||||
}
|
||||
|
||||
if let Some(clip_chain_id) = build_clip_path_clip_chain_if_necessary(
|
||||
self.style.clone_clip_path(),
|
||||
display_list,
|
||||
&new_scroll_node_id,
|
||||
&new_clip_chain_id,
|
||||
BuilderForBoxFragment::new(self, &containing_block.rect),
|
||||
) {
|
||||
new_clip_chain_id = clip_chain_id;
|
||||
}
|
||||
|
||||
let establishes_containing_block_for_all_descendants = self
|
||||
.style
|
||||
.establishes_containing_block_for_all_descendants(self.base.flags);
|
||||
|
|
|
@ -13,6 +13,7 @@ use style::properties::longhands::backface_visibility::computed_value::T as Back
|
|||
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
|
||||
use style::properties::longhands::column_span::computed_value::T as ColumnSpan;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::basic_shape::ClipPath;
|
||||
use style::values::computed::image::Image as ComputedImageLayer;
|
||||
use style::values::computed::{Length, LengthPercentage, NonNegativeLengthPercentage, Size};
|
||||
use style::values::generics::box_::Perspective;
|
||||
|
@ -608,6 +609,10 @@ impl ComputedValuesExt for ComputedValues {
|
|||
return true;
|
||||
}
|
||||
|
||||
if self.get_svg().clip_path != ClipPath::None {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Statically positioned fragments don't establish stacking contexts if the previous
|
||||
// conditions are not fulfilled. Furthermore, z-index doesn't apply to statically
|
||||
// positioned fragments (except for flex items, see below).
|
||||
|
|
4
tests/wpt/include.ini
vendored
4
tests/wpt/include.ini
vendored
|
@ -61,6 +61,10 @@ skip: true
|
|||
[css-masking]
|
||||
[clip]
|
||||
skip: false
|
||||
[clip-path]
|
||||
skip: false
|
||||
[animations]
|
||||
skip: true
|
||||
[css-multicol]
|
||||
skip: true
|
||||
[css-nesting]
|
||||
|
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-blending-offset.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-blending-offset.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-blending-offset.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-borderBox-1a.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-borderBox-1a.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-borderBox-1a.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-borderBox-1c.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-borderBox-1c.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-borderBox-1c.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-circle-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-circle-002.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-circle-003.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-circle-004.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-circle-005.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-circle-006.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-circle-007.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-circle-008.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-circle-closest-corner.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-circle-closest-corner.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-circle-closest-corner.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-circle-farthest-corner.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-circle-farthest-corner.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-circle-farthest-corner.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-contentBox-1a.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-contentBox-1a.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-contentBox-1a.html]
|
||||
expected: FAIL
|
|
@ -1,2 +1,2 @@
|
|||
[clip-path-descendant-text-mutated-001.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-ellipse-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-ellipse-002.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-ellipse-003.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-ellipse-004.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-ellipse-005.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-ellipse-006.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-ellipse-007.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-ellipse-008.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-ellipse-closest-farthest-corner.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-ellipse-closest-farthest-corner.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-ellipse-closest-farthest-corner.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-fillBox-1a.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-fillBox-1a.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-fillBox-1a.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-filter-order.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clip-path-fixed-nested.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-fixed-scroll.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-fixed-scroll.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-fixed-scroll.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-foreignobject-non-zero-xy.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-foreignobject-non-zero-xy.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-foreignobject-non-zero-xy.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-geometryBox-2.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-geometryBox-2.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-geometryBox-2.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-inline-007.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-inline-007.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-inline-007.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-mix-blend-mode-1.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-mix-blend-mode-1.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-mix-blend-mode-1.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-paddingBox-1a.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-paddingBox-1a.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-paddingBox-1a.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-paddingBox-1c.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-paddingBox-1c.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-paddingBox-1c.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-path-003.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-path-003.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-path-003.html]
|
||||
expected: FAIL
|
3
tests/wpt/meta/css/css-masking/clip-path/clip-path-path-with-zoom-hittest.html.ini
vendored
Normal file
3
tests/wpt/meta/css/css-masking/clip-path/clip-path-path-with-zoom-hittest.html.ini
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[clip-path-path-with-zoom-hittest.html]
|
||||
[clip-path: path() hit-test takes zoom into account]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-path-with-zoom.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-path-with-zoom.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-path-with-zoom.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-round-zero-size.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-round-zero-size.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-round-zero-size.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-scaled-video.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-scaled-video.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-scaled-video.html]
|
||||
expected: FAIL
|
|
@ -1,2 +1,2 @@
|
|||
[clip-path-scroll.html]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-shape-005.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-shape-005.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-shape-005.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-shape-006.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-shape-006.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-shape-006.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-shape-foreignobject-non-zero-xy.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-shape-foreignobject-non-zero-xy.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-shape-foreignobject-non-zero-xy.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-strokeBox-1a.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-strokeBox-1a.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-strokeBox-1a.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-svg-text-font-loading.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-svg-text-font-loading.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-svg-text-font-loading.html]
|
||||
expected: TIMEOUT
|
|
@ -0,0 +1,2 @@
|
|||
[clip-path-url-reference-svg-foreignobject-zoomed.html]
|
||||
expected: FAIL
|
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-viewBox-1c.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/clip-path-viewBox-1c.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[clip-path-viewBox-1c.html]
|
||||
expected: FAIL
|
|
@ -1,10 +0,0 @@
|
|||
[interpolation.html]
|
||||
[Test circle with negative easing on clip-path]
|
||||
expected: FAIL
|
||||
|
||||
[Test ellipse with negative easing on clip-path]
|
||||
expected: FAIL
|
||||
|
||||
[Test inset with negative easing on clip-path]
|
||||
expected: FAIL
|
||||
|
2
tests/wpt/meta/css/css-masking/clip-path/svg-clip-path-fixed-values.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-masking/clip-path/svg-clip-path-fixed-values.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[svg-clip-path-fixed-values.html]
|
||||
expected: FAIL
|
|
@ -2,9 +2,6 @@
|
|||
[Property background-blend-mode does not support quirky length]
|
||||
expected: FAIL
|
||||
|
||||
[Property clip-path does not support quirky length]
|
||||
expected: FAIL
|
||||
|
||||
[Property column-span does not support quirky length]
|
||||
expected: FAIL
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue