diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2019-12-01 09:53:45 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-01 09:53:45 -0500 |
commit | 40fd7910a54d8e0180f605d4b4e8f80931383e44 (patch) | |
tree | b5cb9a63d944ea2bf5c2f1ce35861e2f97f986df | |
parent | 1dd6ad7063a5bf71a37894654a7561338c6a39bc (diff) | |
parent | 5a360ac63d374023884289ca86b7c2390394c533 (diff) | |
download | servo-40fd7910a54d8e0180f605d4b4e8f80931383e44.tar.gz servo-40fd7910a54d8e0180f605d4b4e8f80931383e44.zip |
Auto merge of #24928 - servo:2020-images, r=SimonSapin
Start supporting images in layout 2020
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | components/layout_2020/Cargo.toml | 1 | ||||
-rw-r--r-- | components/layout_2020/display_list.rs | 28 | ||||
-rw-r--r-- | components/layout_2020/dom_traversal.rs | 33 | ||||
-rw-r--r-- | components/layout_2020/flow/construct.rs | 14 | ||||
-rw-r--r-- | components/layout_2020/flow/inline.rs | 14 | ||||
-rw-r--r-- | components/layout_2020/flow/mod.rs | 142 | ||||
-rw-r--r-- | components/layout_2020/flow/root.rs | 12 | ||||
-rw-r--r-- | components/layout_2020/formatting_contexts.rs | 2 | ||||
-rw-r--r-- | components/layout_2020/fragments.rs | 9 | ||||
-rw-r--r-- | components/layout_2020/replaced.rs | 71 | ||||
-rw-r--r-- | components/style/properties/longhands/inherited_box.mako.rs | 3 | ||||
-rw-r--r-- | tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-007.xht.ini | 2 |
13 files changed, 267 insertions, 65 deletions
diff --git a/Cargo.lock b/Cargo.lock index 531fa815666..34f3cf8fe39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2723,6 +2723,7 @@ dependencies = [ "ipc-channel", "libc", "msg", + "net_traits", "range", "rayon", "rayon_croissant", diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml index 7bfe7cbea16..5a28da8d9ee 100644 --- a/components/layout_2020/Cargo.toml +++ b/components/layout_2020/Cargo.toml @@ -22,6 +22,7 @@ gfx_traits = {path = "../gfx_traits"} ipc-channel = "0.12" libc = "0.2" msg = {path = "../msg"} +net_traits = {path = "../net_traits"} range = {path = "../range"} rayon = "1" rayon_croissant = "0.1.1" diff --git a/components/layout_2020/display_list.rs b/components/layout_2020/display_list.rs index 5dc5ecb96ad..16592a8a5d0 100644 --- a/components/layout_2020/display_list.rs +++ b/components/layout_2020/display_list.rs @@ -78,6 +78,34 @@ impl Fragment { .wr .push_text(&common, rect.into(), &glyphs, t.font_key, rgba(color), None); }, + Fragment::Image(i) => { + use style::computed_values::image_rendering::T as ImageRendering; + is_contentful.0 = true; + let rect = i + .content_rect + .to_physical(i.style.writing_mode(), containing_block) + .translate(&containing_block.top_left); + let common = CommonItemProperties { + clip_rect: rect.clone().into(), + clip_id: wr::ClipId::root(builder.pipeline_id), + spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id), + hit_info: None, + // TODO(gw): Make use of the WR backface visibility functionality. + flags: PrimitiveFlags::default(), + }; + builder.wr.push_image( + &common, + rect.into(), + match i.style.get_inherited_box().image_rendering { + ImageRendering::Auto => wr::ImageRendering::Auto, + ImageRendering::CrispEdges => wr::ImageRendering::CrispEdges, + ImageRendering::Pixelated => wr::ImageRendering::Pixelated, + }, + wr::AlphaType::PremultipliedAlpha, + i.image_key, + wr::ColorF::WHITE, + ); + }, } } } diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 732dc1afdc9..ee368566bef 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -3,18 +3,22 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::element_data::{LayoutBox, LayoutDataForElement}; +use crate::geom::physical::Vec2; use crate::replaced::ReplacedContent; use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOutside}; use crate::wrapper::GetRawData; use atomic_refcell::{AtomicRefCell, AtomicRefMut}; +use net_traits::image::base::{Image as NetImage, ImageMetadata}; use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; use servo_arc::Arc as ServoArc; use std::convert::TryInto; use std::marker::PhantomData as marker; +use std::sync::Arc; use style::context::SharedStyleContext; use style::dom::TNode; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; +use style::values::computed::Length; #[derive(Clone, Copy)] pub enum WhichPseudoElement { @@ -90,11 +94,12 @@ fn traverse_element<'dom, Node>( ) where Node: NodeExt<'dom>, { + let replaced = ReplacedContent::for_element(element); let style = element.style(context); match Display::from(style.get_box().display) { Display::None => element.unset_boxes_in_subtree(), Display::Contents => { - if ReplacedContent::for_element(element, context).is_some() { + if replaced.is_some() { // `display: content` on a replaced element computes to `display: none` // <https://drafts.csswg.org/css-display-3/#valdef-display-contents> element.unset_boxes_in_subtree() @@ -107,10 +112,7 @@ fn traverse_element<'dom, Node>( handler.handle_element( &style, display, - match ReplacedContent::for_element(element, context) { - Some(replaced) => Contents::Replaced(replaced), - None => Contents::OfElement(element), - }, + replaced.map_or(Contents::OfElement(element), Contents::Replaced), element.element_box_slot(), ); }, @@ -287,6 +289,7 @@ impl Drop for BoxSlot<'_> { pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync { fn is_element(self) -> bool; fn as_text(self) -> Option<String>; + fn as_image(self) -> Option<(Option<Arc<NetImage>>, Vec2<Length>)>; fn first_child(self) -> Option<Self>; fn next_sibling(self) -> Option<Self>; fn parent_node(self) -> Option<Self>; @@ -315,6 +318,26 @@ where } } + fn as_image(self) -> Option<(Option<Arc<NetImage>>, Vec2<Length>)> { + let node = self.to_threadsafe(); + let (resource, metadata) = node.image_data()?; + let (width, height) = resource + .as_ref() + .map(|image| (image.width, image.height)) + .or_else(|| metadata.map(|metadata| (metadata.width, metadata.height))) + .unwrap_or((0, 0)); + let (mut width, mut height) = (width as f32, height as f32); + if let Some(density) = node.image_density().filter(|density| *density != 1.) { + width = (width as f64 / density) as f32; + height = (height as f64 / density) as f32; + } + let size = Vec2 { + x: Length::new(width), + y: Length::new(height), + }; + Some((resource, size)) + } + fn first_child(self) -> Option<Self> { TNode::first_child(&self) } diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 57d7bf9dd31..e7599bc843a 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -325,10 +325,14 @@ where contents: Contents<Node>, ) -> Arc<InlineLevelBox> { let box_ = match contents.try_into() { - Err(replaced) => Arc::new(InlineLevelBox::Atomic { - style: style.clone(), - contents: replaced, - }), + Err(replaced) => Arc::new(InlineLevelBox::Atomic( + IndependentFormattingContext::construct( + self.context, + style.clone(), + display_inside, + <Contents<Node>>::Replaced(replaced), + ), + )), Ok(non_replaced) => match display_inside { DisplayInside::Flow | // TODO: Properly implement display: inline-block. @@ -449,7 +453,7 @@ where let box_ = Arc::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox( AbsolutelyPositionedBox { contents: IndependentFormattingContext::construct( - unimplemented!(), + self.context, style, display_inside, contents, diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 5a48ac9a2ee..f944fdbedf2 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -5,12 +5,12 @@ use crate::context::LayoutContext; use crate::flow::float::FloatBox; use crate::flow::FlowLayout; +use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::{ AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment, TextFragment, }; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; -use crate::replaced::ReplacedContent; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::{relative_adjustement, ContainingBlock}; use servo_arc::Arc; @@ -29,11 +29,7 @@ pub(crate) enum InlineLevelBox { TextRun(TextRun), OutOfFlowAbsolutelyPositionedBox(AbsolutelyPositionedBox), OutOfFlowFloatBox(FloatBox), - Atomic { - style: Arc<ComputedValues>, - // FIXME: this should be IndependentFormattingContext: - contents: ReplacedContent, - }, + Atomic(IndependentFormattingContext), } #[derive(Debug)] @@ -112,9 +108,9 @@ impl InlineFormattingContext { ifc.partial_inline_boxes_stack.push(partial) }, InlineLevelBox::TextRun(run) => run.layout(layout_context, &mut ifc), - InlineLevelBox::Atomic { style: _, contents } => { - // FIXME - match *contents {} + InlineLevelBox::Atomic(_independent) => { + // TODO + continue; }, InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { let initial_start_corner = diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 9281c135760..4b887abde35 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -15,6 +15,7 @@ use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{ adjust_static_positions, AbsolutelyPositionedBox, AbsolutelyPositionedFragment, }; +use crate::replaced::ReplacedContent; use crate::style_ext::{ComputedValuesExt, Position}; use crate::{relative_adjustement, ContainingBlock}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; @@ -295,10 +296,12 @@ impl BlockLevelBox { )) }, BlockLevelBox::Independent(contents) => match contents.as_replaced() { - Ok(replaced) => { - // FIXME - match *replaced {} - }, + Ok(replaced) => Fragment::Box(layout_in_flow_replaced_block_level( + layout_context, + containing_block, + &contents.style, + replaced, + )), Err(non_replaced) => Fragment::Box(layout_in_flow_non_replaced_block_level( layout_context, containing_block, @@ -360,27 +363,15 @@ fn layout_in_flow_non_replaced_block_level<'a>( let box_size = style.box_size(); let inline_size = box_size.inline.percentage_relative_to(cbis); if let LengthOrAuto::LengthPercentage(is) = inline_size { - let inline_margins = cbis - is - pb.inline_sum(); - match ( - &mut computed_margin.inline_start, - &mut computed_margin.inline_end, - ) { - (s @ &mut LengthOrAuto::Auto, e @ &mut LengthOrAuto::Auto) => { - *s = LengthOrAuto::LengthPercentage(inline_margins / 2.); - *e = LengthOrAuto::LengthPercentage(inline_margins / 2.); - }, - (s @ &mut LengthOrAuto::Auto, _) => { - *s = LengthOrAuto::LengthPercentage(inline_margins); - }, - (_, e @ &mut LengthOrAuto::Auto) => { - *e = LengthOrAuto::LengthPercentage(inline_margins); - }, - (_, e @ _) => { - // Either the inline-end margin is auto, - // or we’re over-constrained and we do as if it were. - *e = LengthOrAuto::LengthPercentage(inline_margins); - }, - } + let (margin_inline_start, margin_inline_end) = solve_inline_margins_for_in_flow_block_level( + containing_block, + pb.inline_sum(), + computed_margin.inline_start, + computed_margin.inline_end, + is, + ); + computed_margin.inline_start = LengthOrAuto::LengthPercentage(margin_inline_start); + computed_margin.inline_end = LengthOrAuto::LengthPercentage(margin_inline_end); } let margin = computed_margin.auto_is(Length::zero); let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); @@ -479,3 +470,104 @@ fn layout_in_flow_non_replaced_block_level<'a>( block_margins_collapsed_with_children, } } + +/// https://drafts.csswg.org/css2/visudet.html#block-replaced-width +/// https://drafts.csswg.org/css2/visudet.html#inline-replaced-width +/// https://drafts.csswg.org/css2/visudet.html#inline-replaced-height +fn layout_in_flow_replaced_block_level<'a>( + layout_context: &LayoutContext, + containing_block: &ContainingBlock, + style: &Arc<ComputedValues>, + replaced: &ReplacedContent, +) -> BoxFragment { + let cbis = containing_block.inline_size; + let padding = style.padding().percentages_relative_to(cbis); + let border = style.border_width(); + let computed_margin = style.margin().percentages_relative_to(cbis); + let pb = &padding + &border; + let mode = style.writing_mode(); + // FIXME(nox): We shouldn't pretend we always have a fully known intrinsic size. + let intrinsic_size = replaced.intrinsic_size.size_to_flow_relative(mode); + // FIXME(nox): This can divide by zero. + let intrinsic_ratio = intrinsic_size.inline.px() / intrinsic_size.block.px(); + let box_size = style.box_size(); + let inline_size = box_size.inline.percentage_relative_to(cbis); + let block_size = box_size + .block + .maybe_percentage_relative_to(containing_block.block_size.non_auto()); + let (inline_size, block_size) = match (inline_size, block_size) { + (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::LengthPercentage(block)) => { + (inline, block) + }, + (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::Auto) => { + (inline, inline / intrinsic_ratio) + }, + (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(block)) => { + (block * intrinsic_ratio, block) + }, + (LengthOrAuto::Auto, LengthOrAuto::Auto) => (intrinsic_size.inline, intrinsic_size.block), + }; + let (margin_inline_start, margin_inline_end) = solve_inline_margins_for_in_flow_block_level( + containing_block, + pb.inline_sum(), + computed_margin.inline_start, + computed_margin.inline_end, + inline_size, + ); + let margin = Sides { + inline_start: margin_inline_start, + inline_end: margin_inline_end, + block_start: computed_margin.block_start.auto_is(Length::zero), + block_end: computed_margin.block_end.auto_is(Length::zero), + }; + let containing_block_for_children = ContainingBlock { + inline_size, + block_size: LengthOrAuto::LengthPercentage(block_size), + mode, + }; + // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows + assert_eq!( + containing_block.mode, containing_block_for_children.mode, + "Mixed writing modes are not supported yet" + ); + let independent_layout = replaced.layout(layout_context, style, &containing_block_for_children); + let relative_adjustement = relative_adjustement( + style, + inline_size, + LengthOrAuto::LengthPercentage(block_size), + ); + let content_rect = Rect { + start_corner: Vec2 { + block: pb.block_start + relative_adjustement.block, + inline: pb.inline_start + relative_adjustement.inline + margin.inline_start, + }, + size: Vec2 { + block: block_size, + inline: inline_size, + }, + }; + BoxFragment { + style: style.clone(), + children: independent_layout.fragments, + content_rect, + padding, + border, + block_margins_collapsed_with_children: CollapsedBlockMargins::from_margin(&margin), + margin, + } +} + +fn solve_inline_margins_for_in_flow_block_level( + containing_block: &ContainingBlock, + padding_border_inline_sum: Length, + computed_margin_inline_start: LengthOrAuto, + computed_margin_inline_end: LengthOrAuto, + inline_size: Length, +) -> (Length, Length) { + let inline_margins = containing_block.inline_size - padding_border_inline_sum - inline_size; + match (computed_margin_inline_start, computed_margin_inline_end) { + (LengthOrAuto::Auto, LengthOrAuto::Auto) => (inline_margins / 2., inline_margins / 2.), + (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => (inline_margins - end, end), + (LengthOrAuto::LengthPercentage(start), _) => (start, inline_margins - start), + } +} diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 8df332b90d6..f4cb0abafb9 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -48,7 +48,7 @@ fn construct_for_root_element<'dom>( root_element: impl NodeExt<'dom>, ) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) { let style = root_element.style(context); - let replaced = ReplacedContent::for_element(root_element, context); + let replaced = ReplacedContent::for_element(root_element); let box_style = style.get_box(); let display_inside = match Display::from(box_style.display) { @@ -63,21 +63,13 @@ fn construct_for_root_element<'dom>( Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside, }; - if let Some(replaced) = replaced { - let _box = match replaced {}; - #[allow(unreachable_code)] - { - return (ContainsFloats::No, vec![Arc::new(_box)]); - } - } - let position = box_style.position; let float = box_style.float; let contents = IndependentFormattingContext::construct( context, style, display_inside, - Contents::OfElement(root_element), + replaced.map_or(Contents::OfElement(root_element), Contents::Replaced), ); if position.is_absolutely_positioned() { ( diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 96294947720..06e863da1b1 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -83,7 +83,7 @@ impl IndependentFormattingContext { absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>, ) -> IndependentLayout { match self.as_replaced() { - Ok(replaced) => match *replaced {}, + Ok(replaced) => replaced.layout(layout_context, &self.style, containing_block), Err(ifc) => ifc.layout( layout_context, containing_block, diff --git a/components/layout_2020/fragments.rs b/components/layout_2020/fragments.rs index a0ea097abb6..e0c335eb3c9 100644 --- a/components/layout_2020/fragments.rs +++ b/components/layout_2020/fragments.rs @@ -10,12 +10,13 @@ use std::sync::Arc; use style::properties::ComputedValues; use style::values::computed::Length; use style::Zero; -use webrender_api::FontInstanceKey; +use webrender_api::{FontInstanceKey, ImageKey}; pub(crate) enum Fragment { Box(BoxFragment), Anonymous(AnonymousFragment), Text(TextFragment), + Image(ImageFragment), } pub(crate) struct BoxFragment { @@ -61,6 +62,12 @@ pub(crate) struct TextFragment { pub glyphs: Vec<Arc<GlyphStore>>, } +pub(crate) struct ImageFragment { + pub style: ServoArc<ComputedValues>, + pub content_rect: Rect<Length>, + pub image_key: ImageKey, +} + impl AnonymousFragment { pub fn no_op(mode: (WritingMode, Direction)) -> Self { Self { diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 04defc26b02..bb003f613fa 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -2,20 +2,77 @@ * 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 crate::context::LayoutContext; use crate::dom_traversal::NodeExt; +use crate::formatting_contexts::IndependentLayout; +use crate::fragments::{Fragment, ImageFragment}; +use crate::geom::{flow_relative, physical}; +use crate::positioned::AbsolutelyPositionedFragment; +use crate::ContainingBlock; +use net_traits::image::base::Image; +use servo_arc::Arc as ServoArc; +use std::sync::Arc; use style::context::SharedStyleContext; +use style::properties::ComputedValues; +use style::values::computed::Length; +use style::Zero; #[derive(Debug)] -pub(super) enum ReplacedContent { - // Not implemented yet +pub(crate) struct ReplacedContent { + pub kind: ReplacedContentKind, + pub intrinsic_size: physical::Vec2<Length>, +} + +#[derive(Debug)] +pub(crate) enum ReplacedContentKind { + Image(Option<Arc<Image>>), } impl ReplacedContent { - pub fn for_element<'dom, Node>(element: Node, _context: &SharedStyleContext) -> Option<Self> - where - Node: NodeExt<'dom>, - { - // FIXME: implement <img> etc. + pub fn for_element<'dom>(element: impl NodeExt<'dom>) -> Option<Self> { + if let Some((image, intrinsic_size)) = element.as_image() { + return Some(Self { + kind: ReplacedContentKind::Image(image), + intrinsic_size, + }); + } None } + + pub fn layout<'a>( + &'a self, + layout_context: &LayoutContext, + style: &ServoArc<ComputedValues>, + containing_block: &ContainingBlock, + ) -> IndependentLayout { + let (fragments, content_block_size) = match self.kind { + ReplacedContentKind::Image(ref image) => { + // FIXME(nox): We should not assume block size is known. + let block_size = containing_block.block_size.non_auto().unwrap(); + let fragments = image + .as_ref() + .and_then(|image| image.id) + .map(|image_key| { + Fragment::Image(ImageFragment { + style: style.clone(), + content_rect: flow_relative::Rect { + start_corner: flow_relative::Vec2::zero(), + size: flow_relative::Vec2 { + inline: containing_block.inline_size, + block: block_size, + }, + }, + image_key, + }) + }) + .into_iter() + .collect::<Vec<_>>(); + (fragments, block_size) + }, + }; + IndependentLayout { + fragments, + content_block_size, + } + } } diff --git a/components/style/properties/longhands/inherited_box.mako.rs b/components/style/properties/longhands/inherited_box.mako.rs index 2542ee5e376..a92d638671b 100644 --- a/components/style/properties/longhands/inherited_box.mako.rs +++ b/components/style/properties/longhands/inherited_box.mako.rs @@ -72,9 +72,10 @@ ${helpers.single_keyword( ${helpers.single_keyword( "image-rendering", "auto crisp-edges", - engines="gecko servo-2013", + engines="gecko servo-2013 servo-2020", extra_gecko_values="optimizespeed optimizequality", extra_servo_2013_values="pixelated", + extra_servo_2020_values="pixelated", gecko_aliases="-moz-crisp-edges=crisp-edges", animation_value_type="discrete", spec="https://drafts.csswg.org/css-images/#propdef-image-rendering", diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-007.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-007.xht.ini index b8731bb917c..0c000bb2cd3 100644 --- a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-007.xht.ini +++ b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-007.xht.ini @@ -1,2 +1,2 @@ [containing-block-007.xht] - expected: CRASH + expected: FAIL |