mirror of
https://github.com/servo/servo.git
synced 2025-09-27 23:30:08 +01:00
layout: Parameterize content box query (#38935)
Parameterize and rename both `Layout::content_box_query` and `Layout::content_boxes_query` to support the query of rendered padding area and content area that accounts for transform and scroll. Both of these query have been misleading for a time since they are using border box, instead of content box of a Node. This PR adds a new type `layout_api::BoxAreaType` to be passed from `ScriptThread` to `LayoutThread` to query the respective area. It is then used for the query within `IntersectionObserver` to pass several WPTs. Testing: Existing WPT Coverage. --------- Signed-off-by: Jo Steven Novaryo <jo.steven.novaryo@huawei.com>
This commit is contained in:
parent
32aba08be7
commit
10ca3b6fde
21 changed files with 125 additions and 125 deletions
|
@ -302,6 +302,14 @@ impl BoxFragment {
|
|||
rect.translate(self.cumulative_containing_block_rect.origin.to_vector())
|
||||
}
|
||||
|
||||
pub(crate) fn cumulative_content_box_rect(&self) -> PhysicalRect<Au> {
|
||||
self.offset_by_containing_block(&self.margin_rect())
|
||||
}
|
||||
|
||||
pub(crate) fn cumulative_padding_box_rect(&self) -> PhysicalRect<Au> {
|
||||
self.offset_by_containing_block(&self.padding_rect())
|
||||
}
|
||||
|
||||
pub(crate) fn cumulative_border_box_rect(&self) -> PhysicalRect<Au> {
|
||||
self.offset_by_containing_block(&self.border_rect())
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use base::id::PipelineId;
|
|||
use base::print_tree::PrintTree;
|
||||
use euclid::{Point2D, Rect, Size2D, UnknownUnit};
|
||||
use fonts::{ByteIndex, FontMetrics, GlyphStore};
|
||||
use layout_api::BoxAreaType;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use range::Range as ServoRange;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
|
@ -202,11 +203,13 @@ impl Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cumulative_border_box_rect(&self) -> Option<PhysicalRect<Au>> {
|
||||
pub(crate) fn cumulative_box_area_rect(&self, area: BoxAreaType) -> Option<PhysicalRect<Au>> {
|
||||
match self {
|
||||
Fragment::Box(fragment) | Fragment::Float(fragment) => {
|
||||
Some(fragment.borrow().cumulative_border_box_rect())
|
||||
},
|
||||
Fragment::Box(fragment) | Fragment::Float(fragment) => Some(match area {
|
||||
BoxAreaType::Content => fragment.borrow().cumulative_content_box_rect(),
|
||||
BoxAreaType::Padding => fragment.borrow().cumulative_padding_box_rect(),
|
||||
BoxAreaType::Border => fragment.borrow().cumulative_border_box_rect(),
|
||||
}),
|
||||
Fragment::Positioning(fragment) => {
|
||||
let fragment = fragment.borrow();
|
||||
Some(fragment.offset_by_containing_block(&fragment.rect))
|
||||
|
|
|
@ -28,9 +28,9 @@ use fonts_traits::StylesheetWebFontLoadFinishedCallback;
|
|||
use fxhash::FxHashMap;
|
||||
use layout_api::wrapper_traits::LayoutNode;
|
||||
use layout_api::{
|
||||
IFrameSizes, Layout, LayoutConfig, LayoutDamage, LayoutFactory, OffsetParentResponse,
|
||||
PropertyRegistration, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest,
|
||||
ReflowRequestRestyle, ReflowResult, RegisterPropertyError, TrustedNodeAddress,
|
||||
BoxAreaType, IFrameSizes, Layout, LayoutConfig, LayoutDamage, LayoutFactory,
|
||||
OffsetParentResponse, PropertyRegistration, QueryMsg, ReflowGoal, ReflowPhasesRun,
|
||||
ReflowRequest, ReflowRequestRestyle, ReflowResult, RegisterPropertyError, TrustedNodeAddress,
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
|
||||
|
@ -90,8 +90,8 @@ use webrender_api::units::{DevicePixel, LayoutVector2D};
|
|||
use crate::context::{CachedImageOrError, ImageResolver, LayoutContext};
|
||||
use crate::display_list::{DisplayListBuilder, HitTest, StackingContextTree};
|
||||
use crate::query::{
|
||||
get_the_text_steps, process_client_rect_request, process_content_box_request,
|
||||
process_content_boxes_request, process_node_scroll_area_request, process_offset_parent_query,
|
||||
get_the_text_steps, process_box_area_request, process_box_areas_request,
|
||||
process_client_rect_request, process_node_scroll_area_request, process_offset_parent_query,
|
||||
process_resolved_font_style_query, process_resolved_style_request, process_text_index_request,
|
||||
};
|
||||
use crate::traversal::{RecalcStyle, compute_damage_and_repair_style};
|
||||
|
@ -262,14 +262,17 @@ impl Layout for LayoutThread {
|
|||
.remove_all_web_fonts_from_stylesheet(&stylesheet);
|
||||
}
|
||||
|
||||
/// Return the union of this node's content boxes in the coordinate space of the Document.
|
||||
/// to implement `getBoundingClientRect()`.
|
||||
/// Return the union of this node's areas in the coordinate space of the Document. This is used
|
||||
/// to implement `getBoundingClientRect()` and support many other API where the such query is
|
||||
/// required.
|
||||
///
|
||||
/// Part of <https://drafts.csswg.org/cssom-view-1/#element-get-the-bounding-box>
|
||||
/// TODO(stevennovaryo): Rename and parameterize the function, allowing padding area
|
||||
/// query and possibly, query without consideration of transform.
|
||||
/// Part of <https://drafts.csswg.org/cssom-view-1/#element-get-the-bounding-box>.
|
||||
#[servo_tracing::instrument(skip_all)]
|
||||
fn query_content_box(&self, node: TrustedNodeAddress) -> Option<UntypedRect<Au>> {
|
||||
fn query_box_area(
|
||||
&self,
|
||||
node: TrustedNodeAddress,
|
||||
area: BoxAreaType,
|
||||
) -> Option<UntypedRect<Au>> {
|
||||
// If we have not built a fragment tree yet, there is no way we have layout information for
|
||||
// this query, which can be run without forcing a layout (for IntersectionObserver).
|
||||
if self.fragment_tree.borrow().is_none() {
|
||||
|
@ -280,16 +283,16 @@ impl Layout for LayoutThread {
|
|||
let stacking_context_tree = self.stacking_context_tree.borrow();
|
||||
let stacking_context_tree = stacking_context_tree
|
||||
.as_ref()
|
||||
.expect("Should always have a StackingContextTree for content box queries");
|
||||
process_content_box_request(stacking_context_tree, node.to_threadsafe())
|
||||
.expect("Should always have a StackingContextTree for box area queries");
|
||||
process_box_area_request(stacking_context_tree, node.to_threadsafe(), area)
|
||||
}
|
||||
|
||||
/// Get a `Vec` of bounding boxes of this node's `Fragement`s in the coordinate space of the
|
||||
/// Document. This is used to implement `getClientRects()`.
|
||||
/// Get a `Vec` of bounding boxes of this node's `Fragment`s specific area in the coordinate space of
|
||||
/// the Document. This is used to implement `getClientRects()`.
|
||||
///
|
||||
/// See <https://drafts.csswg.org/cssom-view/#dom-element-getclientrects>.
|
||||
#[servo_tracing::instrument(skip_all)]
|
||||
fn query_content_boxes(&self, node: TrustedNodeAddress) -> Vec<UntypedRect<Au>> {
|
||||
fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Vec<UntypedRect<Au>> {
|
||||
// If we have not built a fragment tree yet, there is no way we have layout information for
|
||||
// this query, which can be run without forcing a layout (for IntersectionObserver).
|
||||
if self.fragment_tree.borrow().is_none() {
|
||||
|
@ -300,8 +303,8 @@ impl Layout for LayoutThread {
|
|||
let stacking_context_tree = self.stacking_context_tree.borrow();
|
||||
let stacking_context_tree = stacking_context_tree
|
||||
.as_ref()
|
||||
.expect("Should always have a StackingContextTree for content box queries");
|
||||
process_content_boxes_request(stacking_context_tree, node.to_threadsafe())
|
||||
.expect("Should always have a StackingContextTree for box area queries");
|
||||
process_box_areas_request(stacking_context_tree, node.to_threadsafe(), area)
|
||||
}
|
||||
|
||||
#[servo_tracing::instrument(skip_all)]
|
||||
|
@ -1597,8 +1600,8 @@ impl ReflowPhases {
|
|||
QueryMsg::NodesFromPointQuery => {
|
||||
Self::StackingContextTreeConstruction | Self::DisplayListConstruction
|
||||
},
|
||||
QueryMsg::ContentBox |
|
||||
QueryMsg::ContentBoxes |
|
||||
QueryMsg::BoxArea |
|
||||
QueryMsg::BoxAreas |
|
||||
QueryMsg::ResolvedStyleQuery |
|
||||
QueryMsg::ScrollingAreaOrOffsetQuery |
|
||||
QueryMsg::ElementsFromPoint => Self::StackingContextTreeConstruction,
|
||||
|
|
|
@ -11,7 +11,7 @@ use euclid::default::{Point2D, Rect};
|
|||
use euclid::{SideOffsets2D, Size2D};
|
||||
use itertools::Itertools;
|
||||
use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
||||
use layout_api::{LayoutElementType, LayoutNodeType, OffsetParentResponse};
|
||||
use layout_api::{BoxAreaType, LayoutElementType, LayoutNodeType, OffsetParentResponse};
|
||||
use script::layout_dom::{ServoLayoutNode, ServoThreadSafeLayoutNode};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use servo_geometry::{FastLayoutTransform, au_rect_to_f32_rect, f32_rect_to_au_rect};
|
||||
|
@ -67,14 +67,15 @@ fn root_transform_for_layout_node(
|
|||
Some(scroll_tree.cumulative_node_to_root_transform(&scroll_tree_node_id))
|
||||
}
|
||||
|
||||
pub(crate) fn process_content_box_request(
|
||||
pub(crate) fn process_box_area_request(
|
||||
stacking_context_tree: &StackingContextTree,
|
||||
node: ServoThreadSafeLayoutNode<'_>,
|
||||
area: BoxAreaType,
|
||||
) -> Option<Rect<Au>> {
|
||||
let rects: Vec<_> = node
|
||||
.fragments_for_pseudo(None)
|
||||
.iter()
|
||||
.filter_map(Fragment::cumulative_border_box_rect)
|
||||
.filter_map(|node| node.cumulative_box_area_rect(area))
|
||||
.collect();
|
||||
if rects.is_empty() {
|
||||
return None;
|
||||
|
@ -92,23 +93,24 @@ pub(crate) fn process_content_box_request(
|
|||
transform_au_rectangle(rect_union, transform)
|
||||
}
|
||||
|
||||
pub(crate) fn process_content_boxes_request(
|
||||
pub(crate) fn process_box_areas_request(
|
||||
stacking_context_tree: &StackingContextTree,
|
||||
node: ServoThreadSafeLayoutNode<'_>,
|
||||
area: BoxAreaType,
|
||||
) -> Vec<Rect<Au>> {
|
||||
let fragments = node.fragments_for_pseudo(None);
|
||||
let content_boxes = fragments
|
||||
let box_areas = fragments
|
||||
.iter()
|
||||
.filter_map(Fragment::cumulative_border_box_rect)
|
||||
.filter_map(|node| node.cumulative_box_area_rect(area))
|
||||
.map(|rect| rect.to_untyped());
|
||||
|
||||
let Some(transform) =
|
||||
root_transform_for_layout_node(&stacking_context_tree.compositor_info.scroll_tree, node)
|
||||
else {
|
||||
return content_boxes.collect();
|
||||
return box_areas.collect();
|
||||
};
|
||||
|
||||
content_boxes
|
||||
box_areas
|
||||
.filter_map(|rect| transform_au_rectangle(rect, transform))
|
||||
.collect()
|
||||
}
|
||||
|
@ -575,7 +577,7 @@ pub fn process_offset_parent_query(node: ServoLayoutNode<'_>) -> Option<OffsetPa
|
|||
.fragments_for_pseudo(None)
|
||||
.first()
|
||||
.cloned()?;
|
||||
let mut border_box = fragment.cumulative_border_box_rect()?;
|
||||
let mut border_box = fragment.cumulative_box_area_rect(BoxAreaType::Border)?;
|
||||
|
||||
// 2. If the offsetParent of the element is null return the x-coordinate of the left
|
||||
// border edge of the first CSS layout box associated with the element, relative to
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue