Skip to content

Commit

Permalink
Add layout_2020 support for NodeGeometryQuery and ContentBoxQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
mrobinson authored and SimonSapin committed Feb 11, 2020
1 parent b10a24e commit fa78392
Show file tree
Hide file tree
Showing 21 changed files with 193 additions and 172 deletions.
200 changes: 144 additions & 56 deletions components/layout_2020/flow/root.rs
Expand Up @@ -10,7 +10,7 @@ use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::Fragment;
use crate::geom::flow_relative::Vec2;
use crate::geom::PhysicalRect;
use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize};
use crate::positioned::AbsolutelyPositionedBox;
use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContent;
Expand All @@ -22,7 +22,8 @@ use euclid::default::{Point2D, Rect, Size2D};
use gfx_traits::print_tree::PrintTree;
use script_layout_interface::wrapper_traits::LayoutNode;
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::dom::OpaqueNode;
use style::properties::{style_structs, ComputedValues};
use style::values::computed::Length;
use style_traits::CSSPixel;

Expand All @@ -35,8 +36,8 @@ pub struct FragmentTreeRoot {
/// The scrollable overflow of the root of the fragment tree.
scrollable_overflow: PhysicalRect<Length>,

/// The axis-aligned bounding box of the border box of all child fragments
bounding_box_of_border_boxes: PhysicalRect<Length>,
/// The containing block used in the layout of this fragment tree.
initial_containing_block: PhysicalRect<Length>,
}

impl BoxTreeRoot {
Expand Down Expand Up @@ -117,13 +118,18 @@ impl BoxTreeRoot {
viewport: euclid::Size2D<f32, CSSPixel>,
) -> FragmentTreeRoot {
let style = ComputedValues::initial_values();

// FIXME: use the document’s mode:
// https://drafts.csswg.org/css-writing-modes/#principal-flow
let physical_containing_block = PhysicalRect::new(
PhysicalPoint::zero(),
PhysicalSize::new(Length::new(viewport.width), Length::new(viewport.height)),
);
let initial_containing_block = DefiniteContainingBlock {
size: Vec2 {
inline: Length::new(viewport.width),
block: Length::new(viewport.height),
inline: physical_containing_block.size.width,
block: physical_containing_block.size.height,
},
// FIXME: use the document’s mode:
// https://drafts.csswg.org/css-writing-modes/#principal-flow
style,
};

Expand All @@ -142,8 +148,6 @@ impl BoxTreeRoot {
&mut independent_layout.fragments,
);

// FIXME(mrobinson, bug 25564): We should be using the containing block
// here to properly convert scrollable overflow to physical geometry.
let scrollable_overflow =
independent_layout
.fragments
Expand All @@ -167,51 +171,18 @@ impl BoxTreeRoot {
acc.union(&child_overflow)
});

let containing_block = PhysicalRect::zero();
let bounding_box_of_border_boxes =
independent_layout
.fragments
.iter()
.fold(PhysicalRect::zero(), |acc, child| {
acc.union(&match child {
Fragment::Box(fragment) => fragment
.border_rect()
.to_physical(fragment.style.writing_mode, &containing_block),
Fragment::Anonymous(fragment) => {
fragment.rect.to_physical(fragment.mode, &containing_block)
},
Fragment::Text(fragment) => fragment
.rect
.to_physical(fragment.parent_style.writing_mode, &containing_block),
Fragment::Image(fragment) => fragment
.rect
.to_physical(fragment.style.writing_mode, &containing_block),
})
});

FragmentTreeRoot {
children: independent_layout.fragments,
scrollable_overflow,
bounding_box_of_border_boxes,
initial_containing_block: physical_containing_block,
}
}
}

impl FragmentTreeRoot {
pub fn build_display_list(
&self,
builder: &mut crate::display_list::DisplayListBuilder,
viewport_size: webrender_api::units::LayoutSize,
) {
let containing_block = PhysicalRect::new(
euclid::Point2D::zero(),
euclid::Size2D::new(
Length::new(viewport_size.width),
Length::new(viewport_size.height),
),
);
pub fn build_display_list(&self, builder: &mut crate::display_list::DisplayListBuilder) {
for fragment in &self.children {
fragment.build_display_list(builder, &containing_block)
fragment.build_display_list(builder, &self.initial_containing_block)
}
}

Expand All @@ -229,15 +200,132 @@ impl FragmentTreeRoot {
))
}

pub fn bounding_box_of_border_boxes(&self) -> Rect<Au> {
let origin = Point2D::new(
Au::from_f32_px(self.bounding_box_of_border_boxes.origin.x.px()),
Au::from_f32_px(self.bounding_box_of_border_boxes.origin.y.px()),
);
let size = Size2D::new(
Au::from_f32_px(self.bounding_box_of_border_boxes.size.width.px()),
Au::from_f32_px(self.bounding_box_of_border_boxes.size.height.px()),
);
Rect::new(origin, size)
fn iterate_through_fragments<F>(&self, process_func: &mut F)
where
F: FnMut(&Fragment, &PhysicalRect<Length>) -> bool,
{
fn do_iteration<M>(
fragment: &Fragment,
containing_block: &PhysicalRect<Length>,
process_func: &mut M,
) -> bool
where
M: FnMut(&Fragment, &PhysicalRect<Length>) -> bool,
{
if !process_func(fragment, containing_block) {
return false;
}

match fragment {
Fragment::Box(fragment) => {
let new_containing_block = fragment
.content_rect
.to_physical(fragment.style.writing_mode, containing_block)
.translate(containing_block.origin.to_vector());
for child in &fragment.children {
do_iteration(child, &new_containing_block, process_func);
}
},
Fragment::Anonymous(fragment) => {
let new_containing_block = fragment
.rect
.to_physical(fragment.mode, containing_block)
.translate(containing_block.origin.to_vector());
for child in &fragment.children {
do_iteration(child, &new_containing_block, process_func);
}
},
_ => {},
}

true
}

for child in &self.children {
if !do_iteration(child, &self.initial_containing_block, process_func) {
break;
}
}
}

pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect<Au> {
let mut rect = PhysicalRect::zero();
let mut process = |fragment: &Fragment, containing_block: &PhysicalRect<Length>| {
let fragment_relative_rect = match fragment {
Fragment::Box(fragment) if fragment.tag == requested_node => fragment
.border_rect()
.to_physical(fragment.style.writing_mode, &containing_block),
Fragment::Text(fragment) if fragment.tag == requested_node => fragment
.rect
.to_physical(fragment.parent_style.writing_mode, &containing_block),
Fragment::Box(_) |
Fragment::Text(_) |
Fragment::Image(_) |
Fragment::Anonymous(_) => return true,
};

rect = fragment_relative_rect
.translate(containing_block.origin.to_vector())
.union(&rect);
return true;
};

self.iterate_through_fragments(&mut process);

Rect::new(
Point2D::new(
Au::from_f32_px(rect.origin.x.px()),
Au::from_f32_px(rect.origin.y.px()),
),
Size2D::new(
Au::from_f32_px(rect.size.width.px()),
Au::from_f32_px(rect.size.height.px()),
),
)
}

pub fn get_border_dimensions_for_node(&self, requested_node: OpaqueNode) -> Rect<i32> {
let mut border_dimensions = Rect::zero();
let mut process = |fragment: &Fragment, containing_block: &PhysicalRect<Length>| {
let (style, padding_rect) = match fragment {
Fragment::Box(fragment) if fragment.tag == requested_node => {
(&fragment.style, fragment.padding_rect())
},
Fragment::Box(_) |
Fragment::Text(_) |
Fragment::Image(_) |
Fragment::Anonymous(_) => return true,
};

// https://drafts.csswg.org/cssom-view/#dom-element-clienttop
// " If the element has no associated CSS layout box or if the
// CSS layout box is inline, return zero." For this check we
// also explicitly ignore the list item portion of the display
// style.
let display = &style.get_box().display;
if display.inside() == style::values::specified::box_::DisplayInside::Flow &&
display.outside() == style::values::specified::box_::DisplayOutside::Inline
{
return false;
}

let style_structs::Border {
border_top_width: top_width,
border_left_width: left_width,
..
} = *style.get_border();

let padding_rect = padding_rect.to_physical(style.writing_mode, &containing_block);
border_dimensions.size.width = padding_rect.size.width.px() as i32;
border_dimensions.size.height = padding_rect.size.height.px() as i32;
border_dimensions.origin.y = top_width.px() as i32;
border_dimensions.origin.x = left_width.px() as i32;

false
};

self.iterate_through_fragments(&mut process);

border_dimensions
}
}
17 changes: 13 additions & 4 deletions components/layout_2020/query.rs
Expand Up @@ -165,22 +165,31 @@ impl LayoutRPC for LayoutRPCImpl {
}

pub fn process_content_box_request(
_requested_node: OpaqueNode,
requested_node: OpaqueNode,
fragment_tree_root: Option<&FragmentTreeRoot>,
) -> Option<Rect<Au>> {
let fragment_tree_root = match fragment_tree_root {
Some(fragment_tree_root) => fragment_tree_root,
None => return None,
};
Some(fragment_tree_root.bounding_box_of_border_boxes())

Some(fragment_tree_root.get_content_box_for_node(requested_node))
}

pub fn process_content_boxes_request(_requested_node: OpaqueNode) -> Vec<Rect<Au>> {
vec![]
}

pub fn process_node_geometry_request(_requested_node: OpaqueNode) -> Rect<i32> {
Rect::zero()
pub fn process_node_geometry_request(
requested_node: OpaqueNode,
fragment_tree_root: Option<&FragmentTreeRoot>,
) -> Rect<i32> {
let fragment_tree_root = match fragment_tree_root {
Some(fragment_tree_root) => fragment_tree_root,
None => return Rect::zero(),
};

fragment_tree_root.get_border_dimensions_for_node(requested_node)
}

pub fn process_node_scroll_id_request<N: LayoutNode>(
Expand Down
15 changes: 9 additions & 6 deletions components/layout_thread_2020/lib.rs
Expand Up @@ -1234,7 +1234,10 @@ impl LayoutThread {
rw_data.text_index_response = process_text_index_request(node, point_in_node);
},
&QueryMsg::ClientRectQuery(node) => {
rw_data.client_rect_response = process_node_geometry_request(node);
rw_data.client_rect_response = process_node_geometry_request(
node,
(&*self.fragment_tree_root.borrow()).as_ref(),
);
},
&QueryMsg::NodeScrollGeometryQuery(node) => {
rw_data.scroll_area_response = process_node_scroll_area_request(node);
Expand Down Expand Up @@ -1383,11 +1386,7 @@ impl LayoutThread {
fragment_tree.scrollable_overflow(),
);

let viewport_size = webrender_api::units::LayoutSize::from_untyped(Size2D::new(
self.viewport_size.width.to_f32_px(),
self.viewport_size.height.to_f32_px(),
));
fragment_tree.build_display_list(&mut display_list, viewport_size);
fragment_tree.build_display_list(&mut display_list);

if self.dump_flow_tree {
fragment_tree.print();
Expand All @@ -1408,6 +1407,10 @@ impl LayoutThread {
self.paint_time_metrics
.maybe_observe_paint_time(self, epoch, display_list.is_contentful);

let viewport_size = webrender_api::units::LayoutSize::from_untyped(Size2D::new(
self.viewport_size.width.to_f32_px(),
self.viewport_size.height.to_f32_px(),
));
self.webrender_api.send_display_list(
self.webrender_document,
epoch,
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

@@ -1,7 +1,4 @@
[elementFromPoint.html]
[Fieldsets]
expected: FAIL

[SVG element at x,y]
expected: FAIL

Expand Down

This file was deleted.

@@ -1,7 +1,4 @@
[negativeMargins.html]
[cssom-view - elementFromPoint and elementsFromPoint dealing with negative margins]
expected: FAIL

[cssom-view - elementFromPoint and elementsFromPoint dealing with negative margins 1]
expected: FAIL

@@ -1,11 +1,5 @@
[scroll-behavior-smooth.html]
expected: ERROR
[Smooth scrolling while doing history navigation.]
expected: FAIL

[Instant scrolling while doing history navigation.]
expected: FAIL

[scroll-behavior: smooth on DIV element]
expected: FAIL

@@ -0,0 +1,19 @@
[scrollLeftTop.html]
[writing-mode:vertical-lr; direction:ltr]
expected: FAIL

[writing-mode:vertical-rl; direction:rtl]
expected: FAIL

[writing-mode:vertical-lr; direction:rtl]
expected: FAIL

[writing-mode:vertical-rl; direction:ltr]
expected: FAIL

[writing-mode:horizontal-tb; direction:ltr]
expected: FAIL

[writing-mode:horizontal-tb; direction:rtl]
expected: FAIL

0 comments on commit fa78392

Please sign in to comment.