aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout_2020
diff options
context:
space:
mode:
authorAnthony Ramine <nox@nox.paris>2019-11-29 09:28:44 +0100
committerSimon Sapin <simon.sapin@exyr.org>2019-12-01 10:56:33 +0100
commit5a360ac63d374023884289ca86b7c2390394c533 (patch)
treeddd0f62d6628b36f44d857be64bfa14e8ea1564d /components/layout_2020
parentf24e8d0557c6cb51fac0a5e8ff4046ec6a1717d3 (diff)
downloadservo-5a360ac63d374023884289ca86b7c2390394c533.tar.gz
servo-5a360ac63d374023884289ca86b7c2390394c533.zip
Start supporting images in layout 2020
Diffstat (limited to 'components/layout_2020')
-rw-r--r--components/layout_2020/Cargo.toml1
-rw-r--r--components/layout_2020/display_list.rs28
-rw-r--r--components/layout_2020/dom_traversal.rs33
-rw-r--r--components/layout_2020/flow/mod.rs139
-rw-r--r--components/layout_2020/flow/root.rs12
-rw-r--r--components/layout_2020/formatting_contexts.rs2
-rw-r--r--components/layout_2020/fragments.rs9
-rw-r--r--components/layout_2020/replaced.rs71
8 files changed, 249 insertions, 46 deletions
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/mod.rs b/components/layout_2020/flow/mod.rs
index 5c2e3d8e988..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,24 +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, &mut LengthOrAuto::LengthPercentage(e)) => {
- *s = LengthOrAuto::LengthPercentage(inline_margins - e);
- },
- (&mut LengthOrAuto::LengthPercentage(s), 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 - s);
- },
- }
+ 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);
@@ -476,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,
+ }
+ }
}