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

@ -13,12 +13,12 @@ use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::{Arc, Mutex};
use app_units::Au;
use embedder_traits::resources::{self, Resource};
use euclid::default::Size2D as UntypedSize2D;
use euclid::{Point2D, Rect, Scale, Size2D};
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D};
use euclid::{Point2D, Scale, Size2D, Vector2D};
use fnv::FnvHashMap;
use fxhash::FxHashMap;
use gfx::font_cache_thread::FontCacheThread;
@ -33,7 +33,7 @@ use layout::query::{
process_content_box_request, process_content_boxes_request, process_element_inner_text_query,
process_node_geometry_request, process_node_scroll_area_request,
process_node_scroll_id_request, process_offset_parent_query, process_resolved_font_style_query,
process_resolved_style_request, process_text_index_request, LayoutRPCImpl, LayoutThreadData,
process_resolved_style_request, process_text_index_request,
};
use layout::traversal::RecalcStyle;
use layout::{layout_debug, BoxTree, FragmentTree};
@ -51,10 +51,11 @@ use profile_traits::time::{
};
use script::layout_dom::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode};
use script_layout_interface::message::{
Msg, NodesFromPointQueryType, QueryMsg, ReflowComplete, ReflowGoal, ScriptReflow,
Msg, NodesFromPointQueryType, ReflowComplete, ReflowGoal, ScriptReflow,
};
use script_layout_interface::{
Layout, LayoutConfig, LayoutFactory, OffsetParentResponse, TrustedNodeAddress,
};
use script_layout_interface::rpc::{LayoutRPC, OffsetParentResponse, TextIndexResponse};
use script_layout_interface::{Layout, LayoutConfig, LayoutFactory};
use script_traits::{
ConstellationControlMsg, DrawAPaintImageResult, IFrameSizeMsg, LayoutControlMsg,
LayoutMsg as ConstellationMsg, PaintWorkletError, Painter, ScrollState, UntrustedNodeAddress,
@ -68,14 +69,15 @@ use style::animation::DocumentAnimationSet;
use style::context::{
QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
};
use style::dom::{TElement, TNode};
use style::dom::{OpaqueNode, TElement, TNode};
use style::driver;
use style::error_reporting::RustLogReporter;
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
use style::invalidation::element::restyle_hints::RestyleHint;
use style::media_queries::{Device, MediaList, MediaType};
use style::properties::style_structs::Font;
use style::properties::PropertyId;
use style::selector_parser::SnapshotMap;
use style::selector_parser::{PseudoElement, SnapshotMap};
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
use style::stylesheets::{
DocumentStyleSheet, Origin, Stylesheet, StylesheetInDocument, UrlExtraData,
@ -86,7 +88,8 @@ use style::traversal::DomTraversal;
use style::traversal_flags::TraversalFlags;
use style_traits::{CSSPixel, DevicePixel, SpeculativePainter};
use url::Url;
use webrender_api::{units, HitTestFlags};
use webrender_api::units::LayoutPixel;
use webrender_api::{units, ExternalScrollId, HitTestFlags};
/// Information needed by layout.
pub struct LayoutThread {
@ -143,11 +146,8 @@ pub struct LayoutThread {
/// constraints.
viewport_size: UntypedSize2D<Au>,
/// A mutex to allow for fast, read-only RPC of layout's internal data
/// structures, while still letting the LayoutThread modify them.
///
/// All the other elements of this struct are read-only.
rw_data: Arc<Mutex<LayoutThreadData>>,
/// Scroll offsets of nodes that scroll.
scroll_offsets: RefCell<HashMap<ExternalScrollId, Vector2D<f32, LayoutPixel>>>,
webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>,
@ -224,56 +224,6 @@ impl Drop for ScriptReflowResult {
}
}
/// The `LayoutThread` `rw_data` lock must remain locked until the first reflow,
/// as RPC calls don't make sense until then. Use this in combination with
/// `LayoutThread::lock_rw_data` and `LayoutThread::return_rw_data`.
pub enum RWGuard<'a> {
/// If the lock was previously held, from when the thread started.
Held(MutexGuard<'a, LayoutThreadData>),
/// If the lock was just used, and has been returned since there has been
/// a reflow already.
Used(MutexGuard<'a, LayoutThreadData>),
}
impl<'a> Deref for RWGuard<'a> {
type Target = LayoutThreadData;
fn deref(&self) -> &LayoutThreadData {
match *self {
RWGuard::Held(ref x) => &**x,
RWGuard::Used(ref x) => &**x,
}
}
}
impl<'a> DerefMut for RWGuard<'a> {
fn deref_mut(&mut self) -> &mut LayoutThreadData {
match *self {
RWGuard::Held(ref mut x) => &mut **x,
RWGuard::Used(ref mut x) => &mut **x,
}
}
}
struct RwData<'a, 'b: 'a> {
rw_data: &'b Arc<Mutex<LayoutThreadData>>,
possibly_locked_rw_data: &'a mut Option<MutexGuard<'b, LayoutThreadData>>,
}
impl<'a, 'b: 'a> RwData<'a, 'b> {
/// If no reflow has happened yet, this will just return the lock in
/// `possibly_locked_rw_data`. Otherwise, it will acquire the `rw_data` lock.
///
/// If you do not wish RPCs to remain blocked, just drop the `RWGuard`
/// returned from this function. If you _do_ wish for them to remain blocked,
/// use `block`.
fn lock(&mut self) -> RWGuard<'b> {
match self.possibly_locked_rw_data.take() {
None => RWGuard::Used(self.rw_data.lock().unwrap()),
Some(x) => RWGuard::Held(x),
}
}
}
impl Layout for LayoutThread {
fn process(&mut self, msg: script_layout_interface::message::Msg) {
self.handle_request(Request::FromScript(msg));
@ -287,10 +237,6 @@ impl Layout for LayoutThread {
self.handle_request(Request::FromFontCache);
}
fn rpc(&self) -> Box<dyn script_layout_interface::rpc::LayoutRPC> {
Box::new(LayoutRPCImpl(self.rw_data.clone())) as Box<dyn LayoutRPC>
}
fn waiting_for_web_fonts_to_load(&self) -> bool {
self.outstanding_web_fonts.load(Ordering::SeqCst) != 0
}
@ -330,6 +276,152 @@ impl Layout for LayoutThread {
self.stylist
.remove_stylesheet(DocumentStyleSheet(stylesheet.clone()), &guard);
}
fn query_content_box(&self, node: OpaqueNode) -> Option<UntypedRect<Au>> {
process_content_box_request(node, self.fragment_tree.borrow().clone())
}
fn query_content_boxes(&self, node: OpaqueNode) -> Vec<UntypedRect<Au>> {
process_content_boxes_request(node, self.fragment_tree.borrow().clone())
}
fn query_client_rect(&self, node: OpaqueNode) -> UntypedRect<i32> {
process_node_geometry_request(node, self.fragment_tree.borrow().clone())
}
fn query_element_inner_text(
&self,
node: script_layout_interface::TrustedNodeAddress,
) -> String {
let node = unsafe { ServoLayoutNode::<DOMLayoutData>::new(&node) };
process_element_inner_text_query(node)
}
fn query_inner_window_dimension(
&self,
_context: BrowsingContextId,
) -> Option<Size2D<f32, CSSPixel>> {
// TODO(jdm): port the iframe sizing code from layout2013's display
// builder in order to support query iframe sizing.
None
}
fn query_nodes_from_point(
&self,
point: UntypedPoint2D<f32>,
query_type: NodesFromPointQueryType,
) -> Vec<UntrustedNodeAddress> {
let mut flags = match query_type {
NodesFromPointQueryType::Topmost => HitTestFlags::empty(),
NodesFromPointQueryType::All => HitTestFlags::FIND_ALL,
};
// The point we get is not relative to the entire WebRender scene, but to this
// particular pipeline, so we need to tell WebRender about that.
flags.insert(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT);
let client_point = units::DevicePoint::from_untyped(point);
let results =
self.webrender_api
.hit_test(Some(self.id.to_webrender()), client_point, flags);
results.iter().map(|result| result.node).collect()
}
fn query_offset_parent(&self, node: OpaqueNode) -> OffsetParentResponse {
process_offset_parent_query(node, self.fragment_tree.borrow().clone())
}
fn query_resolved_style(
&self,
node: TrustedNodeAddress,
pseudo: Option<PseudoElement>,
property_id: PropertyId,
animations: DocumentAnimationSet,
animation_timeline_value: f64,
) -> String {
let node: ServoLayoutNode<DOMLayoutData> = unsafe { ServoLayoutNode::new(&node) };
let document = node.owner_doc();
let document_shared_lock = document.style_shared_lock();
let guards = StylesheetGuards {
author: &document_shared_lock.read(),
ua_or_user: &UA_STYLESHEETS.shared_lock.read(),
};
let snapshot_map = SnapshotMap::new();
let shared_style_context = self.build_shared_style_context(
guards,
&snapshot_map,
animation_timeline_value,
&animations,
TraversalFlags::empty(),
);
let fragment_tree = self.fragment_tree.borrow().clone();
process_resolved_style_request(
&shared_style_context,
node,
&pseudo,
&property_id,
fragment_tree,
)
}
fn query_resolved_font_style(
&self,
node: TrustedNodeAddress,
value: &str,
animations: DocumentAnimationSet,
animation_timeline_value: f64,
) -> Option<ServoArc<Font>> {
let node: ServoLayoutNode<DOMLayoutData> = unsafe { ServoLayoutNode::new(&node) };
let document = node.owner_doc();
let document_shared_lock = document.style_shared_lock();
let guards = StylesheetGuards {
author: &document_shared_lock.read(),
ua_or_user: &UA_STYLESHEETS.shared_lock.read(),
};
let snapshot_map = SnapshotMap::new();
let shared_style_context = self.build_shared_style_context(
guards,
&snapshot_map,
animation_timeline_value,
&animations,
TraversalFlags::empty(),
);
process_resolved_font_style_query(
&shared_style_context,
node,
value,
self.url.clone(),
document_shared_lock,
)
}
fn query_scroll_id(
&self,
node: script_layout_interface::TrustedNodeAddress,
) -> ExternalScrollId {
let node = unsafe { ServoLayoutNode::<DOMLayoutData>::new(&node) };
process_node_scroll_id_request(self.id, node)
}
fn query_scrolling_area(&self, node: Option<OpaqueNode>) -> UntypedRect<i32> {
process_node_scroll_area_request(node, self.fragment_tree.borrow().clone())
}
fn query_text_indext(
&self,
node: OpaqueNode,
point_in_node: UntypedPoint2D<f32>,
) -> Option<usize> {
let point_in_node = Point2D::new(
Au::from_f32_px(point_in_node.x),
Au::from_f32_px(point_in_node.y),
);
process_text_index_request(node, point_in_node)
}
}
enum Request {
@ -398,23 +490,8 @@ impl LayoutThread {
Au::from_f32_px(window_size.initial_viewport.height),
),
webrender_api: webrender_api_sender,
scroll_offsets: Default::default(),
stylist: Stylist::new(device, QuirksMode::NoQuirks),
rw_data: Arc::new(Mutex::new(LayoutThreadData {
display_list: None,
content_box_response: None,
content_boxes_response: Vec::new(),
client_rect_response: Rect::zero(),
scroll_id_response: None,
scrolling_area_response: Rect::zero(),
resolved_style_response: String::new(),
resolved_font_style_response: None,
offset_parent_response: OffsetParentResponse::empty(),
scroll_offsets: HashMap::new(),
text_index_response: TextIndexResponse(None),
nodes_from_point_response: vec![],
element_inner_text_response: String::new(),
inner_window_dimensions_response: None,
})),
webrender_image_cache: Default::default(),
paint_time_metrics,
last_iframe_sizes: Default::default(),
@ -422,6 +499,27 @@ impl LayoutThread {
}
}
fn build_shared_style_context<'a>(
&'a self,
guards: StylesheetGuards<'a>,
snapshot_map: &'a SnapshotMap,
animation_timeline_value: f64,
animations: &DocumentAnimationSet,
traversal_flags: TraversalFlags,
) -> SharedStyleContext<'a> {
SharedStyleContext {
stylist: &self.stylist,
options: GLOBAL_STYLE_DATA.options.clone(),
guards,
visited_styles_enabled: false,
animations: animations.clone(),
registered_speculative_painters: &self.registered_painters,
current_time_for_animations: animation_timeline_value,
traversal_flags,
snapshot_map,
}
}
// Create a layout context for use in building display lists, hit testing, &c.
fn build_layout_context<'a>(
&'a self,
@ -441,17 +539,13 @@ impl LayoutThread {
LayoutContext {
id: self.id,
origin,
style_context: SharedStyleContext {
stylist: &self.stylist,
options: GLOBAL_STYLE_DATA.options.clone(),
style_context: self.build_shared_style_context(
guards,
visited_styles_enabled: false,
animations: animations.clone(),
registered_speculative_painters: &self.registered_painters,
current_time_for_animations: animation_timeline_value,
traversal_flags,
snapshot_map,
},
animation_timeline_value,
animations,
traversal_flags,
),
image_cache: self.image_cache.clone(),
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
webrender_image_cache: self.webrender_image_cache.clone(),
@ -462,26 +556,18 @@ impl LayoutThread {
/// Receives and dispatches messages from the script and constellation threads
fn handle_request<'a, 'b>(&mut self, request: Request) {
let rw_data = self.rw_data.clone();
let mut possibly_locked_rw_data = Some(rw_data.lock().unwrap());
let mut rw_data = RwData {
rw_data: &rw_data,
possibly_locked_rw_data: &mut possibly_locked_rw_data,
};
match request {
Request::FromPipeline(LayoutControlMsg::SetScrollStates(new_scroll_states)) => {
self.handle_request_helper(Msg::SetScrollStates(new_scroll_states), &mut rw_data)
self.handle_request_helper(Msg::SetScrollStates(new_scroll_states))
},
Request::FromPipeline(LayoutControlMsg::ExitNow) => {
self.handle_request_helper(Msg::ExitNow, &mut rw_data);
self.handle_request_helper(Msg::ExitNow);
},
Request::FromPipeline(LayoutControlMsg::PaintMetric(epoch, paint_time)) => {
self.paint_time_metrics.maybe_set_metric(epoch, paint_time);
},
Request::FromScript(msg) => self.handle_request_helper(msg, &mut rw_data),
Request::FromScript(msg) => self.handle_request_helper(msg),
Request::FromFontCache => {
let _rw_data = rw_data.lock();
self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst);
self.handle_web_font_loaded();
},
@ -489,32 +575,23 @@ impl LayoutThread {
}
/// Receives and dispatches messages from other threads.
fn handle_request_helper(
&mut self,
request: Msg,
possibly_locked_rw_data: &mut RwData<'_, '_>,
) {
fn handle_request_helper(&mut self, request: Msg) {
match request {
Msg::SetQuirksMode(mode) => self.handle_set_quirks_mode(mode),
Msg::GetRPC(response_chan) => {
response_chan
.send(Box::new(LayoutRPCImpl(self.rw_data.clone())) as Box<dyn LayoutRPC + Send>)
.unwrap();
},
Msg::Reflow(data) => {
let mut data = ScriptReflowResult::new(data);
profile(
profile_time::ProfilerCategory::LayoutPerform,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| self.handle_reflow(&mut data, possibly_locked_rw_data),
|| self.handle_reflow(&mut data),
);
},
Msg::SetScrollStates(new_scroll_states) => {
self.set_scroll_states(new_scroll_states, possibly_locked_rw_data);
self.set_scroll_states(new_scroll_states);
},
Msg::CollectReports(reports_chan) => {
self.collect_reports(reports_chan, possibly_locked_rw_data);
self.collect_reports(reports_chan);
},
Msg::RegisterPaint(_name, _properties, _painter) => {},
// Receiving the Exit message at this stage only happens when layout is undergoing a "force exit".
@ -522,24 +599,18 @@ impl LayoutThread {
}
}
fn collect_reports(
&self,
reports_chan: ReportsChan,
possibly_locked_rw_data: &mut RwData<'_, '_>,
) {
fn collect_reports(&self, reports_chan: ReportsChan) {
let mut reports = vec![];
// Servo uses vanilla jemalloc, which doesn't have a
// malloc_enclosing_size_of function.
let mut ops = MallocSizeOfOps::new(servo_allocator::usable_size, None, None);
// FIXME(njn): Just measuring the display tree for now.
let rw_data = possibly_locked_rw_data.lock();
let display_list = rw_data.display_list.as_ref();
let formatted_url = &format!("url({})", self.url);
reports.push(Report {
path: path![formatted_url, "layout-thread", "display-list"],
kind: ReportKind::ExplicitJemallocHeapSize,
size: display_list.map_or(0, |sc| sc.size_of(&mut ops)),
size: 0,
});
reports.push(Report {
@ -590,71 +661,12 @@ impl LayoutThread {
}
/// The high-level routine that performs layout.
fn handle_reflow(
&mut self,
data: &mut ScriptReflowResult,
possibly_locked_rw_data: &mut RwData<'_, '_>,
) {
fn handle_reflow(&mut self, data: &mut ScriptReflowResult) {
let document = unsafe { ServoLayoutNode::<DOMLayoutData>::new(&data.document) };
let document = document.as_document().unwrap();
let mut rw_data = possibly_locked_rw_data.lock();
let root_element = match document.root_element() {
None => {
// Since we cannot compute anything, give spec-required placeholders.
debug!("layout: No root node: bailing");
match data.reflow_goal {
ReflowGoal::LayoutQuery(ref query_msg, _) => match query_msg {
&QueryMsg::ContentBoxQuery(_) => {
rw_data.content_box_response = None;
},
&QueryMsg::ContentBoxesQuery(_) => {
rw_data.content_boxes_response = Vec::new();
},
&QueryMsg::NodesFromPointQuery(..) => {
rw_data.nodes_from_point_response = Vec::new();
},
&QueryMsg::ClientRectQuery(_) => {
rw_data.client_rect_response = Rect::zero();
},
&QueryMsg::ScrollingAreaQuery(_) => {
rw_data.scrolling_area_response = Rect::zero();
},
&QueryMsg::NodeScrollIdQuery(_) => {
rw_data.scroll_id_response = None;
},
&QueryMsg::ResolvedStyleQuery(_, _, _) => {
rw_data.resolved_style_response = String::new();
},
&QueryMsg::ResolvedFontStyleQuery(_, _, _) => {
rw_data.resolved_font_style_response = None;
},
&QueryMsg::OffsetParentQuery(_) => {
rw_data.offset_parent_response = OffsetParentResponse::empty();
},
&QueryMsg::StyleQuery => {},
&QueryMsg::TextIndexQuery(..) => {
rw_data.text_index_response = TextIndexResponse(None);
},
&QueryMsg::ElementInnerTextQuery(_) => {
rw_data.element_inner_text_response = String::new();
},
&QueryMsg::InnerWindowDimensionsQuery(browsing_context_id) => {
rw_data.inner_window_dimensions_response = self
.last_iframe_sizes
.borrow()
.get(&browsing_context_id)
.cloned();
},
},
ReflowGoal::Full |
ReflowGoal::TickAnimations |
ReflowGoal::UpdateScrollNode(_) => {},
}
return;
},
Some(x) => x,
let Some(root_element) = document.root_element() else {
debug!("layout: No root node: bailing");
return;
};
// Calculate the actual viewport as per DEVICE-ADAPT § 6
@ -850,124 +862,18 @@ impl LayoutThread {
}
self.first_reflow.set(false);
self.respond_to_query_if_necessary(
&data.reflow_goal,
&mut *rw_data,
&mut layout_context,
data.result.borrow_mut().as_mut().unwrap(),
document_shared_lock,
);
}
fn respond_to_query_if_necessary(
&self,
reflow_goal: &ReflowGoal,
rw_data: &mut LayoutThreadData,
context: &mut LayoutContext,
reflow_result: &mut ReflowComplete,
shared_lock: &SharedRwLock,
) {
reflow_result.pending_images = std::mem::take(&mut *context.pending_images.lock().unwrap());
match *reflow_goal {
ReflowGoal::LayoutQuery(ref querymsg, _) => match querymsg {
&QueryMsg::ContentBoxQuery(node) => {
rw_data.content_box_response =
process_content_box_request(node, self.fragment_tree.borrow().clone());
},
&QueryMsg::ContentBoxesQuery(node) => {
rw_data.content_boxes_response =
process_content_boxes_request(node, self.fragment_tree.borrow().clone());
},
&QueryMsg::TextIndexQuery(node, point_in_node) => {
let point_in_node = Point2D::new(
Au::from_f32_px(point_in_node.x),
Au::from_f32_px(point_in_node.y),
);
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, self.fragment_tree.borrow().clone());
},
&QueryMsg::ScrollingAreaQuery(node) => {
rw_data.scrolling_area_response =
process_node_scroll_area_request(node, self.fragment_tree.borrow().clone());
},
&QueryMsg::NodeScrollIdQuery(node) => {
let node = unsafe { ServoLayoutNode::<DOMLayoutData>::new(&node) };
rw_data.scroll_id_response =
Some(process_node_scroll_id_request(self.id, node));
},
&QueryMsg::ResolvedStyleQuery(node, ref pseudo, ref property) => {
let node = unsafe { ServoLayoutNode::<DOMLayoutData>::new(&node) };
let fragment_tree = self.fragment_tree.borrow().clone();
rw_data.resolved_style_response = process_resolved_style_request(
context,
node,
pseudo,
property,
fragment_tree,
);
},
&QueryMsg::ResolvedFontStyleQuery(node, ref property, ref value) => {
let node = unsafe { ServoLayoutNode::<DOMLayoutData>::new(&node) };
rw_data.resolved_font_style_response = process_resolved_font_style_query(
context,
node,
property,
value,
self.url.clone(),
shared_lock,
);
},
&QueryMsg::OffsetParentQuery(node) => {
rw_data.offset_parent_response =
process_offset_parent_query(node, self.fragment_tree.borrow().clone());
},
&QueryMsg::StyleQuery => {},
&QueryMsg::NodesFromPointQuery(client_point, ref reflow_goal) => {
let mut flags = match reflow_goal {
&NodesFromPointQueryType::Topmost => HitTestFlags::empty(),
&NodesFromPointQueryType::All => HitTestFlags::FIND_ALL,
};
// The point we get is not relative to the entire WebRender scene, but to this
// particular pipeline, so we need to tell WebRender about that.
flags.insert(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT);
let client_point = units::DevicePoint::from_untyped(client_point);
let results = self.webrender_api.hit_test(
Some(self.id.to_webrender()),
client_point,
flags,
);
rw_data.nodes_from_point_response =
results.iter().map(|result| result.node).collect()
},
&QueryMsg::ElementInnerTextQuery(node) => {
let node = unsafe { ServoLayoutNode::<DOMLayoutData>::new(&node) };
rw_data.element_inner_text_response = process_element_inner_text_query(node);
},
&QueryMsg::InnerWindowDimensionsQuery(_browsing_context_id) => {
// TODO(jdm): port the iframe sizing code from layout2013's display
// builder in order to support query iframe sizing.
rw_data.inner_window_dimensions_response = None;
},
},
ReflowGoal::UpdateScrollNode(scroll_state) => {
self.update_scroll_node_state(&scroll_state, rw_data);
},
ReflowGoal::Full | ReflowGoal::TickAnimations => {},
data.result.borrow_mut().as_mut().unwrap().pending_images =
std::mem::take(&mut *layout_context.pending_images.lock().unwrap());
if let ReflowGoal::UpdateScrollNode(scroll_state) = data.reflow_goal {
self.update_scroll_node_state(&scroll_state);
}
}
fn update_scroll_node_state(&self, state: &ScrollState, rw_data: &mut LayoutThreadData) {
rw_data
.scroll_offsets
fn update_scroll_node_state(&self, state: &ScrollState) {
self.scroll_offsets
.borrow_mut()
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
self.id.to_webrender(),
@ -976,12 +882,7 @@ impl LayoutThread {
);
}
fn set_scroll_states(
&mut self,
new_scroll_states: Vec<ScrollState>,
possibly_locked_rw_data: &mut RwData<'_, '_>,
) {
let mut rw_data = possibly_locked_rw_data.lock();
fn set_scroll_states(&mut self, new_scroll_states: Vec<ScrollState>) {
let mut script_scroll_states = vec![];
let mut layout_scroll_states = HashMap::new();
for new_state in &new_scroll_states {
@ -1000,7 +901,7 @@ impl LayoutThread {
self.id,
script_scroll_states,
));
rw_data.scroll_offsets = layout_scroll_states
self.scroll_offsets = RefCell::new(layout_scroll_states);
}
fn perform_post_style_recalc_layout_passes(