mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Implement outlines in layout-2020
This only covers the CSS2 properties: 'outline-width', 'outline-style', 'outline-color', and the shorthand 'outline'. CSS UI 3 introduced 'outline-offset', which is left for a follow-up. 'outline-color: invert' isn't included either, but note CSS UI says that conformant UAs may ignore the 'invert' value on platforms that do not support color inversion of the pixels on the screen. Tests that are now passing: - /_mozilla/css/input_insertion_point_empty_a.html - /_mozilla/css/outlines_simple_a.html - /_mozilla/css/stacking_context_overflow_relative_outline_a.html - /_mozilla/mozilla/getPropertyPriority.html - /css/CSS2/ui/outline-color-001.xht - /css/CSS2/ui/outline-color-002.xht - /css/CSS2/ui/outline-color-007.xht - /css/CSS2/ui/outline-color-008.xht - /css/CSS2/ui/outline-color-013.xht - /css/CSS2/ui/outline-color-018.xht - /css/CSS2/ui/outline-color-023.xht - /css/CSS2/ui/outline-color-024.xht - /css/CSS2/ui/outline-color-025.xht - /css/CSS2/ui/outline-color-031.xht - /css/CSS2/ui/outline-color-036.xht - /css/CSS2/ui/outline-color-041.xht - /css/CSS2/ui/outline-color-046.xht - /css/CSS2/ui/outline-color-047.xht - /css/CSS2/ui/outline-color-048.xht - /css/CSS2/ui/outline-color-049.xht - /css/CSS2/ui/outline-color-050.xht - /css/CSS2/ui/outline-color-051.xht - /css/CSS2/ui/outline-color-052.xht - /css/CSS2/ui/outline-color-053.xht - /css/CSS2/ui/outline-color-054.xht - /css/CSS2/ui/outline-color-058.xht - /css/CSS2/ui/outline-color-059.xht - /css/CSS2/ui/outline-color-061.xht - /css/CSS2/ui/outline-color-062.xht - /css/CSS2/ui/outline-color-069.xht - /css/CSS2/ui/outline-color-070.xht - /css/CSS2/ui/outline-color-071.xht - /css/CSS2/ui/outline-color-072.xht - /css/CSS2/ui/outline-color-073.xht - /css/CSS2/ui/outline-color-074.xht - /css/CSS2/ui/outline-color-075.xht - /css/CSS2/ui/outline-color-079.xht - /css/CSS2/ui/outline-color-081.xht - /css/CSS2/ui/outline-color-082.xht - /css/CSS2/ui/outline-color-089.xht - /css/CSS2/ui/outline-color-090.xht - /css/CSS2/ui/outline-color-091.xht - /css/CSS2/ui/outline-color-092.xht - /css/CSS2/ui/outline-color-093.xht - /css/CSS2/ui/outline-color-094.xht - /css/CSS2/ui/outline-color-095.xht - /css/CSS2/ui/outline-color-099.xht - /css/CSS2/ui/outline-color-101.xht - /css/CSS2/ui/outline-color-102.xht - /css/CSS2/ui/outline-color-109.xht - /css/CSS2/ui/outline-color-110.xht - /css/CSS2/ui/outline-color-111.xht - /css/CSS2/ui/outline-color-112.xht - /css/CSS2/ui/outline-color-113.xht - /css/CSS2/ui/outline-color-114.xht - /css/CSS2/ui/outline-color-115.xht - /css/CSS2/ui/outline-color-119.xht - /css/CSS2/ui/outline-color-121.xht - /css/CSS2/ui/outline-color-122.xht - /css/CSS2/ui/outline-color-130.xht - /css/css-ui/outline-001.html - /css/css-ui/outline-002.html - /css/css-ui/outline-004.html - /css/css-ui/outline-007.html - /css/css-ui/outline-008.html - /css/css-ui/outline-016.html - /css/css-ui/outline-018.html - /css/css-ui/outline-021.html - /css/css-ui/outline-022.html - /css/css-ui/outline-color-001.html - /css/css-ui/outline-style-011.html - /css/css-ui/outline-style-012.html - /css/css-ui/outline-style-013.html - /css/css-ui/outline-style-014.html - /css/css-ui/parsing/outline-color-computed.html - /css/css-ui/parsing/outline-color-valid-mandatory.html - /css/css-ui/parsing/outline-shorthand.html - /css/css-ui/parsing/outline-style-computed.html - /css/css-ui/parsing/outline-style-valid.html - /css/css-ui/parsing/outline-width-valid.html - /css/css-ui/translucent-outline.html Also improvements in: - /_mozilla/mozilla/calc.html - /css/css-ui/animation/outline-color-interpolation.html - /css/css-ui/animation/outline-width-interpolation.html - /css/css-ui/inheritance.html - /css/css-ui/outline-017.html - /css/css-ui/parsing/outline-valid-mandatory.html - /css/css-ui/parsing/outline-width-computed.html - /css/cssom/cssom-setProperty-shorthand.html - /css/cssom/getComputedStyle-resolved-colors.html - /css/cssom/serialize-values.html - /css/cssom/shorthand-values.html New failures: - /css/CSS2/ui/outline-applies-to-005.xht - /css/CSS2/ui/outline-applies-to-006.xht - /css/CSS2/ui/outline-color-applies-to-005.xht - /css/CSS2/ui/outline-color-applies-to-006.xht - /css/CSS2/ui/outline-style-applies-to-005.xht - /css/CSS2/ui/outline-style-applies-to-006.xht - /css/CSS2/ui/outline-width-applies-to-005.xht - /css/CSS2/ui/outline-width-applies-to-006.xht Al of these fail because tables are not implemented yet. - /css/css-ui/outline-offset.html Fails because outline-offset is not implemented yet. - /css/css-ui/outline-with-padding-001.html Fails because the outline doesn't include overflowing contents. I don't think this is required by the spec, Firefox fails too.
This commit is contained in:
parent
c92ea9774f
commit
d67bf49bd9
106 changed files with 121 additions and 1020 deletions
|
@ -4,6 +4,7 @@
|
|||
|
||||
use crate::context::LayoutContext;
|
||||
use crate::display_list::conversions::ToWebRender;
|
||||
use crate::display_list::stacking_context::StackingContextSection;
|
||||
use crate::fragments::{BoxFragment, Fragment, Tag, TextFragment};
|
||||
use crate::geom::{PhysicalPoint, PhysicalRect};
|
||||
use crate::replaced::IntrinsicSizes;
|
||||
|
@ -21,7 +22,7 @@ use style::computed_values::text_decoration_style::T as ComputedTextDecorationSt
|
|||
use style::dom::OpaqueNode;
|
||||
use style::properties::longhands::visibility::computed_value::T as Visibility;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::{BorderStyle, Length, LengthPercentage};
|
||||
use style::values::computed::{BorderStyle, Color, Length, LengthPercentage, OutlineStyle};
|
||||
use style::values::specified::text::TextDecorationLine;
|
||||
use style::values::specified::ui::CursorKind;
|
||||
use style_traits::CSSPixel;
|
||||
|
@ -115,11 +116,12 @@ impl Fragment {
|
|||
&self,
|
||||
builder: &mut DisplayListBuilder,
|
||||
containing_block: &PhysicalRect<Length>,
|
||||
section: StackingContextSection,
|
||||
) {
|
||||
match self {
|
||||
Fragment::Box(b) => match b.style.get_inherited_box().visibility {
|
||||
Visibility::Visible => {
|
||||
BuilderForBoxFragment::new(b, containing_block).build(builder)
|
||||
BuilderForBoxFragment::new(b, containing_block).build(builder, section)
|
||||
},
|
||||
Visibility::Hidden => (),
|
||||
Visibility::Collapse => (),
|
||||
|
@ -418,10 +420,14 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
fn build(&mut self, builder: &mut DisplayListBuilder) {
|
||||
self.build_hit_test(builder);
|
||||
self.build_background(builder);
|
||||
self.build_border(builder);
|
||||
fn build(&mut self, builder: &mut DisplayListBuilder, section: StackingContextSection) {
|
||||
if section == StackingContextSection::Outline {
|
||||
self.build_outline(builder);
|
||||
} else {
|
||||
self.build_hit_test(builder);
|
||||
self.build_background(builder);
|
||||
self.build_border(builder);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_hit_test(&self, builder: &mut DisplayListBuilder) {
|
||||
|
@ -555,18 +561,8 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn build_border(&mut self, builder: &mut DisplayListBuilder) {
|
||||
let b = self.fragment.style.get_border();
|
||||
let widths = SideOffsets2D::new(
|
||||
b.border_top_width.px(),
|
||||
b.border_right_width.px(),
|
||||
b.border_bottom_width.px(),
|
||||
b.border_left_width.px(),
|
||||
);
|
||||
if widths == SideOffsets2D::zero() {
|
||||
return;
|
||||
}
|
||||
let side = |style, color| wr::BorderSide {
|
||||
fn build_border_side(&mut self, style: BorderStyle, color: Color) -> wr::BorderSide {
|
||||
wr::BorderSide {
|
||||
color: rgba(self.fragment.style.resolve_color(color)),
|
||||
style: match style {
|
||||
BorderStyle::None => wr::BorderStyle::None,
|
||||
|
@ -580,13 +576,26 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
BorderStyle::Inset => wr::BorderStyle::Inset,
|
||||
BorderStyle::Outset => wr::BorderStyle::Outset,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn build_border(&mut self, builder: &mut DisplayListBuilder) {
|
||||
let border = self.fragment.style.get_border();
|
||||
let widths = SideOffsets2D::new(
|
||||
border.border_top_width.px(),
|
||||
border.border_right_width.px(),
|
||||
border.border_bottom_width.px(),
|
||||
border.border_left_width.px(),
|
||||
);
|
||||
if widths == SideOffsets2D::zero() {
|
||||
return;
|
||||
}
|
||||
let common = builder.common_properties(self.border_rect, &self.fragment.style);
|
||||
let details = wr::BorderDetails::Normal(wr::NormalBorder {
|
||||
top: side(b.border_top_style, b.border_top_color),
|
||||
right: side(b.border_right_style, b.border_right_color),
|
||||
bottom: side(b.border_bottom_style, b.border_bottom_color),
|
||||
left: side(b.border_left_style, b.border_left_color),
|
||||
top: self.build_border_side(border.border_top_style, border.border_top_color),
|
||||
right: self.build_border_side(border.border_right_style, border.border_right_color),
|
||||
bottom: self.build_border_side(border.border_bottom_style, border.border_bottom_color),
|
||||
left: self.build_border_side(border.border_left_style, border.border_left_color),
|
||||
radius: self.border_radius,
|
||||
do_aa: true,
|
||||
});
|
||||
|
@ -594,6 +603,35 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
.wr
|
||||
.push_border(&common, self.border_rect, widths, details)
|
||||
}
|
||||
|
||||
fn build_outline(&mut self, builder: &mut DisplayListBuilder) {
|
||||
let outline = self.fragment.style.get_outline();
|
||||
let width = outline.outline_width.px();
|
||||
if width == 0.0 {
|
||||
return;
|
||||
}
|
||||
let outline_rect = self.border_rect.inflate(width, width);
|
||||
let common = builder.common_properties(outline_rect, &self.fragment.style);
|
||||
let widths = SideOffsets2D::new_all_same(width);
|
||||
let style = match outline.outline_style {
|
||||
// TODO: treating 'auto' as 'solid' is allowed by the spec,
|
||||
// but we should do something better.
|
||||
OutlineStyle::Auto => BorderStyle::Solid,
|
||||
OutlineStyle::BorderStyle(s) => s,
|
||||
};
|
||||
let side = self.build_border_side(style, outline.outline_color);
|
||||
let details = wr::BorderDetails::Normal(wr::NormalBorder {
|
||||
top: side,
|
||||
right: side,
|
||||
bottom: side,
|
||||
left: side,
|
||||
radius: expand_radii(self.border_radius, width),
|
||||
do_aa: true,
|
||||
});
|
||||
builder
|
||||
.wr
|
||||
.push_border(&common, outline_rect, widths, details)
|
||||
}
|
||||
}
|
||||
|
||||
fn rgba(rgba: cssparser::RGBA) -> wr::ColorF {
|
||||
|
@ -699,6 +737,27 @@ fn inner_radii(mut radii: wr::BorderRadius, offsets: units::LayoutSideOffsets) -
|
|||
radii
|
||||
}
|
||||
|
||||
fn expand_radii(mut radii: wr::BorderRadius, increment: f32) -> wr::BorderRadius {
|
||||
assert!(increment > 0.0, "increment must be positive");
|
||||
let expand = |radius: &mut f32| {
|
||||
// Expand the radius by the specified amount, but keeping sharp corners.
|
||||
// TODO: this behavior is not continuous, it's being discussed in the CSSWG:
|
||||
// https://github.com/w3c/csswg-drafts/issues/7103
|
||||
if *radius > 0.0 {
|
||||
*radius += increment;
|
||||
}
|
||||
};
|
||||
expand(&mut radii.top_left.width);
|
||||
expand(&mut radii.top_left.height);
|
||||
expand(&mut radii.top_right.width);
|
||||
expand(&mut radii.top_right.height);
|
||||
expand(&mut radii.bottom_right.width);
|
||||
expand(&mut radii.bottom_right.height);
|
||||
expand(&mut radii.bottom_left.width);
|
||||
expand(&mut radii.bottom_left.height);
|
||||
radii
|
||||
}
|
||||
|
||||
fn clip_for_radii(
|
||||
radii: wr::BorderRadius,
|
||||
rect: units::LayoutRect,
|
||||
|
|
|
@ -99,6 +99,7 @@ pub(crate) enum StackingContextSection {
|
|||
BackgroundsAndBorders,
|
||||
BlockBackgroundsAndBorders,
|
||||
Content,
|
||||
Outline,
|
||||
}
|
||||
|
||||
pub(crate) struct StackingContextFragment {
|
||||
|
@ -113,7 +114,7 @@ impl StackingContextFragment {
|
|||
builder.current_space_and_clip = self.space_and_clip;
|
||||
self.fragment
|
||||
.borrow()
|
||||
.build_display_list(builder, &self.containing_block);
|
||||
.build_display_list(builder, &self.containing_block, self.section);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,6 +425,13 @@ impl StackingContext {
|
|||
child_context.build_display_list(builder);
|
||||
}
|
||||
|
||||
// Step 10: Outline
|
||||
while child_fragments.peek().map_or(false, |child| {
|
||||
child.section == StackingContextSection::Outline
|
||||
}) {
|
||||
child_fragments.next().unwrap().build_display_list(builder);
|
||||
}
|
||||
|
||||
if pushed_context {
|
||||
builder.wr.pop_stacking_context();
|
||||
}
|
||||
|
@ -671,6 +679,14 @@ impl BoxFragment {
|
|||
containing_block: containing_block_info.rect,
|
||||
fragment: fragment.clone(),
|
||||
});
|
||||
if self.style.get_outline().outline_width.px() > 0.0 {
|
||||
stacking_context.fragments.push(StackingContextFragment {
|
||||
space_and_clip: builder.current_space_and_clip,
|
||||
section: StackingContextSection::Outline,
|
||||
containing_block: containing_block_info.rect,
|
||||
fragment: fragment.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
// We want to build the scroll frame after the background and border, because
|
||||
// they shouldn't scroll with the rest of the box content.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue