Skip to content

Commit

Permalink
Refactor scrolls on the window object
Browse files Browse the repository at this point in the history
Refactor the scrolling and scrollable area calculation on the window
object, to make it better match the specification. This has some mild
changes to behavior, but in general things work the same as they did
before. This is mainly preparation for properly handling viewport
propagation of the `overflow` property but seems to fix a few issues as
well.

There is one new failure in Layout 2020 regarding `position: sticky`,
but this isn't a big deal because there is no support for `position:
sticky` in Layout 2020 yet.

Co-authored-by: Rakhi Sharma <atbrakhi@igalia.com>
  • Loading branch information
mrobinson and atbrakhi committed Aug 10, 2023
1 parent 8dceb8e commit 822a618
Show file tree
Hide file tree
Showing 18 changed files with 135 additions and 128 deletions.
21 changes: 16 additions & 5 deletions components/layout/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub struct LayoutThreadData {
pub scroll_id_response: Option<ExternalScrollId>,

/// A queued response for the scroll {top, left, width, height} of a node in pixels.
pub scroll_area_response: Rect<i32>,
pub scrolling_area_response: Rect<i32>,

/// A queued response for the resolved style property of an element.
pub resolved_style_response: String,
Expand Down Expand Up @@ -157,9 +157,9 @@ impl LayoutRPC for LayoutRPCImpl {
}
}

fn node_scroll_area(&self) -> NodeGeometryResponse {
fn scrolling_area(&self) -> NodeGeometryResponse {
NodeGeometryResponse {
client_rect: self.0.lock().unwrap().scroll_area_response,
client_rect: self.0.lock().unwrap().scrolling_area_response,
}
}

Expand Down Expand Up @@ -729,10 +729,21 @@ pub fn process_node_scroll_id_request<'dom>(
}

/// https://drafts.csswg.org/cssom-view/#scrolling-area
pub fn process_node_scroll_area_request(
requested_node: OpaqueNode,
pub fn process_scrolling_area_request(
requested_node: Option<OpaqueNode>,
layout_root: &mut dyn Flow,
) -> Rect<i32> {
let requested_node = match requested_node {
Some(node) => node,
None => {
let rect = layout_root.base().overflow.scroll;
return Rect::new(
Point2D::new(rect.origin.x.to_nearest_px(), rect.origin.y.to_nearest_px()),
Size2D::new(rect.width().ceil_to_px(), rect.height().ceil_to_px()),
);
},
};

let mut iterator = UnioningFragmentScrollAreaIterator::new(requested_node);
sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
match iterator.overflow_direction {
Expand Down
9 changes: 9 additions & 0 deletions components/layout_2020/fragment_tree/fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,15 @@ impl Fragment {
}
}

pub fn scrolling_area(&self, containing_block: &PhysicalRect<Length>) -> PhysicalRect<Length> {
match self {
Fragment::Box(fragment) | Fragment::Float(fragment) => fragment
.scrollable_overflow(&containing_block)
.translate(containing_block.origin.to_vector()),
_ => self.scrollable_overflow(containing_block),
}
}

pub fn scrollable_overflow(
&self,
containing_block: &PhysicalRect<Length>,
Expand Down
46 changes: 18 additions & 28 deletions components/layout_2020/fragment_tree/fragment_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,36 +170,26 @@ impl FragmentTree {
.unwrap_or_else(Rect::zero)
}

pub fn get_scroll_area_for_node(&self, requested_node: OpaqueNode) -> Rect<i32> {
let mut scroll_area: PhysicalRect<Length> = PhysicalRect::zero();
pub fn get_scrolling_area_for_viewport(&self) -> PhysicalRect<Length> {
let mut scroll_area = self.initial_containing_block;
for fragment in self.root_fragments.iter() {
scroll_area = fragment
.borrow()
.scrolling_area(&self.initial_containing_block)
.union(&scroll_area);
}
scroll_area
}

pub fn get_scrolling_area_for_node(&self, requested_node: OpaqueNode) -> PhysicalRect<Length> {
let tag_to_find = Tag::new(requested_node);
self.find(|fragment, _, containing_block| {
if fragment.tag() != Some(tag_to_find) {
return None::<()>;
let scroll_area = self.find(|fragment, _, containing_block| {
if fragment.tag() == Some(tag_to_find) {
Some(fragment.scrolling_area(&containing_block))
} else {
None
}

scroll_area = match fragment {
Fragment::Box(fragment) | Fragment::Float(fragment) => fragment
.scrollable_overflow(&containing_block)
.translate(containing_block.origin.to_vector()),
Fragment::Text(_) |
Fragment::AbsoluteOrFixedPositioned(_) |
Fragment::Image(_) |
Fragment::IFrame(_) |
Fragment::Anonymous(_) => return None,
};
None::<()>
});

Rect::new(
Point2D::new(
scroll_area.origin.x.px() as i32,
scroll_area.origin.y.px() as i32,
),
Size2D::new(
scroll_area.size.width.px() as i32,
scroll_area.size.height.px() as i32,
),
)
scroll_area.unwrap_or_else(PhysicalRect::<Length>::zero)
}
}
23 changes: 14 additions & 9 deletions components/layout_2020/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub struct LayoutThreadData {
pub scroll_id_response: Option<ExternalScrollId>,

/// A queued response for the scroll {top, left, width, height} of a node in pixels.
pub scroll_area_response: Rect<i32>,
pub scrolling_area_response: Rect<i32>,

/// A queued response for the resolved style property of an element.
pub resolved_style_response: String,
Expand Down Expand Up @@ -115,9 +115,9 @@ impl LayoutRPC for LayoutRPCImpl {
}
}

fn node_scroll_area(&self) -> NodeGeometryResponse {
fn scrolling_area(&self) -> NodeGeometryResponse {
NodeGeometryResponse {
client_rect: self.0.lock().unwrap().scroll_area_response,
client_rect: self.0.lock().unwrap().scrolling_area_response,
}
}

Expand Down Expand Up @@ -201,14 +201,19 @@ pub fn process_node_scroll_id_request<'dom>(

/// https://drafts.csswg.org/cssom-view/#scrolling-area
pub fn process_node_scroll_area_request(
requested_node: OpaqueNode,
requested_node: Option<OpaqueNode>,
fragment_tree: Option<Arc<FragmentTree>>,
) -> Rect<i32> {
if let Some(fragment_tree) = fragment_tree {
fragment_tree.get_scroll_area_for_node(requested_node)
} else {
Rect::zero()
}
let rect = match (fragment_tree, requested_node) {
(Some(tree), Some(node)) => tree.get_scrolling_area_for_node(node),
(Some(tree), None) => tree.get_scrolling_area_for_viewport(),
_ => return Rect::zero(),
};

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

/// Return the resolved value of property for a given (pseudo)element.
Expand Down
19 changes: 9 additions & 10 deletions components/layout_thread/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@ use layout::incremental::{RelayoutMode, SpecialRestyleDamage};
use layout::parallel;
use layout::query::{
process_client_rect_query, process_content_box_request, process_content_boxes_request,
process_element_inner_text_query, process_node_scroll_area_request,
process_node_scroll_id_request, process_offset_parent_query,
process_resolved_font_style_request, process_resolved_style_request, LayoutRPCImpl,
LayoutThreadData,
process_element_inner_text_query, process_node_scroll_id_request, process_offset_parent_query,
process_resolved_font_style_request, process_resolved_style_request,
process_scrolling_area_request, LayoutRPCImpl, LayoutThreadData,
};
use layout::sequential;
use layout::traversal::{
Expand Down Expand Up @@ -501,7 +500,7 @@ impl LayoutThread {
content_boxes_response: Vec::new(),
client_rect_response: Rect::zero(),
scroll_id_response: None,
scroll_area_response: Rect::zero(),
scrolling_area_response: Rect::zero(),
resolved_style_response: String::new(),
resolved_font_style_response: None,
offset_parent_response: OffsetParentResponse::empty(),
Expand Down Expand Up @@ -1130,8 +1129,8 @@ impl LayoutThread {
&QueryMsg::ClientRectQuery(_) => {
rw_data.client_rect_response = Rect::zero();
},
&QueryMsg::NodeScrollGeometryQuery(_) => {
rw_data.scroll_area_response = Rect::zero();
&QueryMsg::ScrollingAreaQuery(_) => {
rw_data.scrolling_area_response = Rect::zero();
},
&QueryMsg::NodeScrollIdQuery(_) => {
rw_data.scroll_id_response = None;
Expand Down Expand Up @@ -1440,9 +1439,9 @@ impl LayoutThread {
&QueryMsg::ClientRectQuery(node) => {
rw_data.client_rect_response = process_client_rect_query(node, root_flow);
},
&QueryMsg::NodeScrollGeometryQuery(node) => {
rw_data.scroll_area_response =
process_node_scroll_area_request(node, root_flow);
&QueryMsg::ScrollingAreaQuery(node) => {
rw_data.scrolling_area_response =
process_scrolling_area_request(node, root_flow);
},
&QueryMsg::NodeScrollIdQuery(node) => {
let node: ServoLayoutNode<LayoutData> = unsafe { ServoLayoutNode::new(&node) };
Expand Down
10 changes: 5 additions & 5 deletions components/layout_thread_2020/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ impl LayoutThread {
content_boxes_response: Vec::new(),
client_rect_response: Rect::zero(),
scroll_id_response: None,
scroll_area_response: Rect::zero(),
scrolling_area_response: Rect::zero(),
resolved_style_response: String::new(),
resolved_font_style_response: None,
offset_parent_response: OffsetParentResponse::empty(),
Expand Down Expand Up @@ -826,8 +826,8 @@ impl LayoutThread {
&QueryMsg::ClientRectQuery(_) => {
rw_data.client_rect_response = Rect::zero();
},
&QueryMsg::NodeScrollGeometryQuery(_) => {
rw_data.scroll_area_response = Rect::zero();
&QueryMsg::ScrollingAreaQuery(_) => {
rw_data.scrolling_area_response = Rect::zero();
},
&QueryMsg::NodeScrollIdQuery(_) => {
rw_data.scroll_id_response = None;
Expand Down Expand Up @@ -1105,8 +1105,8 @@ impl LayoutThread {
rw_data.client_rect_response =
process_node_geometry_request(node, self.fragment_tree.borrow().clone());
},
&QueryMsg::NodeScrollGeometryQuery(node) => {
rw_data.scroll_area_response =
&QueryMsg::ScrollingAreaQuery(node) => {
rw_data.scrolling_area_response =
process_node_scroll_area_request(node, self.fragment_tree.borrow().clone());
},
&QueryMsg::NodeScrollIdQuery(node) => {
Expand Down
13 changes: 9 additions & 4 deletions components/script/dom/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,14 @@ impl Element {
node.parent_directionality()
})
}

pub(crate) fn is_root(&self) -> bool {
match self.node.GetParentNode() {
None => false,
Some(node) => node.is::<Document>(),
}
}

}

#[inline]
Expand Down Expand Up @@ -3189,10 +3197,7 @@ impl<'a> SelectorsElement for DomRoot<Element> {
}

fn is_root(&self) -> bool {
match self.node.GetParentNode() {
None => false,
Some(node) => node.is::<Document>(),
}
Element::is_root(self)
}

fn is_empty(&self) -> bool {
Expand Down
58 changes: 32 additions & 26 deletions components/script/dom/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use crate::script_thread::ScriptThread;
use app_units::Au;
use devtools_traits::NodeInfo;
use dom_struct::dom_struct;
use euclid::default::{Point2D, Rect, Size2D, Vector2D};
use euclid::default::{Rect, Size2D, Vector2D};
use html5ever::{Namespace, Prefix, QualName};
use js::jsapi::JSObject;
use js::rust::HandleObject;
Expand Down Expand Up @@ -800,38 +800,44 @@ impl Node {
// https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth
// https://drafts.csswg.org/cssom-view/#dom-element-scrollheight
pub fn scroll_area(&self) -> Rect<i32> {
// Step 1
// "1. Let document be the element’s node document.""
let document = self.owner_doc();
// Step 3
let window = document.window();

let html_element = document.GetDocumentElement();
// "2. If document is not the active document, return zero and terminate these steps.""
if !document.is_active() {
return Rect::zero();
}

// "3. Let viewport width/height be the width of the viewport excluding the width/height of the
// scroll bar, if any, or zero if there is no viewport."
let window = document.window();
let viewport = Size2D::new(window.InnerWidth(), window.InnerHeight());

let in_quirks_mode = document.quirks_mode() == QuirksMode::Quirks;
let is_root = self
.downcast::<Element>()
.map_or(false, |e| e.is_root());
let is_body_element = self
.downcast::<HTMLBodyElement>()
.map_or(false, |e| e.is_the_html_body_element());

let scroll_area = window.scroll_area_query(self);

match (
document != window.Document(),
is_body_element,
document.quirks_mode(),
html_element.as_deref() == self.downcast::<Element>(),
) {
// Step 2 && Step 5
(true, _, _, _) | (_, false, QuirksMode::Quirks, true) => Rect::zero(),
// Step 6 && Step 7
(false, false, _, true) | (false, true, QuirksMode::Quirks, _) => Rect::new(
Point2D::new(window.ScrollX(), window.ScrollY()),
Size2D::new(
cmp::max(window.InnerWidth(), scroll_area.size.width),
cmp::max(window.InnerHeight(), scroll_area.size.height),
),
),
// Step 9
_ => scroll_area,
}
// "4. If the element is the root element and document is not in quirks mode
// return max(viewport scrolling area width/height, viewport width/height)."
// "5. If the element is the body element, document is in quirks mode and the
// element is not potentially scrollable, return max(viewport scrolling area
// width, viewport width)."
if (is_root && !in_quirks_mode) || (is_body_element && in_quirks_mode) {
println!("return is root: {:?}", is_root);
let viewport_scrolling_area = window.scrolling_area_query(None);
return Rect::new(
viewport_scrolling_area.origin,
viewport_scrolling_area.size.max(viewport));
}

// "6. If the element does not have any associated box return zero and terminate
// these steps."
// "7. Return the width of the element’s scrolling area."
window.scrolling_area_query(Some(self))
}

pub fn scroll_offset(&self) -> Vector2D<f32> {
Expand Down

0 comments on commit 822a618

Please sign in to comment.