mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
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:
parent
5e44582277
commit
9232b0f550
9 changed files with 208 additions and 196 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
root_element,
|
||||
rayon_pool,
|
||||
&mut layout_context,
|
||||
viewport_changed,
|
||||
);
|
||||
self.calculate_overflow(damage);
|
||||
};
|
||||
let (damage, iframe_sizes) = self.restyle_and_build_trees(
|
||||
&mut reflow_request,
|
||||
document,
|
||||
root_element,
|
||||
&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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue