layout: Only create a LayoutContext if restyling (#37726)

The creation of `LayoutContext` does more work than necessary if layout
just needs to do something like make a display list and not restyle and
relayout. This change makes it so that these kind of non-restyle layouts
do not need to create a display list. In addition, the creation of
`LayoutContext` is better encapsulate

Testing: This should not change observable behavior and is thus covered
by existing WPT tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2025-06-27 17:01:30 +02:00 committed by GitHub
parent 5e44582277
commit 9232b0f550
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 208 additions and 196 deletions

View file

@ -4,7 +4,6 @@
use std::sync::Arc;
use base::id::PipelineId;
use euclid::Size2D;
use fnv::FnvHashMap;
use fonts::FontContext;
@ -26,10 +25,8 @@ use webrender_api::units::{DeviceIntSize, DeviceSize};
pub(crate) type CachedImageOrError = Result<CachedImage, ResolveImageError>;
pub struct LayoutContext<'a> {
pub id: PipelineId,
pub(crate) struct LayoutContext<'a> {
pub use_rayon: bool,
pub origin: ImmutableOrigin,
/// Bits shared by the layout and style system.
pub style_context: SharedStyleContext<'a>,
@ -37,31 +34,12 @@ pub struct LayoutContext<'a> {
/// A FontContext to be used during layout.
pub font_context: Arc<FontContext>,
/// Reference to the script thread image cache.
pub image_cache: Arc<dyn ImageCache>,
/// A list of in-progress image loads to be shared with the script thread.
pub pending_images: Mutex<Vec<PendingImage>>,
/// A list of fully loaded vector images that need to be rasterized to a specific
/// size determined by layout. This will be shared with the script thread.
pub pending_rasterization_images: Mutex<Vec<PendingRasterizationImage>>,
/// A collection of `<iframe>` sizes to send back to script.
pub iframe_sizes: Mutex<IFrameSizes>,
// A cache that maps image resources used in CSS (e.g as the `url()` value
// for `background-image` or `content` property) to the final resolved image data.
pub resolved_images_cache:
Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), CachedImageOrError>>>,
/// A shared reference to script's map of DOM nodes with animated images. This is used
/// to manage image animations in script and inform the script about newly animating
/// nodes.
pub node_to_animating_image_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
/// The DOM node that is highlighted by the devtools inspector, if any
pub highlighted_dom_node: Option<OpaqueNode>,
/// An [`ImageResolver`] used for resolving images during box and fragment
/// tree construction. Later passed to display list construction.
pub image_resolver: Arc<ImageResolver>,
}
pub enum ResolvedImage<'a> {
@ -74,15 +52,6 @@ pub enum ResolvedImage<'a> {
},
}
impl Drop for LayoutContext<'_> {
fn drop(&mut self) {
if !std::thread::panicking() {
assert!(self.pending_images.lock().is_empty());
assert!(self.pending_rasterization_images.lock().is_empty());
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum ResolveImageError {
LoadError,
@ -103,12 +72,44 @@ pub(crate) enum LayoutImageCacheResult {
LoadError,
}
impl LayoutContext<'_> {
#[inline(always)]
pub fn shared_context(&self) -> &SharedStyleContext {
&self.style_context
pub(crate) struct ImageResolver {
/// The origin of the `Document` that this [`ImageResolver`] resolves images for.
pub origin: ImmutableOrigin,
/// Reference to the script thread image cache.
pub image_cache: Arc<dyn ImageCache>,
/// A list of in-progress image loads to be shared with the script thread.
pub pending_images: Mutex<Vec<PendingImage>>,
/// A list of fully loaded vector images that need to be rasterized to a specific
/// size determined by layout. This will be shared with the script thread.
pub pending_rasterization_images: Mutex<Vec<PendingRasterizationImage>>,
/// A shared reference to script's map of DOM nodes with animated images. This is used
/// to manage image animations in script and inform the script about newly animating
/// nodes.
pub node_to_animating_image_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
// A cache that maps image resources used in CSS (e.g as the `url()` value
// for `background-image` or `content` property) to the final resolved image data.
pub resolved_images_cache:
Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), CachedImageOrError>>>,
/// The current animation timeline value used to properly initialize animating images.
pub animation_timeline_value: f64,
}
impl Drop for ImageResolver {
fn drop(&mut self) {
if !std::thread::panicking() {
assert!(self.pending_images.lock().is_empty());
assert!(self.pending_rasterization_images.lock().is_empty());
}
}
}
impl ImageResolver {
pub(crate) fn get_or_request_image_or_meta(
&self,
node: OpaqueNode,
@ -155,18 +156,14 @@ impl LayoutContext<'_> {
}
}
pub fn handle_animated_image(&self, node: OpaqueNode, image: Arc<RasterImage>) {
pub(crate) fn handle_animated_image(&self, node: OpaqueNode, image: Arc<RasterImage>) {
let mut map = self.node_to_animating_image_map.write();
if !image.should_animate() {
map.remove(&node);
return;
}
let new_image_animation_state = || {
ImageAnimationState::new(
image.clone(),
self.shared_context().current_time_for_animations,
)
};
let new_image_animation_state =
|| ImageAnimationState::new(image.clone(), self.animation_timeline_value);
let entry = map.entry(node).or_insert_with(new_image_animation_state);
@ -218,7 +215,7 @@ impl LayoutContext<'_> {
}
}
pub fn rasterize_vector_image(
pub(crate) fn rasterize_vector_image(
&self,
image_id: PendingImageId,
size: DeviceIntSize,
@ -237,7 +234,7 @@ impl LayoutContext<'_> {
result
}
pub fn resolve_image<'a>(
pub(crate) fn resolve_image<'a>(
&self,
node: Option<OpaqueNode>,
image: &'a Image,

View file

@ -11,9 +11,10 @@ use base::id::ScrollTreeNodeId;
use clip::{Clip, ClipId};
use compositing_traits::display_list::{CompositorDisplayListInfo, SpatialTreeNodeInfo};
use embedder_traits::Cursor;
use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit, Vector2D};
use euclid::{Point2D, Scale, SideOffsets2D, Size2D, UnknownUnit, Vector2D};
use fonts::GlyphStore;
use gradient::WebRenderGradient;
use layout_api::ReflowRequest;
use net_traits::image_cache::Image as CachedImage;
use range::Range as ServoRange;
use servo_arc::Arc as ServoArc;
@ -37,7 +38,7 @@ use style::values::generics::NonNegative;
use style::values::generics::rect::Rect;
use style::values::specified::text::TextDecorationLine;
use style::values::specified::ui::CursorKind;
use style_traits::CSSPixel;
use style_traits::{CSSPixel as StyloCSSPixel, DevicePixel as StyloDevicePixel};
use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutRect, LayoutSize};
use webrender_api::{
self as wr, BorderDetails, BoxShadowClipMode, BuiltDisplayList, ClipChainId, ClipMode,
@ -47,7 +48,7 @@ use webrender_api::{
use wr::units::LayoutVector2D;
use crate::cell::ArcRefCell;
use crate::context::{LayoutContext, ResolvedImage};
use crate::context::{ImageResolver, ResolvedImage};
pub use crate::display_list::conversions::ToWebRender;
use crate::display_list::stacking_context::StackingContextSection;
use crate::fragment_tree::{
@ -92,10 +93,6 @@ pub(crate) struct DisplayListBuilder<'a> {
/// list building functions.
current_clip_id: ClipId,
/// A [LayoutContext] used to get information about the device pixel ratio
/// and get handles to WebRender images.
pub context: &'a LayoutContext<'a>,
/// The [`wr::DisplayListBuilder`] for this Servo [`DisplayListBuilder`].
pub webrender_display_list_builder: &'a mut wr::DisplayListBuilder,
@ -116,6 +113,12 @@ pub(crate) struct DisplayListBuilder<'a> {
/// A mapping from [`ClipId`] To WebRender [`ClipChainId`] used when building this WebRender
/// display list.
clip_map: Vec<ClipChainId>,
/// An [`ImageResolver`] to use during display list construction.
image_resolver: Arc<ImageResolver>,
/// The device pixel ratio used for this `Document`'s display list.
device_pixel_ratio: Scale<f32, StyloCSSPixel, StyloDevicePixel>,
}
struct InspectorHighlight {
@ -132,7 +135,7 @@ struct InspectorHighlight {
struct HighlightTraversalState {
/// The smallest rectangle that fully encloses all fragments created by the highlighted
/// dom node, if any.
content_box: euclid::Rect<Au, CSSPixel>,
content_box: euclid::Rect<Au, StyloCSSPixel>,
spatial_id: SpatialId,
@ -154,9 +157,11 @@ impl InspectorHighlight {
impl DisplayListBuilder<'_> {
pub(crate) fn build(
context: &LayoutContext,
reflow_request: &ReflowRequest,
stacking_context_tree: &mut StackingContextTree,
fragment_tree: &FragmentTree,
image_resolver: Arc<ImageResolver>,
device_pixel_ratio: Scale<f32, StyloCSSPixel, StyloDevicePixel>,
debug: &DebugOptions,
) -> BuiltDisplayList {
// Build the rest of the display list which inclues all of the WebRender primitives.
@ -182,14 +187,15 @@ impl DisplayListBuilder<'_> {
current_scroll_node_id: compositor_info.root_reference_frame_id,
current_reference_frame_scroll_node_id: compositor_info.root_reference_frame_id,
current_clip_id: ClipId::INVALID,
context,
webrender_display_list_builder: &mut webrender_display_list_builder,
compositor_info,
inspector_highlight: context
inspector_highlight: reflow_request
.highlighted_dom_node
.map(InspectorHighlight::for_node),
paint_body_background: true,
clip_map: Default::default(),
image_resolver,
device_pixel_ratio,
};
builder.add_all_spatial_nodes();
@ -756,7 +762,7 @@ impl Fragment {
let color = parent_style.clone_color();
let font_metrics = &fragment.font_metrics;
let dppx = builder.context.style_context.device_pixel_ratio().get();
let dppx = builder.device_pixel_ratio.get();
let common = builder.common_properties(rect.to_webrender(), &parent_style);
// Shadows. According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front to
@ -1232,7 +1238,7 @@ impl<'a> BuilderForBoxFragment<'a> {
let node = self.fragment.base.tag.map(|tag| tag.node);
// Reverse because the property is top layer first, we want to paint bottom layer first.
for (index, image) in b.background_image.0.iter().enumerate().rev() {
match builder.context.resolve_image(node, image) {
match builder.image_resolver.resolve_image(node, image) {
Err(_) => {},
Ok(ResolvedImage::Gradient(gradient)) => {
let intrinsic = NaturalSizes::empty();
@ -1279,7 +1285,7 @@ impl<'a> BuilderForBoxFragment<'a> {
let image_wr_key = match image {
CachedImage::Raster(raster_image) => raster_image.id,
CachedImage::Vector(vector_image) => {
let scale = builder.context.shared_context().device_pixel_ratio().0;
let scale = builder.device_pixel_ratio.get();
let default_size: DeviceIntSize =
Size2D::new(size.width * scale, size.height * scale).to_i32();
let layer_size = layer.as_ref().map(|layer| {
@ -1292,9 +1298,11 @@ impl<'a> BuilderForBoxFragment<'a> {
node.and_then(|node| {
let size = layer_size.unwrap_or(default_size);
builder
.context
.rasterize_vector_image(vector_image.id, size, node)
builder.image_resolver.rasterize_vector_image(
vector_image.id,
size,
node,
)
})
.and_then(|rasterized_image| rasterized_image.id)
},
@ -1476,7 +1484,7 @@ impl<'a> BuilderForBoxFragment<'a> {
let mut height = border_image_size.height;
let node = self.fragment.base.tag.map(|tag| tag.node);
let source = match builder
.context
.image_resolver
.resolve_image(node, &border.border_image_source)
{
Err(_) => return false,

View file

@ -71,7 +71,7 @@ impl<'dom> NodeAndStyleInfo<'dom> {
.to_threadsafe()
.as_element()?
.with_pseudo(pseudo_element_type)?
.style(context.shared_context());
.style(&context.style_context);
Some(NodeAndStyleInfo {
node: self.node,
pseudo_element_type: Some(pseudo_element_type),
@ -200,10 +200,8 @@ fn traverse_children_of<'dom>(
traverse_eager_pseudo_element(PseudoElement::Before, parent_element, context, handler);
if parent_element.is_text_input() {
let info = NodeAndStyleInfo::new(
parent_element,
parent_element.style(context.shared_context()),
);
let info =
NodeAndStyleInfo::new(parent_element, parent_element.style(&context.style_context));
let node_text_content = parent_element.to_threadsafe().node_text_content();
if node_text_content.is_empty() {
// The addition of zero-width space here forces the text input to have an inline formatting
@ -220,7 +218,7 @@ fn traverse_children_of<'dom>(
} else {
for child in iter_child_nodes(parent_element) {
if child.is_text_node() {
let info = NodeAndStyleInfo::new(child, child.style(context.shared_context()));
let info = NodeAndStyleInfo::new(child, child.style(&context.style_context));
handler.handle_text(&info, child.to_threadsafe().node_text_content());
} else if child.is_element() {
traverse_element(child, context, handler);
@ -241,7 +239,7 @@ fn traverse_element<'dom>(
element.unset_pseudo_element_box(PseudoElement::Marker);
let replaced = ReplacedContents::for_element(element, context);
let style = element.style(context.shared_context());
let style = element.style(&context.style_context);
match Display::from(style.get_box().display) {
Display::None => element.unset_all_boxes(),
Display::Contents => {
@ -300,7 +298,7 @@ fn traverse_eager_pseudo_element<'dom>(
return;
};
let style = pseudo_element.style(context.shared_context());
let style = pseudo_element.style(&context.style_context);
if style.ineffective_content_property() {
return;
}

View file

@ -45,7 +45,7 @@ pub struct BoxTree {
}
impl BoxTree {
pub fn construct(context: &LayoutContext, root_element: ServoLayoutNode<'_>) -> Self {
pub(crate) fn construct(context: &LayoutContext, root_element: ServoLayoutNode<'_>) -> Self {
let boxes = construct_for_root_element(context, root_element);
// Zero box for `:root { display: none }`, one for the root element otherwise.
@ -59,7 +59,7 @@ impl BoxTree {
// > none, user agents must instead apply the overflow-* values of the first such child
// > element to the viewport. The element from which the value is propagated must then have a
// > used overflow value of visible.
let root_style = root_element.style(context.shared_context());
let root_style = root_element.style(&context.style_context);
let mut viewport_overflow_x = root_style.clone_overflow_x();
let mut viewport_overflow_y = root_style.clone_overflow_y();
@ -76,7 +76,7 @@ impl BoxTree {
continue;
}
let style = child.style(context.shared_context());
let style = child.style(&context.style_context);
if !style.get_box().display.is_none() {
viewport_overflow_x = style.clone_overflow_x();
viewport_overflow_y = style.clone_overflow_y();
@ -123,7 +123,10 @@ impl BoxTree {
/// * how intrinsic content sizes are computed eagerly makes it hard
/// to update those sizes for ancestors of the node from which we
/// made an incremental update.
pub fn update(context: &LayoutContext, dirty_root_from_script: ServoLayoutNode<'_>) -> bool {
pub(crate) fn update(
context: &LayoutContext,
dirty_root_from_script: ServoLayoutNode<'_>,
) -> bool {
let Some(box_tree_update) = IncrementalBoxTreeUpdate::find(dirty_root_from_script) else {
return false;
};
@ -136,7 +139,7 @@ fn construct_for_root_element(
context: &LayoutContext,
root_element: ServoLayoutNode<'_>,
) -> Vec<ArcRefCell<BlockLevelBox>> {
let info = NodeAndStyleInfo::new(root_element, root_element.style(context.shared_context()));
let info = NodeAndStyleInfo::new(root_element, root_element.style(&context.style_context));
let box_style = info.style.get_box();
let display_inside = match Display::from(box_style.display) {
@ -188,7 +191,7 @@ fn construct_for_root_element(
}
impl BoxTree {
pub fn layout(
pub(crate) fn layout(
&self,
layout_context: &LayoutContext,
viewport: UntypedSize2D<Au>,

View file

@ -111,10 +111,10 @@ impl IndependentFormattingContext {
},
DisplayInside::Table => {
let table_grid_style = context
.shared_context()
.style_context
.stylist
.style_for_anonymous::<ServoLayoutElement>(
&context.shared_context().guards,
&context.style_context.guards,
&PseudoElement::ServoTableGrid,
&node_and_style_info.style,
);

View file

@ -59,6 +59,7 @@ impl FragmentTree {
let mut animations = layout_context.style_context.animations.sets.write();
let mut invalid_animating_nodes: FxHashSet<_> = animations.keys().cloned().collect();
let mut image_animations = layout_context
.image_resolver
.node_to_animating_image_map
.write()
.to_owned();

View file

@ -26,8 +26,9 @@ use fonts_traits::StylesheetWebFontLoadFinishedCallback;
use fxhash::FxHashMap;
use ipc_channel::ipc::IpcSender;
use layout_api::{
Layout, LayoutConfig, LayoutFactory, NodesFromPointQueryType, OffsetParentResponse, QueryMsg,
ReflowGoal, ReflowRequest, ReflowRequestRestyle, ReflowResult, TrustedNodeAddress,
IFrameSizes, Layout, LayoutConfig, LayoutFactory, NodesFromPointQueryType,
OffsetParentResponse, QueryMsg, ReflowGoal, ReflowRequest, ReflowRequestRestyle, ReflowResult,
TrustedNodeAddress,
};
use log::{debug, error, warn};
use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
@ -38,7 +39,6 @@ use profile_traits::time::{
self as profile_time, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType,
};
use profile_traits::{path, time_profile};
use rayon::ThreadPool;
use script::layout_dom::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode};
use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter, ScriptThreadMessage};
use servo_arc::Arc as ServoArc;
@ -78,7 +78,7 @@ use url::Url;
use webrender_api::units::{DevicePixel, DevicePoint, LayoutSize, LayoutVector2D};
use webrender_api::{ExternalScrollId, HitTestFlags};
use crate::context::{CachedImageOrError, LayoutContext};
use crate::context::{CachedImageOrError, ImageResolver, LayoutContext};
use crate::display_list::{DisplayListBuilder, StackingContextTree};
use crate::query::{
get_the_text_steps, process_client_rect_request, process_content_box_request,
@ -655,109 +655,34 @@ impl LayoutThread {
return None;
};
let document_shared_lock = document.style_shared_lock();
let author_guard = document_shared_lock.read();
let ua_stylesheets = &*UA_STYLESHEETS;
let ua_or_user_guard = ua_stylesheets.shared_lock.read();
let rayon_pool = STYLE_THREAD_POOL.lock();
let rayon_pool = rayon_pool.pool();
let rayon_pool = rayon_pool.as_ref();
let guards = StylesheetGuards {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
let mut snapshot_map = SnapshotMap::new();
let mut _snapshot_setter = None;
let mut viewport_changed = false;
if let Some(restyle) = reflow_request.restyle.as_mut() {
_snapshot_setter = Some(SnapshotSetter::new(restyle, &mut snapshot_map));
viewport_changed = self.viewport_did_change(reflow_request.viewport_details);
if self.update_device_if_necessary(&reflow_request, viewport_changed, &guards) {
if let Some(mut data) = root_element.mutate_data() {
data.hint.insert(RestyleHint::recascade_subtree());
}
}
self.prepare_stylist_for_reflow(
&reflow_request,
document,
root_element,
&guards,
ua_stylesheets,
&snapshot_map,
);
if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
// Need to manually force layout to build a new display list regardless of whether the box tree
// changed or not.
self.need_new_display_list.set(true);
}
}
let mut layout_context = LayoutContext {
id: self.id,
let image_resolver = Arc::new(ImageResolver {
origin: reflow_request.origin.clone(),
style_context: self.build_shared_style_context(
guards,
&snapshot_map,
reflow_request.animation_timeline_value,
&reflow_request.animations,
match reflow_request.stylesheets_changed() {
true => TraversalFlags::ForCSSRuleChanges,
false => TraversalFlags::empty(),
},
),
image_cache: self.image_cache.clone(),
font_context: self.font_context.clone(),
resolved_images_cache: self.resolved_images_cache.clone(),
pending_images: Mutex::default(),
pending_rasterization_images: Mutex::default(),
node_to_animating_image_map: reflow_request.node_to_animating_image_map.clone(),
iframe_sizes: Mutex::default(),
use_rayon: rayon_pool.is_some(),
highlighted_dom_node: reflow_request.highlighted_dom_node,
};
animation_timeline_value: reflow_request.animation_timeline_value,
});
let mut damage = RestyleDamage::empty();
if let Some(restyle) = reflow_request.restyle.as_ref() {
damage = self.restyle_and_build_trees(
restyle,
let (damage, iframe_sizes) = self.restyle_and_build_trees(
&mut reflow_request,
document,
root_element,
rayon_pool,
&mut layout_context,
viewport_changed,
&image_resolver,
);
self.calculate_overflow(damage);
};
self.build_stacking_context_tree(&reflow_request, damage);
let built_display_list =
self.build_display_list(&reflow_request, damage, &mut layout_context);
let built_display_list = self.build_display_list(&reflow_request, damage, &image_resolver);
if let ReflowGoal::UpdateScrollNode(external_scroll_id, offset) = reflow_request.reflow_goal
{
self.set_scroll_offset_from_script(external_scroll_id, offset);
}
if self.debug.dump_scroll_tree {
// Print the [ScrollTree], this is done after display list build so we have
// the information about webrender id. Whether a scroll tree is initialized
// or not depends on the reflow goal.
if let Some(tree) = self.stacking_context_tree.borrow().as_ref() {
tree.compositor_info.scroll_tree.debug_print();
} else {
println!(
"Scroll Tree -- reflow {:?}: scroll tree is not initialized yet.",
reflow_request.reflow_goal
);
}
}
let pending_images = std::mem::take(&mut *layout_context.pending_images.lock());
let pending_images = std::mem::take(&mut *image_resolver.pending_images.lock());
let pending_rasterization_images =
std::mem::take(&mut *layout_context.pending_rasterization_images.lock());
let iframe_sizes = std::mem::take(&mut *layout_context.iframe_sizes.lock());
std::mem::take(&mut *image_resolver.pending_rasterization_images.lock());
Some(ReflowResult {
built_display_list,
@ -829,20 +754,80 @@ impl LayoutThread {
#[servo_tracing::instrument(skip_all)]
fn restyle_and_build_trees(
&self,
restyle: &ReflowRequestRestyle,
&mut self,
reflow_request: &mut ReflowRequest,
document: ServoLayoutDocument<'_>,
root_element: ServoLayoutElement<'_>,
rayon_pool: Option<&ThreadPool>,
layout_context: &mut LayoutContext<'_>,
viewport_changed: bool,
) -> RestyleDamage {
image_resolver: &Arc<ImageResolver>,
) -> (RestyleDamage, IFrameSizes) {
let mut snapshot_map = SnapshotMap::new();
let _snapshot_setter = match reflow_request.restyle.as_mut() {
Some(restyle) => SnapshotSetter::new(restyle, &mut snapshot_map),
None => return (RestyleDamage::empty(), IFrameSizes::default()),
};
let document_shared_lock = document.style_shared_lock();
let author_guard = document_shared_lock.read();
let ua_stylesheets = &*UA_STYLESHEETS;
let ua_or_user_guard = ua_stylesheets.shared_lock.read();
let rayon_pool = STYLE_THREAD_POOL.lock();
let rayon_pool = rayon_pool.pool();
let rayon_pool = rayon_pool.as_ref();
let guards = StylesheetGuards {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
let viewport_changed = self.viewport_did_change(reflow_request.viewport_details);
if self.update_device_if_necessary(reflow_request, viewport_changed, &guards) {
if let Some(mut data) = root_element.mutate_data() {
data.hint.insert(RestyleHint::recascade_subtree());
}
}
self.prepare_stylist_for_reflow(
reflow_request,
document,
root_element,
&guards,
ua_stylesheets,
&snapshot_map,
);
if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
// Need to manually force layout to build a new display list regardless of whether the box tree
// changed or not.
self.need_new_display_list.set(true);
}
let layout_context = LayoutContext {
style_context: self.build_shared_style_context(
guards,
&snapshot_map,
reflow_request.animation_timeline_value,
&reflow_request.animations,
match reflow_request.stylesheets_changed() {
true => TraversalFlags::ForCSSRuleChanges,
false => TraversalFlags::empty(),
},
),
font_context: self.font_context.clone(),
iframe_sizes: Mutex::default(),
use_rayon: rayon_pool.is_some(),
image_resolver: image_resolver.clone(),
};
let restyle = reflow_request
.restyle
.as_ref()
.expect("Should not get here if there is not restyle.");
let dirty_root = unsafe {
ServoLayoutNode::new(&restyle.dirty_root.unwrap())
.as_element()
.unwrap()
};
let recalc_style_traversal = RecalcStyle::new(layout_context);
let recalc_style_traversal = RecalcStyle::new(&layout_context);
let token = {
let shared =
DomTraversal::<ServoLayoutElement>::shared_context(&recalc_style_traversal);
@ -851,20 +836,19 @@ impl LayoutThread {
if !token.should_traverse() {
layout_context.style_context.stylist.rule_tree().maybe_gc();
return RestyleDamage::empty();
return (RestyleDamage::empty(), IFrameSizes::default());
}
let dirty_root: ServoLayoutNode =
driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
let root_node = root_element.as_node();
let mut damage =
compute_damage_and_repair_style(layout_context.shared_context(), root_node);
let mut damage = compute_damage_and_repair_style(&layout_context.style_context, root_node);
if viewport_changed {
damage = RestyleDamage::RELAYOUT;
} else if !damage.contains(RestyleDamage::RELAYOUT) {
layout_context.style_context.stylist.rule_tree().maybe_gc();
return damage;
return (damage, IFrameSizes::default());
}
let mut box_tree = self.box_tree.borrow_mut();
@ -915,12 +899,14 @@ impl LayoutThread {
.style_context
.stylist
.rule_tree()
.dump_stdout(&layout_context.shared_context().guards);
.dump_stdout(&layout_context.style_context.guards);
}
// GC the rule tree if some heuristics are met.
layout_context.style_context.stylist.rule_tree().maybe_gc();
damage
let mut iframe_sizes = layout_context.iframe_sizes.lock();
(damage, std::mem::take(&mut *iframe_sizes))
}
fn calculate_overflow(&self, damage: RestyleDamage) {
@ -995,6 +981,20 @@ impl LayoutThread {
// The stacking context tree is up-to-date again.
self.need_new_stacking_context_tree.set(false);
if self.debug.dump_scroll_tree {
// Print the [ScrollTree], this is done after display list build so we have
// the information about webrender id. Whether a scroll tree is initialized
// or not depends on the reflow goal.
if let Some(tree) = self.stacking_context_tree.borrow().as_ref() {
tree.compositor_info.scroll_tree.debug_print();
} else {
println!(
"Scroll Tree -- reflow {:?}: scroll tree is not initialized yet.",
reflow_request.reflow_goal
);
}
}
}
/// Build the display list for the current layout and send it to the renderer. If no display
@ -1003,7 +1003,7 @@ impl LayoutThread {
&self,
reflow_request: &ReflowRequest,
damage: RestyleDamage,
layout_context: &mut LayoutContext<'_>,
image_resolver: &Arc<ImageResolver>,
) -> bool {
if !ReflowPhases::necessary(&reflow_request.reflow_goal)
.contains(ReflowPhases::DisplayListConstruction)
@ -1032,9 +1032,11 @@ impl LayoutThread {
stacking_context_tree.compositor_info.epoch = epoch.into();
let built_display_list = DisplayListBuilder::build(
layout_context,
reflow_request,
stacking_context_tree,
fragment_tree,
image_resolver.clone(),
self.device().device_pixel_ratio(),
&self.debug,
);
self.compositor_api.send_display_list(

View file

@ -161,7 +161,9 @@ impl ReplacedContents {
};
if let ReplacedContentKind::Image(Some(Image::Raster(ref image))) = kind {
context.handle_animated_image(element.opaque(), image.clone());
context
.image_resolver
.handle_animated_image(element.opaque(), image.clone());
}
let natural_size = if let Some(naturalc_size_in_dots) = natural_size_in_dots {
@ -190,7 +192,7 @@ impl ReplacedContents {
image_url: &ComputedUrl,
) -> Option<Self> {
if let ComputedUrl::Valid(image_url) = image_url {
let (image, width, height) = match context.get_or_request_image_or_meta(
let (image, width, height) = match context.image_resolver.get_or_request_image_or_meta(
element.opaque(),
image_url.clone().into(),
UsePlaceholder::No,
@ -323,12 +325,13 @@ impl ReplacedContents {
.and_then(|image| match image {
Image::Raster(raster_image) => raster_image.id,
Image::Vector(vector_image) => {
let scale = layout_context.shared_context().device_pixel_ratio();
let scale = layout_context.style_context.device_pixel_ratio();
let width = object_fit_size.width.scale_by(scale.0).to_px();
let height = object_fit_size.height.scale_by(scale.0).to_px();
let size = Size2D::new(width, height);
let tag = self.base_fragment_info.tag?;
layout_context
.image_resolver
.rasterize_vector_image(vector_image.id, size, tag.node)
.and_then(|i| i.id)
},
@ -355,7 +358,7 @@ impl ReplacedContents {
},
ReplacedContentKind::IFrame(iframe) => {
let size = Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px());
let hidpi_scale_factor = layout_context.shared_context().device_pixel_ratio();
let hidpi_scale_factor = layout_context.style_context.device_pixel_ratio();
layout_context.iframe_sizes.lock().insert(
iframe.browsing_context_id,

View file

@ -20,11 +20,11 @@ pub struct RecalcStyle<'a> {
}
impl<'a> RecalcStyle<'a> {
pub fn new(context: &'a LayoutContext<'a>) -> Self {
pub(crate) fn new(context: &'a LayoutContext<'a>) -> Self {
RecalcStyle { context }
}
pub fn context(&self) -> &LayoutContext<'a> {
pub(crate) fn context(&self) -> &LayoutContext<'a> {
self.context
}
}