layout: Remove LayoutRPC and query layout via the Layout trait (#31937)

Instead of the tricky `LayoutRPC` interface, query layout using the
`Layout` trait. This means that now queries will requires calling layout
and then running the query. During layout an enum is used to indicate
what kind of layout is necessary.

This change also removes the mutex-locked `rw_data` from both layout
threads. It's no longer necessary since layout runs synchronously. The
one downside here is that for resolved style queries, we now have to
create two StyleContexts. One for layout and one for the query itself.
The creation of this context should not be very expensive though.

`LayoutRPC` used to be necessary because layout used to run
asynchronously from script, but that no longer happens. With this
change, it becomes possible to safely pass nodes to layout from script
-- a cleanup that can happen in a followup change.
This commit is contained in:
Martin Robinson 2024-03-29 17:25:47 +01:00 committed by GitHub
parent 07391e346b
commit b7d089930e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 667 additions and 1174 deletions

View file

@ -3,22 +3,17 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Utilities for querying the layout, as needed by layout.
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use app_units::Au;
use euclid::default::{Point2D, Rect};
use euclid::{SideOffsets2D, Size2D, Vector2D};
use log::warn;
use msg::constellation_msg::PipelineId;
use script_layout_interface::rpc::{
ContentBoxResponse, ContentBoxesResponse, LayoutRPC, NodeGeometryResponse,
NodeScrollIdResponse, OffsetParentResponse, ResolvedStyleResponse, TextIndexResponse,
};
use script_layout_interface::wrapper_traits::{
LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
};
use script_traits::UntrustedNodeAddress;
use script_layout_interface::OffsetParentResponse;
use servo_arc::Arc as ServoArc;
use servo_url::ServoUrl;
use style::computed_values::position::T as Position;
@ -27,7 +22,7 @@ use style::dom::{OpaqueNode, TElement};
use style::properties::style_structs::Font;
use style::properties::{
parse_one_declaration_into, ComputedValues, Importance, LonghandId, PropertyDeclarationBlock,
PropertyDeclarationId, PropertyId, SourcePropertyDeclaration,
PropertyDeclarationId, PropertyId, ShorthandId, SourcePropertyDeclaration,
};
use style::selector_parser::PseudoElement;
use style::shared_lock::SharedRwLock;
@ -35,146 +30,11 @@ use style::stylesheets::{CssRuleType, Origin, UrlExtraData};
use style::stylist::RuleInclusion;
use style::traversal::resolve_style;
use style::values::generics::text::LineHeight;
use style_traits::{CSSPixel, ParsingMode, ToCss};
use webrender_api::units::LayoutPixel;
use webrender_api::{DisplayListBuilder, ExternalScrollId};
use style_traits::{ParsingMode, ToCss};
use webrender_api::ExternalScrollId;
use crate::context::LayoutContext;
use crate::fragment_tree::{Fragment, FragmentFlags, FragmentTree, Tag};
/// Mutable data belonging to the LayoutThread.
///
/// This needs to be protected by a mutex so we can do fast RPCs.
pub struct LayoutThreadData {
/// The root stacking context.
pub display_list: Option<DisplayListBuilder>,
/// A queued response for the union of the content boxes of a node.
pub content_box_response: Option<Rect<Au>>,
/// A queued response for the content boxes of a node.
pub content_boxes_response: Vec<Rect<Au>>,
/// A queued response for the client {top, left, width, height} of a node in pixels.
pub client_rect_response: Rect<i32>,
/// A queued response for the scroll id for a given node.
pub scroll_id_response: Option<ExternalScrollId>,
/// A queued response for the scroll {top, left, width, height} of a node in pixels.
pub scrolling_area_response: Rect<i32>,
/// A queued response for the resolved style property of an element.
pub resolved_style_response: String,
/// A queued response for the resolved font style for canvas.
pub resolved_font_style_response: Option<ServoArc<Font>>,
/// A queued response for the offset parent/rect of a node.
pub offset_parent_response: OffsetParentResponse,
/// Scroll offsets of scrolling regions.
pub scroll_offsets: HashMap<ExternalScrollId, Vector2D<f32, LayoutPixel>>,
/// Index in a text fragment. We need this do determine the insertion point.
pub text_index_response: TextIndexResponse,
/// A queued response for the list of nodes at a given point.
pub nodes_from_point_response: Vec<UntrustedNodeAddress>,
/// A queued response for the inner text of a given element.
pub element_inner_text_response: String,
/// A queued response for the viewport dimensions for a given browsing context.
pub inner_window_dimensions_response: Option<Size2D<f32, CSSPixel>>,
}
pub struct LayoutRPCImpl(pub Arc<Mutex<LayoutThreadData>>);
impl LayoutRPC for LayoutRPCImpl {
// The neat thing here is that in order to answer the following two queries we only
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
fn content_box(&self) -> ContentBoxResponse {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
ContentBoxResponse(rw_data.content_box_response)
}
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
fn content_boxes(&self) -> ContentBoxesResponse {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
ContentBoxesResponse(rw_data.content_boxes_response.clone())
}
fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress> {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
rw_data.nodes_from_point_response.clone()
}
fn node_geometry(&self) -> NodeGeometryResponse {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
NodeGeometryResponse {
client_rect: rw_data.client_rect_response,
}
}
fn scrolling_area(&self) -> NodeGeometryResponse {
NodeGeometryResponse {
client_rect: self.0.lock().unwrap().scrolling_area_response,
}
}
fn node_scroll_id(&self) -> NodeScrollIdResponse {
NodeScrollIdResponse(
self.0
.lock()
.unwrap()
.scroll_id_response
.expect("scroll id is not correctly fetched"),
)
}
/// Retrieves the resolved value for a CSS style property.
fn resolved_style(&self) -> ResolvedStyleResponse {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
ResolvedStyleResponse(rw_data.resolved_style_response.clone())
}
fn resolved_font_style(&self) -> Option<ServoArc<Font>> {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
rw_data.resolved_font_style_response.clone()
}
fn offset_parent(&self) -> OffsetParentResponse {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
rw_data.offset_parent_response.clone()
}
fn text_index(&self) -> TextIndexResponse {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
rw_data.text_index_response.clone()
}
fn element_inner_text(&self) -> String {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
rw_data.element_inner_text_response.clone()
}
fn inner_window_dimensions(&self) -> Option<Size2D<f32, CSSPixel>> {
let LayoutRPCImpl(rw_data) = self;
let rw_data = rw_data.lock().unwrap();
rw_data.inner_window_dimensions_response
}
}
pub fn process_content_box_request(
requested_node: OpaqueNode,
fragment_tree: Option<Arc<FragmentTree>>,
@ -242,7 +102,7 @@ pub fn process_node_scroll_area_request(
/// Return the resolved value of property for a given (pseudo)element.
/// <https://drafts.csswg.org/cssom/#resolved-value>
pub fn process_resolved_style_request<'dom>(
context: &LayoutContext,
context: &SharedStyleContext,
node: impl LayoutNode<'dom>,
pseudo: &Option<PseudoElement>,
property: &PropertyId,
@ -386,7 +246,7 @@ pub fn process_resolved_style_request<'dom>(
}
pub fn process_resolved_style_request_for_unstyled_node<'dom>(
context: &LayoutContext,
context: &SharedStyleContext,
node: impl LayoutNode<'dom>,
pseudo: &Option<PseudoElement>,
property: &PropertyId,
@ -398,7 +258,7 @@ pub fn process_resolved_style_request_for_unstyled_node<'dom>(
let mut tlc = ThreadLocalStyleContext::new();
let mut context = StyleContext {
shared: &context.style_context,
shared: context,
thread_local: &mut tlc,
};
@ -449,8 +309,7 @@ pub fn process_offset_parent_query(
node: OpaqueNode,
fragment_tree: Option<Arc<FragmentTree>>,
) -> OffsetParentResponse {
process_offset_parent_query_inner(node, fragment_tree)
.unwrap_or_else(OffsetParentResponse::empty)
process_offset_parent_query_inner(node, fragment_tree).unwrap_or_default()
}
#[inline]
@ -656,14 +515,13 @@ pub fn process_element_inner_text_query<'dom>(_node: impl LayoutNode<'dom>) -> S
"".to_owned()
}
pub fn process_text_index_request(_node: OpaqueNode, _point: Point2D<Au>) -> TextIndexResponse {
TextIndexResponse(None)
pub fn process_text_index_request(_node: OpaqueNode, _point: Point2D<Au>) -> Option<usize> {
None
}
pub fn process_resolved_font_style_query<'dom, E>(
context: &LayoutContext,
context: &SharedStyleContext,
node: E,
property: &PropertyId,
value: &str,
url_data: ServoUrl,
shared_lock: &SharedRwLock,
@ -673,14 +531,13 @@ where
{
fn create_font_declaration(
value: &str,
property: &PropertyId,
url_data: &ServoUrl,
quirks_mode: QuirksMode,
) -> Option<PropertyDeclarationBlock> {
let mut declarations = SourcePropertyDeclaration::default();
let result = parse_one_declaration_into(
&mut declarations,
property.clone(),
PropertyId::Shorthand(ShorthandId::Font),
value,
Origin::Author,
&UrlExtraData(url_data.get_arc()),
@ -724,8 +581,8 @@ where
// https://html.spec.whatwg.org/multipage/#dom-context-2d-font
// 1. Parse the given font property value
let quirks_mode = context.style_context.quirks_mode();
let declarations = create_font_declaration(value, property, &url_data, quirks_mode)?;
let quirks_mode = context.quirks_mode();
let declarations = create_font_declaration(value, &url_data, quirks_mode)?;
// TODO: Reject 'inherit' and 'initial' values for the font property.
@ -737,7 +594,7 @@ where
} else {
let mut tlc = ThreadLocalStyleContext::new();
let mut context = StyleContext {
shared: &context.style_context,
shared: context,
thread_local: &mut tlc,
};
let styles = resolve_style(&mut context, element, RuleInclusion::All, None, None);
@ -745,22 +602,13 @@ where
}
} else {
let default_declarations =
create_font_declaration("10px sans-serif", property, &url_data, quirks_mode).unwrap();
resolve_for_declarations::<E>(
&context.style_context,
None,
default_declarations,
shared_lock,
)
create_font_declaration("10px sans-serif", &url_data, quirks_mode).unwrap();
resolve_for_declarations::<E>(context, None, default_declarations, shared_lock)
};
// 3. Resolve the parsed value with resolved styles of the parent element
let computed_values = resolve_for_declarations::<E>(
&context.style_context,
Some(&*parent_style),
declarations,
shared_lock,
);
let computed_values =
resolve_for_declarations::<E>(context, Some(&*parent_style), declarations, shared_lock);
Some(computed_values.clone_font())
}