Skip to main content

layout/
layout_impl.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![expect(unsafe_code)]
6
7use std::cell::{Cell, OnceCell, RefCell};
8use std::collections::HashMap;
9use std::fmt::Debug;
10use std::rc::Rc;
11use std::sync::{Arc, LazyLock};
12
13use app_units::Au;
14use bitflags::bitflags;
15use embedder_traits::{
16    EmbedderMsg, ScriptToEmbedderChan, Theme, UntrustedNodeAddress, ViewportDetails,
17};
18use euclid::{Point2D, Rect, Scale, Size2D};
19use fonts::{FontContext, FontContextWebFontMethods, WebFontDocumentContext};
20use fonts_traits::StylesheetWebFontLoadFinishedCallback;
21use icu_locid::subtags::Language;
22use layout_api::{
23    AxesOverflow, BoxAreaType, CSSPixelRectVec, DangerousStyleNode, IFrameSizes, Layout,
24    LayoutConfig, LayoutElement, LayoutFactory, LayoutNode, NodeRenderingType,
25    OffsetParentResponse, PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest,
26    ReflowRequestRestyle, ReflowResult, ReflowStatistics, ScrollContainerQueryFlags,
27    ScrollContainerResponse, TrustedNodeAddress, with_layout_state,
28};
29use log::{debug, warn};
30use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
31use net_traits::image_cache::ImageCache;
32use paint_api::CrossProcessPaintApi;
33use paint_api::display_list::{AxesScrollSensitivity, PaintDisplayListInfo, ScrollType};
34use parking_lot::{Mutex, RwLock};
35use profile_traits::mem::{Report, ReportKind};
36use profile_traits::time::{
37    self as profile_time, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType,
38};
39use profile_traits::{path, time_profile};
40use rustc_hash::FxHashMap;
41use script::layout_dom::{
42    ServoDangerousStyleDocument, ServoDangerousStyleElement, ServoLayoutElement, ServoLayoutNode,
43};
44use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter, ScriptThreadMessage};
45use servo_arc::Arc as ServoArc;
46use servo_base::Epoch;
47use servo_base::generic_channel::GenericSender;
48use servo_base::id::{PipelineId, WebViewId};
49use servo_config::opts::{self, DiagnosticsLogging, DiagnosticsLoggingOption};
50use servo_config::pref;
51use servo_url::ServoUrl;
52use style::animation::DocumentAnimationSet;
53use style::context::{
54    QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
55};
56use style::device::Device;
57use style::device::servo::FontMetricsProvider;
58use style::dom::{OpaqueNode, ShowSubtreeDataAndPrimaryValues, TDocument, TElement, TNode};
59use style::font_metrics::FontMetrics;
60use style::global_style_data::GLOBAL_STYLE_DATA;
61use style::invalidation::element::restyle_hints::RestyleHint;
62use style::invalidation::stylesheets::StylesheetInvalidationSet;
63use style::media_queries::{MediaList, MediaType};
64use style::properties::style_structs::Font;
65use style::properties::{ComputedValues, PropertyId};
66use style::queries::values::PrefersColorScheme;
67use style::selector_parser::{PseudoElement, RestyleDamage, SnapshotMap};
68use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
69use style::stylesheets::{
70    CustomMediaMap, DocumentStyleSheet, Origin, Stylesheet, StylesheetInDocument,
71};
72use style::stylist::Stylist;
73use style::traversal::DomTraversal;
74use style::traversal_flags::TraversalFlags;
75use style::values::computed::font::GenericFontFamily;
76use style::values::computed::{CSSPixelLength, FontSize, Length, NonNegativeLength};
77use style::values::specified::font::{KeywordInfo, QueryFontMetricsFlags};
78use style::{Zero, driver};
79use style_traits::{CSSPixel, SpeculativePainter};
80use stylo_atoms::Atom;
81use url::Url;
82use webrender_api::ExternalScrollId;
83use webrender_api::units::{DevicePixel, LayoutVector2D};
84
85use crate::accessibility_tree::AccessibilityTree;
86use crate::context::{CachedImageOrError, ImageResolver, LayoutContext};
87use crate::display_list::{DisplayListBuilder, HitTest, PaintTimingHandler, StackingContextTree};
88use crate::dom::NodeExt;
89use crate::query::{
90    find_character_offset_in_fragment_descendants, get_the_text_steps, process_box_area_request,
91    process_box_areas_request, process_client_rect_request, process_containing_block_query,
92    process_current_css_zoom_query, process_effective_overflow_query,
93    process_node_scroll_area_request, process_offset_parent_query, process_padding_request,
94    process_resolved_font_style_query, process_resolved_style_request,
95    process_scroll_container_query,
96};
97use crate::traversal::{RecalcStyle, compute_damage_and_rebuild_box_tree};
98use crate::{BoxTree, FragmentTree};
99
100// This mutex is necessary due to syncronisation issues between two different types of thread-local storage
101// which manifest themselves when the layout thread tries to layout iframes in parallel with the main page
102//
103// See: https://github.com/servo/servo/pull/29792
104// And: https://gist.github.com/mukilan/ed57eb61b83237a05fbf6360ec5e33b0
105static STYLE_THREAD_POOL: Mutex<&LazyLock<style::global_style_data::StyleThreadPool>> =
106    Mutex::new(&style::global_style_data::STYLE_THREAD_POOL);
107
108/// A CSS file to style the user agent stylesheet.
109static USER_AGENT_CSS: &[u8] = include_bytes!("./stylesheets/user-agent.css");
110
111/// A CSS file to style the user agent stylesheet in HTML documents.
112static HTML_MODE_CSS: &[u8] = include_bytes!("./stylesheets/html-mode.css");
113
114/// A CSS file to style the Servo browser.
115static SERVO_CSS: &[u8] = include_bytes!("./stylesheets/servo.css");
116
117/// A CSS file to style the presentational hints.
118static PRESENTATIONAL_HINTS_CSS: &[u8] = include_bytes!("./stylesheets/presentational-hints.css");
119
120/// A CSS file to style the quirks mode.
121static QUIRKS_MODE_CSS: &[u8] = include_bytes!("./stylesheets/quirks-mode.css");
122
123/// Information needed by layout.
124pub struct LayoutThread {
125    /// The ID of the pipeline that we belong to.
126    id: PipelineId,
127
128    /// The webview that contains the pipeline we belong to.
129    webview_id: WebViewId,
130
131    /// The URL of the pipeline that we belong to.
132    url: ServoUrl,
133
134    /// Performs CSS selector matching and style resolution.
135    stylist: Stylist,
136
137    /// Is the current reflow of an iframe, as opposed to a root window?
138    is_iframe: bool,
139
140    /// The channel on which messages can be sent to the script thread.
141    script_chan: GenericSender<ScriptThreadMessage>,
142
143    /// The channel on which messages can be sent to the time profiler.
144    time_profiler_chan: profile_time::ProfilerChan,
145
146    /// The channel to send messages to the Embedder.
147    embedder_chan: ScriptToEmbedderChan,
148
149    /// Reference to the script thread image cache.
150    image_cache: Arc<dyn ImageCache>,
151
152    /// A FontContext to be used during layout.
153    font_context: Arc<FontContext>,
154
155    /// Whether or not user agent stylesheets have been added to the Stylist or not.
156    have_added_user_agent_stylesheets: bool,
157
158    // A vector of parsed `DocumentStyleSheet`s representing the corresponding `UserStyleSheet`s
159    // associated with the `WebView` to which this `Layout` belongs. The `DocumentStylesheet`s might
160    // be shared with `Layout`s in the same `ScriptThread`.
161    user_stylesheets: Rc<Vec<DocumentStyleSheet>>,
162
163    /// Whether or not this [`LayoutImpl`]'s [`Device`] has changed since the last restyle.
164    /// If it has, a restyle is pending.
165    device_has_changed: bool,
166
167    /// Is this the first reflow in this LayoutThread?
168    have_ever_generated_display_list: Cell<bool>,
169
170    /// Whether the last display list we sent was effectively empty.
171    last_display_list_was_empty: Cell<bool>,
172
173    /// Whether a new display list is necessary due to changes to layout or stacking
174    /// contexts. This is set to true every time layout changes, even when a display list
175    /// isn't requested for this layout, such as for layout queries. The next time a
176    /// layout requests a display list, it is produced unconditionally, even when the
177    /// layout trees remain the same.
178    need_new_display_list: Cell<bool>,
179
180    /// Whether or not cumulative containing blocks offsets have been set into the
181    /// [`FragmentTree`]. This typically happens during [`StackingContextTree`]
182    /// construction, but if a layout query needs these value beforehand, they are
183    /// eagerly calculated.
184    need_containing_block_calculation: Cell<bool>,
185
186    /// Whether or not the existing stacking context tree is dirty and needs to be
187    /// rebuilt. This happens after a relayout or overflow update. The reason that we
188    /// don't simply clear the stacking context tree when it becomes dirty is that we need
189    /// to preserve scroll offsets from the old tree to the new one.
190    need_new_stacking_context_tree: Cell<bool>,
191
192    /// The box tree.
193    box_tree: RefCell<Option<Arc<BoxTree>>>,
194
195    /// The fragment tree.
196    fragment_tree: RefCell<Option<Rc<FragmentTree>>>,
197
198    /// The [`StackingContextTree`] cached from previous layouts.
199    stacking_context_tree: RefCell<Option<StackingContextTree>>,
200
201    // A cache that maps image resources specified in CSS (e.g as the `url()` value
202    // for `background-image` or `content` properties) to either the final resolved
203    // image data, or an error if the image cache failed to load/decode the image.
204    resolved_images_cache: Arc<RwLock<HashMap<ServoUrl, CachedImageOrError>>>,
205
206    /// The executors for paint worklets.
207    registered_painters: RegisteredPaintersImpl,
208
209    /// Cross-process access to the `Paint` API.
210    paint_api: CrossProcessPaintApi,
211
212    /// Debug options, copied from configuration to this `LayoutThread` in order
213    /// to avoid having to constantly access the thread-safe global options.
214    debug: DiagnosticsLogging,
215
216    /// Tracks the node that was highlighted by the devtools during the last reflow.
217    ///
218    /// If this changed, then we need to create a new display list.
219    previously_highlighted_dom_node: Cell<Option<OpaqueNode>>,
220
221    /// Handler for all Paint Timings
222    paint_timing_handler: RefCell<Option<PaintTimingHandler>>,
223
224    /// Layout's internal representation of its accessibility tree.
225    /// This is `None` if accessibility is not active.
226    accessibility_tree: RefCell<Option<AccessibilityTree>>,
227
228    /// See [Layout::needs_accessibility_update()].
229    needs_accessibility_update: Cell<bool>,
230}
231
232pub struct LayoutFactoryImpl();
233
234impl LayoutFactory for LayoutFactoryImpl {
235    fn create(&self, config: LayoutConfig) -> Box<dyn Layout> {
236        Box::new(LayoutThread::new(config))
237    }
238}
239
240impl Drop for LayoutThread {
241    fn drop(&mut self) {
242        let (keys, instance_keys) = self
243            .font_context
244            .collect_unused_webrender_resources(true /* all */);
245        self.paint_api
246            .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys)
247    }
248}
249
250impl Layout for LayoutThread {
251    fn device(&self) -> &Device {
252        self.stylist.device()
253    }
254
255    fn set_theme(&mut self, theme: Theme) -> bool {
256        let theme: PrefersColorScheme = theme.into();
257        let device = self.stylist.device_mut();
258        if theme == device.color_scheme() {
259            return false;
260        }
261
262        device.set_color_scheme(theme);
263        self.device_has_changed = true;
264        true
265    }
266
267    fn set_viewport_details(&mut self, viewport_details: ViewportDetails) -> bool {
268        let device = self.stylist.device_mut();
269        let device_pixel_ratio = Scale::new(viewport_details.hidpi_scale_factor.get());
270        if device.viewport_size() == viewport_details.size &&
271            device.device_pixel_ratio() == device_pixel_ratio
272        {
273            return false;
274        }
275
276        device.set_viewport_size(viewport_details.size);
277        device.set_device_pixel_ratio(device_pixel_ratio);
278        self.device_has_changed = true;
279        true
280    }
281
282    fn load_web_fonts_from_stylesheet(
283        &self,
284        stylesheet: &ServoArc<Stylesheet>,
285        document_context: &WebFontDocumentContext,
286    ) {
287        let guard = stylesheet.shared_lock.read();
288        self.load_all_web_fonts_from_stylesheet_with_guard(
289            &DocumentStyleSheet(stylesheet.clone()),
290            &guard,
291            document_context,
292        );
293    }
294
295    #[servo_tracing::instrument(skip_all)]
296    fn add_stylesheet(
297        &mut self,
298        stylesheet: ServoArc<Stylesheet>,
299        before_stylesheet: Option<ServoArc<Stylesheet>>,
300        document_context: &WebFontDocumentContext,
301    ) {
302        let guard = stylesheet.shared_lock.read();
303        let stylesheet = DocumentStyleSheet(stylesheet.clone());
304        self.load_all_web_fonts_from_stylesheet_with_guard(&stylesheet, &guard, document_context);
305
306        match before_stylesheet {
307            Some(insertion_point) => self.stylist.insert_stylesheet_before(
308                stylesheet,
309                DocumentStyleSheet(insertion_point),
310                &guard,
311            ),
312            None => self.stylist.append_stylesheet(stylesheet, &guard),
313        }
314    }
315
316    #[servo_tracing::instrument(skip_all)]
317    fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>) {
318        let guard = stylesheet.shared_lock.read();
319        let stylesheet = DocumentStyleSheet(stylesheet.clone());
320        self.stylist.remove_stylesheet(stylesheet.clone(), &guard);
321        self.font_context
322            .remove_all_web_fonts_from_stylesheet(&stylesheet);
323    }
324
325    #[servo_tracing::instrument(skip_all)]
326    fn remove_cached_image(&mut self, url: &ServoUrl) {
327        let mut resolved_images_cache = self.resolved_images_cache.write();
328        resolved_images_cache.remove(url);
329    }
330
331    fn node_rendering_type(
332        &self,
333        node: TrustedNodeAddress,
334        pseudo: Option<PseudoElement>,
335    ) -> NodeRenderingType {
336        with_layout_state(|| {
337            let node = unsafe { ServoLayoutNode::new(&node) };
338
339            // Nodes that are not currently styled are never being rendered.
340            if node
341                .as_element()
342                .is_none_or(|element| element.style_data().is_none())
343            {
344                return NodeRenderingType::NotRendered;
345            }
346
347            let node = match pseudo {
348                Some(pseudo) => node.with_pseudo(pseudo),
349                None => Some(node),
350            };
351            let Some(node) = node else {
352                return NodeRenderingType::NotRendered;
353            };
354            node.rendering_type()
355        })
356    }
357
358    /// Return the node corresponding to the containing block of the provided node.
359    #[servo_tracing::instrument(skip_all)]
360    fn query_containing_block(&self, node: TrustedNodeAddress) -> Option<UntrustedNodeAddress> {
361        with_layout_state(|| {
362            let node = unsafe { ServoLayoutNode::new(&node) };
363            process_containing_block_query(node)
364        })
365    }
366
367    /// Return the resolved values of this node's padding rect.
368    #[servo_tracing::instrument(skip_all)]
369    fn query_padding(&self, node: TrustedNodeAddress) -> Option<PhysicalSides> {
370        with_layout_state(|| {
371            // If we have not built a fragment tree yet, there is no way we have layout information for
372            // this query, which can be run without forcing a layout (for IntersectionObserver).
373            if self.fragment_tree.borrow().is_none() {
374                return None;
375            }
376
377            let node = unsafe { ServoLayoutNode::new(&node) };
378            process_padding_request(node)
379        })
380    }
381
382    /// Return the union of this node's areas in the coordinate space of the Document. This is used
383    /// to implement `getBoundingClientRect()` and support many other API where the such query is
384    /// required.
385    ///
386    /// Part of <https://drafts.csswg.org/cssom-view-1/#element-get-the-bounding-box>.
387    #[servo_tracing::instrument(skip_all)]
388    fn query_box_area(
389        &self,
390        node: TrustedNodeAddress,
391        area: BoxAreaType,
392        exclude_transform_and_inline: bool,
393    ) -> Option<Rect<Au, CSSPixel>> {
394        with_layout_state(|| {
395            // If we have not built a fragment tree yet, there is no way we have layout information for
396            // this query, which can be run without forcing a layout (for IntersectionObserver).
397            if self.fragment_tree.borrow().is_none() {
398                return None;
399            }
400
401            let node = unsafe { ServoLayoutNode::new(&node) };
402            let stacking_context_tree = self.stacking_context_tree.borrow();
403            let stacking_context_tree = stacking_context_tree.as_ref()?;
404            process_box_area_request(
405                self,
406                stacking_context_tree,
407                node,
408                area,
409                exclude_transform_and_inline,
410            )
411        })
412    }
413
414    /// Get a `Vec` of bounding boxes of this node's `Fragment`s specific area in the coordinate space of
415    /// the Document. This is used to implement `getClientRects()`.
416    ///
417    /// See <https://drafts.csswg.org/cssom-view/#dom-element-getclientrects>.
418    #[servo_tracing::instrument(skip_all)]
419    fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> CSSPixelRectVec {
420        with_layout_state(|| {
421            // If we have not built a fragment tree yet, there is no way we have layout information for
422            // this query, which can be run without forcing a layout (for IntersectionObserver).
423            if self.fragment_tree.borrow().is_none() {
424                return None;
425            }
426
427            let node = unsafe { ServoLayoutNode::new(&node) };
428            let stacking_context_tree = self.stacking_context_tree.borrow();
429            let stacking_context_tree = stacking_context_tree.as_ref()?;
430            Some(process_box_areas_request(
431                self,
432                stacking_context_tree,
433                node,
434                area,
435            ))
436        })
437        .unwrap_or_default()
438    }
439
440    #[servo_tracing::instrument(skip_all)]
441    fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32, CSSPixel> {
442        with_layout_state(|| {
443            let node = unsafe { ServoLayoutNode::new(&node) };
444            process_client_rect_request(node)
445        })
446    }
447
448    #[servo_tracing::instrument(skip_all)]
449    fn query_current_css_zoom(&self, node: TrustedNodeAddress) -> f32 {
450        with_layout_state(|| {
451            let node = unsafe { ServoLayoutNode::new(&node) };
452            process_current_css_zoom_query(node)
453        })
454    }
455
456    #[servo_tracing::instrument(skip_all)]
457    fn query_element_inner_outer_text(&self, node: layout_api::TrustedNodeAddress) -> String {
458        with_layout_state(|| {
459            let node = unsafe { ServoLayoutNode::new(&node) };
460            get_the_text_steps(node)
461        })
462    }
463    #[servo_tracing::instrument(skip_all)]
464    fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse {
465        with_layout_state(|| {
466            let node = unsafe { ServoLayoutNode::new(&node) };
467            let stacking_context_tree = self.stacking_context_tree.borrow();
468            let stacking_context_tree = stacking_context_tree.as_ref()?;
469            process_offset_parent_query(self, &stacking_context_tree.paint_info.scroll_tree, node)
470        })
471        .unwrap_or_default()
472    }
473
474    #[servo_tracing::instrument(skip_all)]
475    fn query_scroll_container(
476        &self,
477        node: Option<TrustedNodeAddress>,
478        flags: ScrollContainerQueryFlags,
479    ) -> Option<ScrollContainerResponse> {
480        with_layout_state(|| {
481            let node = unsafe { node.as_ref().map(|node| ServoLayoutNode::new(node)) };
482            let viewport_overflow = self.box_tree.borrow().as_ref()?.viewport_overflow;
483            process_scroll_container_query(node, flags, viewport_overflow)
484        })
485    }
486
487    #[servo_tracing::instrument(skip_all)]
488    fn query_resolved_style(
489        &self,
490        node: TrustedNodeAddress,
491        pseudo: Option<PseudoElement>,
492        property_id: PropertyId,
493        animations: DocumentAnimationSet,
494        animation_timeline_value: f64,
495    ) -> String {
496        with_layout_state(|| {
497            let node = unsafe { ServoLayoutNode::new(&node) };
498            let document = unsafe { node.dangerous_style_node() }.owner_doc();
499            let shared_locks = document.shared_style_locks();
500            let guards = StylesheetGuards {
501                author: &shared_locks.author.read(),
502                ua_or_user: &shared_locks.ua_or_user.read(),
503            };
504            let snapshot_map = SnapshotMap::new();
505
506            let shared_style_context = self.build_shared_style_context(
507                guards,
508                &snapshot_map,
509                animation_timeline_value,
510                &animations,
511                TraversalFlags::empty(),
512            );
513
514            process_resolved_style_request(self, &shared_style_context, node, &pseudo, &property_id)
515        })
516    }
517
518    #[servo_tracing::instrument(skip_all)]
519    fn query_resolved_font_style(
520        &self,
521        node: TrustedNodeAddress,
522        value: &str,
523        animations: DocumentAnimationSet,
524        animation_timeline_value: f64,
525    ) -> Option<ServoArc<Font>> {
526        with_layout_state(|| {
527            let node = unsafe { ServoLayoutNode::new(&node) };
528            let document = unsafe { node.dangerous_style_node() }.owner_doc();
529            let shared_locks = document.shared_style_locks();
530            let shared_author_lock = &shared_locks.author;
531            let guards = StylesheetGuards {
532                author: &shared_author_lock.read(),
533                ua_or_user: &shared_locks.ua_or_user.read(),
534            };
535            let snapshot_map = SnapshotMap::new();
536            let shared_style_context = self.build_shared_style_context(
537                guards,
538                &snapshot_map,
539                animation_timeline_value,
540                &animations,
541                TraversalFlags::empty(),
542            );
543
544            process_resolved_font_style_query(
545                &shared_style_context,
546                node,
547                value,
548                self.url.clone(),
549                shared_author_lock,
550            )
551        })
552    }
553
554    #[servo_tracing::instrument(skip_all)]
555    fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32, CSSPixel> {
556        with_layout_state(|| {
557            let node = node.map(|node| unsafe { ServoLayoutNode::new(&node) });
558            process_node_scroll_area_request(self, node, self.fragment_tree.borrow().clone())
559        })
560    }
561
562    #[servo_tracing::instrument(skip_all)]
563    fn query_text_index(
564        &self,
565        node: TrustedNodeAddress,
566        point_in_node: Point2D<Au, CSSPixel>,
567    ) -> Option<usize> {
568        with_layout_state(|| {
569            let node = unsafe { ServoLayoutNode::new(&node) };
570            let stacking_context_tree = self.stacking_context_tree.borrow_mut();
571            let stacking_context_tree = stacking_context_tree.as_ref()?;
572            find_character_offset_in_fragment_descendants(
573                &node,
574                stacking_context_tree,
575                point_in_node,
576            )
577        })
578    }
579
580    #[servo_tracing::instrument(skip_all)]
581    fn query_elements_from_point(
582        &self,
583        point: webrender_api::units::LayoutPoint,
584        flags: layout_api::ElementsFromPointFlags,
585    ) -> Vec<layout_api::ElementsFromPointResult> {
586        with_layout_state(|| {
587            self.stacking_context_tree
588                .borrow_mut()
589                .as_mut()
590                .map(|tree| HitTest::run(tree, point, flags))
591                .unwrap_or_default()
592        })
593    }
594
595    #[servo_tracing::instrument(skip_all)]
596    fn query_effective_overflow(&self, node: TrustedNodeAddress) -> Option<AxesOverflow> {
597        with_layout_state(|| {
598            let node = unsafe { ServoLayoutNode::new(&node) };
599            process_effective_overflow_query(node)
600        })
601    }
602
603    fn exit_now(&mut self) {}
604
605    fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps) {
606        // TODO: Measure more than just display list, stylist, and font context.
607        let formatted_url = &format!("url({})", self.url);
608        reports.push(Report {
609            path: path![formatted_url, "layout-thread", "display-list"],
610            kind: ReportKind::ExplicitJemallocHeapSize,
611            size: 0,
612        });
613
614        reports.push(Report {
615            path: path![formatted_url, "layout-thread", "stylist"],
616            kind: ReportKind::ExplicitJemallocHeapSize,
617            size: self.stylist.size_of(ops),
618        });
619
620        reports.push(Report {
621            path: path![formatted_url, "layout-thread", "font-context"],
622            kind: ReportKind::ExplicitJemallocHeapSize,
623            size: self.font_context.conditional_size_of(ops),
624        });
625
626        reports.push(Report {
627            path: path![formatted_url, "layout-thread", "box-tree"],
628            kind: ReportKind::ExplicitJemallocHeapSize,
629            size: self
630                .box_tree
631                .borrow()
632                .as_ref()
633                .map_or(0, |tree| tree.conditional_size_of(ops)),
634        });
635
636        reports.push(Report {
637            path: path![formatted_url, "layout-thread", "fragment-tree"],
638            kind: ReportKind::ExplicitJemallocHeapSize,
639            size: self
640                .fragment_tree
641                .borrow()
642                .as_ref()
643                .map(|tree| tree.conditional_size_of(ops))
644                .unwrap_or_default(),
645        });
646
647        reports.push(Report {
648            path: path![formatted_url, "layout-thread", "stacking-context-tree"],
649            kind: ReportKind::ExplicitJemallocHeapSize,
650            size: self.stacking_context_tree.size_of(ops),
651        });
652
653        reports.extend(self.image_cache.memory_reports(formatted_url, ops));
654    }
655
656    fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
657        self.stylist.set_quirks_mode(quirks_mode);
658    }
659
660    fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult> {
661        time_profile!(
662            profile_time::ProfilerCategory::Layout,
663            self.profiler_metadata(),
664            self.time_profiler_chan.clone(),
665            || with_layout_state(|| self.handle_reflow(reflow_request)),
666        )
667    }
668
669    fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails) {
670        with_layout_state(|| {
671            if self.stacking_context_tree.borrow().is_some() &&
672                !self.need_new_stacking_context_tree.get()
673            {
674                return;
675            }
676            self.build_stacking_context_tree(viewport_details);
677        })
678    }
679
680    fn register_paint_worklet_modules(
681        &mut self,
682        _name: Atom,
683        _properties: Vec<Atom>,
684        _painter: Box<dyn Painter>,
685    ) {
686    }
687
688    fn set_scroll_offsets_from_renderer(
689        &mut self,
690        scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
691    ) {
692        let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
693        let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
694            warn!("Received scroll offsets before finishing layout.");
695            return;
696        };
697
698        stacking_context_tree
699            .paint_info
700            .scroll_tree
701            .set_all_scroll_offsets(scroll_states);
702    }
703
704    fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D> {
705        self.stacking_context_tree
706            .borrow_mut()
707            .as_mut()
708            .and_then(|tree| tree.paint_info.scroll_tree.scroll_offset(id))
709    }
710
711    fn needs_new_display_list(&self) -> bool {
712        self.need_new_display_list.get()
713    }
714
715    fn set_needs_new_display_list(&self) {
716        self.need_new_display_list.set(true);
717    }
718
719    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function>
720    fn stylist_mut(&mut self) -> &mut Stylist {
721        &mut self.stylist
722    }
723
724    fn set_accessibility_active(&self, active: bool, epoch: Epoch) {
725        if !active {
726            self.accessibility_tree.replace(None);
727            return;
728        }
729        self.set_needs_accessibility_update();
730        let mut accessibility_tree = self.accessibility_tree.borrow_mut();
731        if accessibility_tree.is_some() {
732            return;
733        }
734        *accessibility_tree = Some(AccessibilityTree::new(self.id.into(), epoch));
735    }
736
737    fn needs_accessibility_update(&self) -> bool {
738        self.needs_accessibility_update.get()
739    }
740
741    fn set_needs_accessibility_update(&self) {
742        self.needs_accessibility_update.set(true);
743    }
744}
745
746impl LayoutThread {
747    fn new(config: LayoutConfig) -> LayoutThread {
748        // Let webrender know about this pipeline by sending an empty display list.
749        config
750            .paint_api
751            .send_initial_transaction(config.webview_id, config.id.into());
752
753        let mut font = Font::initial_values();
754        let default_font_size = pref!(fonts_default_size);
755        font.font_size = FontSize {
756            computed_size: NonNegativeLength::new(default_font_size as f32),
757            used_size: NonNegativeLength::new(default_font_size as f32),
758            keyword_info: KeywordInfo::medium(),
759        };
760
761        // The device pixel ratio is incorrect (it does not have the hidpi value),
762        // but it will be set correctly when the initial reflow takes place.
763        let device = Device::new(
764            MediaType::screen(),
765            QuirksMode::NoQuirks,
766            config.viewport_details.size,
767            Scale::new(config.viewport_details.hidpi_scale_factor.get()),
768            Box::new(LayoutFontMetricsProvider(config.font_context.clone())),
769            ComputedValues::initial_values_with_font_override(font),
770            config.theme.into(),
771        );
772
773        LayoutThread {
774            id: config.id,
775            webview_id: config.webview_id,
776            url: config.url,
777            is_iframe: config.is_iframe,
778            script_chan: config.script_chan.clone(),
779            time_profiler_chan: config.time_profiler_chan,
780            embedder_chan: config.embedder_chan.clone(),
781            registered_painters: RegisteredPaintersImpl(Default::default()),
782            image_cache: config.image_cache,
783            font_context: config.font_context,
784            have_added_user_agent_stylesheets: false,
785            have_ever_generated_display_list: Cell::new(false),
786            last_display_list_was_empty: Cell::new(true),
787            device_has_changed: false,
788            need_containing_block_calculation: Cell::new(false),
789            need_new_display_list: Cell::new(false),
790            need_new_stacking_context_tree: Cell::new(false),
791            box_tree: Default::default(),
792            fragment_tree: Default::default(),
793            stacking_context_tree: Default::default(),
794            paint_api: config.paint_api,
795            stylist: Stylist::new(device, QuirksMode::NoQuirks),
796            resolved_images_cache: Default::default(),
797            debug: opts::get().debug.clone(),
798            previously_highlighted_dom_node: Cell::new(None),
799            paint_timing_handler: Default::default(),
800            user_stylesheets: config.user_stylesheets,
801            accessibility_tree: Default::default(),
802            needs_accessibility_update: Cell::new(false),
803        }
804    }
805
806    fn build_shared_style_context<'a>(
807        &'a self,
808        guards: StylesheetGuards<'a>,
809        snapshot_map: &'a SnapshotMap,
810        animation_timeline_value: f64,
811        animations: &DocumentAnimationSet,
812        traversal_flags: TraversalFlags,
813    ) -> SharedStyleContext<'a> {
814        SharedStyleContext {
815            stylist: &self.stylist,
816            options: GLOBAL_STYLE_DATA.options.clone(),
817            guards,
818            visited_styles_enabled: false,
819            animations: animations.clone(),
820            registered_speculative_painters: &self.registered_painters,
821            current_time_for_animations: animation_timeline_value,
822            traversal_flags,
823            snapshot_map,
824        }
825    }
826
827    fn load_all_web_fonts_from_stylesheet_with_guard(
828        &self,
829        stylesheet: &DocumentStyleSheet,
830        guard: &SharedRwLockReadGuard,
831        document_context: &WebFontDocumentContext,
832    ) {
833        let custom_media = &CustomMediaMap::default();
834        if !stylesheet.is_effective_for_device(self.stylist.device(), custom_media, guard) {
835            return;
836        }
837
838        let locked_script_channel = Mutex::new(self.script_chan.clone());
839        let pipeline_id = self.id;
840        let web_font_finished_loading_callback = move |succeeded: bool| {
841            if succeeded {
842                let _ = locked_script_channel
843                    .lock()
844                    .send(ScriptThreadMessage::WebFontLoaded(pipeline_id));
845            }
846        };
847
848        self.font_context.add_all_web_fonts_from_stylesheet(
849            self.webview_id,
850            stylesheet,
851            guard,
852            self.stylist.device(),
853            Arc::new(web_font_finished_loading_callback) as StylesheetWebFontLoadFinishedCallback,
854            document_context,
855        );
856    }
857
858    /// In some cases, if a restyle isn't necessary we can skip doing any work for layout
859    /// entirely. This check allows us to return early from layout without doing any work
860    /// at all.
861    fn can_skip_reflow_request_entirely(&self, reflow_request: &ReflowRequest) -> bool {
862        // If a restyle is necessary, restyle and reflow is a necessity.
863        if reflow_request.restyle.is_some() {
864            return false;
865        }
866        // We always need to at least build a fragment tree.
867        if self.fragment_tree.borrow().is_none() {
868            return false;
869        }
870        // If accessibility was just activated, we need reflow to build the accessibility tree.
871        if self.needs_accessibility_update() {
872            return false;
873        }
874
875        // If we have a fragment tree and it's up-to-date and this reflow
876        // doesn't need more reflow results, we can skip the rest of layout.
877        let necessary_phases = ReflowPhases::necessary(&reflow_request.reflow_goal);
878        if necessary_phases.is_empty() {
879            return true;
880        }
881
882        // If only the stacking context tree is required, and it's up-to-date,
883        // layout is unnecessary, otherwise a layout is necessary.
884        if necessary_phases == ReflowPhases::StackingContextTreeConstruction {
885            return self.stacking_context_tree.borrow().is_some() &&
886                !self.need_new_stacking_context_tree.get();
887        }
888
889        // Otherwise, the only interesting thing is whether the current display
890        // list is up-to-date.
891        assert_eq!(
892            necessary_phases,
893            ReflowPhases::StackingContextTreeConstruction | ReflowPhases::DisplayListConstruction
894        );
895        !self.need_new_display_list.get()
896    }
897
898    fn maybe_print_reflow_event(&self, reflow_request: &ReflowRequest) {
899        if !self
900            .debug
901            .is_enabled(DiagnosticsLoggingOption::RelayoutEvent)
902        {
903            return;
904        }
905
906        println!(
907            "**** Reflow({}) => {:?}, {:?}",
908            self.id,
909            reflow_request.reflow_goal,
910            reflow_request
911                .restyle
912                .as_ref()
913                .map(|restyle| restyle.reason)
914                .unwrap_or_default()
915        );
916    }
917
918    /// Checks whether we need to update the scroll node, and report whether the
919    /// node is scrolled. We need to update the scroll node whenever it is requested.
920    fn handle_update_scroll_node_request(&self, reflow_request: &ReflowRequest) -> bool {
921        if let ReflowGoal::UpdateScrollNode(external_scroll_id, offset) = reflow_request.reflow_goal
922        {
923            self.set_scroll_offset_from_script(external_scroll_id, offset)
924        } else {
925            false
926        }
927    }
928
929    fn handle_accessibility_tree_update(&self, root_element: &ServoLayoutNode) -> bool {
930        if !self.needs_accessibility_update() {
931            return false;
932        }
933        let mut accessibility_tree = self.accessibility_tree.borrow_mut();
934        let Some(accessibility_tree) = accessibility_tree.as_mut() else {
935            return false;
936        };
937
938        let accessibility_tree = &mut *accessibility_tree;
939        if let Some(tree_update) = accessibility_tree.update_tree(root_element) {
940            // TODO(#4344): send directly to embedder over the pipeline_to_embedder_sender cloned from ScriptThread.
941            // FIXME: Handle send error. Could have a method on accessibility tree to
942            // finalise after sending, removing accessibility damage? On fail, retain damage
943            // for next reflow, as well as retaining document.needs_accessibility_update.
944            let _ = self
945                .embedder_chan
946                .send(EmbedderMsg::AccessibilityTreeUpdate(
947                    self.webview_id,
948                    tree_update,
949                    accessibility_tree.embedder_epoch(),
950                ));
951        }
952        self.needs_accessibility_update.set(false);
953        true
954    }
955
956    /// The high-level routine that performs layout.
957    #[servo_tracing::instrument(skip_all)]
958    fn handle_reflow(&mut self, mut reflow_request: ReflowRequest) -> Option<ReflowResult> {
959        self.maybe_print_reflow_event(&reflow_request);
960
961        if self.can_skip_reflow_request_entirely(&reflow_request) {
962            // We can skip layout, but we might need to update a scroll node.
963            return self
964                .handle_update_scroll_node_request(&reflow_request)
965                .then(|| ReflowResult {
966                    reflow_phases_run: ReflowPhasesRun::UpdatedScrollNodeOffset,
967                    ..Default::default()
968                });
969        }
970
971        let document = unsafe { ServoLayoutNode::new(&reflow_request.document) };
972        let document = unsafe { document.dangerous_style_node() }
973            .as_document()
974            .unwrap();
975        let Some(root_element) = document.root_element() else {
976            if !self.last_display_list_was_empty.get() {
977                return self.clear_layout_trees_and_send_empty_display_list(&reflow_request);
978            }
979            debug!("layout: No root node: bailing");
980            return None;
981        };
982
983        let image_resolver = Arc::new(ImageResolver {
984            origin: reflow_request.origin.clone(),
985            image_cache: self.image_cache.clone(),
986            resolved_images_cache: self.resolved_images_cache.clone(),
987            pending_images: Mutex::default(),
988            pending_rasterization_images: Mutex::default(),
989            pending_svg_elements_for_serialization: Mutex::default(),
990            animating_images: reflow_request.animating_images.clone(),
991            animation_timeline_value: reflow_request.animation_timeline_value,
992        });
993        let mut reflow_statistics = Default::default();
994
995        let (mut reflow_phases_run, iframe_sizes) = self.restyle_and_build_trees(
996            &mut reflow_request,
997            document,
998            root_element,
999            &image_resolver,
1000        );
1001        if self.build_stacking_context_tree_for_reflow(&reflow_request) {
1002            reflow_phases_run.insert(ReflowPhasesRun::BuiltStackingContextTree);
1003        }
1004        if self.build_display_list(&reflow_request, &image_resolver, &mut reflow_statistics) {
1005            reflow_phases_run.insert(ReflowPhasesRun::BuiltDisplayList);
1006        }
1007        if self.handle_update_scroll_node_request(&reflow_request) {
1008            reflow_phases_run.insert(ReflowPhasesRun::UpdatedScrollNodeOffset);
1009        }
1010        if self.handle_accessibility_tree_update(&root_element.as_node()) {
1011            reflow_phases_run.insert(ReflowPhasesRun::UpdatedAccessibilityTree);
1012        }
1013
1014        if self.debug.is_enabled(DiagnosticsLoggingOption::FlowTree) &&
1015            reflow_phases_run.contains(ReflowPhasesRun::RanLayout) &&
1016            let Some(fragment_tree) = &*self.fragment_tree.borrow()
1017        {
1018            fragment_tree.print();
1019        }
1020
1021        let pending_images = std::mem::take(&mut *image_resolver.pending_images.lock());
1022        let pending_rasterization_images =
1023            std::mem::take(&mut *image_resolver.pending_rasterization_images.lock());
1024        let pending_svg_elements_for_serialization =
1025            std::mem::take(&mut *image_resolver.pending_svg_elements_for_serialization.lock());
1026
1027        Some(ReflowResult {
1028            reflow_phases_run,
1029            pending_images,
1030            pending_rasterization_images,
1031            pending_svg_elements_for_serialization,
1032            iframe_sizes: Some(iframe_sizes),
1033            reflow_statistics,
1034        })
1035    }
1036
1037    #[servo_tracing::instrument(skip_all)]
1038    fn prepare_stylist_for_reflow<'dom>(
1039        &mut self,
1040        reflow_request: &ReflowRequest,
1041        document: ServoDangerousStyleDocument<'dom>,
1042        guards: &StylesheetGuards,
1043        ua_stylesheets: &UserAgentStylesheets,
1044    ) -> StylesheetInvalidationSet {
1045        if !self.have_added_user_agent_stylesheets {
1046            for stylesheet in &ua_stylesheets.user_agent_stylesheets {
1047                self.stylist
1048                    .append_stylesheet(stylesheet.clone(), guards.ua_or_user);
1049                self.load_all_web_fonts_from_stylesheet_with_guard(
1050                    stylesheet,
1051                    guards.ua_or_user,
1052                    &reflow_request.document_context,
1053                );
1054            }
1055
1056            if document.is_html_document() {
1057                self.stylist.append_stylesheet(
1058                    ua_stylesheets.html_mode_stylesheet.clone(),
1059                    guards.ua_or_user,
1060                );
1061                self.load_all_web_fonts_from_stylesheet_with_guard(
1062                    &ua_stylesheets.html_mode_stylesheet,
1063                    guards.ua_or_user,
1064                    &reflow_request.document_context,
1065                );
1066            }
1067
1068            for user_stylesheet in self.user_stylesheets.iter() {
1069                self.stylist
1070                    .append_stylesheet(user_stylesheet.clone(), guards.ua_or_user);
1071                self.load_all_web_fonts_from_stylesheet_with_guard(
1072                    user_stylesheet,
1073                    guards.ua_or_user,
1074                    &reflow_request.document_context,
1075                );
1076            }
1077
1078            if self.stylist.quirks_mode() == QuirksMode::Quirks {
1079                self.stylist.append_stylesheet(
1080                    ua_stylesheets.quirks_mode_stylesheet.clone(),
1081                    guards.ua_or_user,
1082                );
1083                self.load_all_web_fonts_from_stylesheet_with_guard(
1084                    &ua_stylesheets.quirks_mode_stylesheet,
1085                    guards.ua_or_user,
1086                    &reflow_request.document_context,
1087                );
1088            }
1089            self.have_added_user_agent_stylesheets = true;
1090        }
1091
1092        if reflow_request.stylesheets_changed() {
1093            self.stylist
1094                .force_stylesheet_origins_dirty(Origin::Author.into());
1095        }
1096
1097        document.flush_shadow_root_stylesheets_if_necessary(&mut self.stylist, guards.author);
1098
1099        self.stylist.flush(guards)
1100    }
1101
1102    #[servo_tracing::instrument(skip_all)]
1103    fn restyle_and_build_trees(
1104        &mut self,
1105        reflow_request: &mut ReflowRequest,
1106        document: ServoDangerousStyleDocument<'_>,
1107        root_element: ServoLayoutElement<'_>,
1108        image_resolver: &Arc<ImageResolver>,
1109    ) -> (ReflowPhasesRun, IFrameSizes) {
1110        let mut snapshot_map = SnapshotMap::new();
1111        let _snapshot_setter = match reflow_request.restyle.as_mut() {
1112            Some(restyle) => SnapshotSetter::new(restyle, &mut snapshot_map),
1113            None => return Default::default(),
1114        };
1115
1116        let shared_locks = document.shared_style_locks();
1117        let user_agent_stylesheets = get_ua_stylesheets(&shared_locks.ua_or_user);
1118        let guards = StylesheetGuards {
1119            author: &shared_locks.author.read(),
1120            ua_or_user: &shared_locks.ua_or_user.read(),
1121        };
1122
1123        let rayon_pool = STYLE_THREAD_POOL.lock();
1124        let rayon_pool = rayon_pool.pool();
1125        let rayon_pool = rayon_pool.as_ref();
1126
1127        let device_has_changed = std::mem::replace(&mut self.device_has_changed, false);
1128        let dangerous_root_element = unsafe { root_element.dangerous_style_element() };
1129        if device_has_changed {
1130            let sheet_origins_affected_by_device_change = self
1131                .stylist
1132                .media_features_change_changed_style(&guards, self.device());
1133            self.stylist
1134                .force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
1135
1136            if let Some(mut data) = dangerous_root_element.mutate_data() {
1137                data.hint.insert(RestyleHint::recascade_subtree());
1138            }
1139        }
1140
1141        self.prepare_stylist_for_reflow(reflow_request, document, &guards, &user_agent_stylesheets)
1142            .process_style(dangerous_root_element, Some(&snapshot_map));
1143
1144        if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
1145            // Need to manually force layout to build a new display list regardless of whether the box tree
1146            // changed or not.
1147            self.need_new_display_list.set(true);
1148        }
1149
1150        let layout_context = LayoutContext {
1151            style_context: self.build_shared_style_context(
1152                guards,
1153                &snapshot_map,
1154                reflow_request.animation_timeline_value,
1155                &reflow_request.animations,
1156                match reflow_request.stylesheets_changed() {
1157                    true => TraversalFlags::ForCSSRuleChanges,
1158                    false => TraversalFlags::empty(),
1159                },
1160            ),
1161            font_context: self.font_context.clone(),
1162            iframe_sizes: Mutex::default(),
1163            use_rayon: rayon_pool.is_some(),
1164            image_resolver: image_resolver.clone(),
1165            painter_id: self.webview_id.into(),
1166        };
1167
1168        let restyle = reflow_request
1169            .restyle
1170            .as_ref()
1171            .expect("Should not get here if there is not restyle.");
1172
1173        let recalc_style_traversal;
1174        let dirty_root;
1175        {
1176            let _span = profile_traits::trace_span!("Styling").entered();
1177
1178            let original_dirty_root = unsafe {
1179                ServoLayoutNode::new(&restyle.dirty_root.unwrap())
1180                    .as_element()
1181                    .unwrap()
1182                    .dangerous_style_element()
1183            };
1184
1185            recalc_style_traversal = RecalcStyle::new(&layout_context);
1186            let token = {
1187                let shared = DomTraversal::<ServoDangerousStyleElement>::shared_context(
1188                    &recalc_style_traversal,
1189                );
1190                RecalcStyle::pre_traverse(original_dirty_root, shared)
1191            };
1192
1193            if !token.should_traverse() {
1194                layout_context.style_context.stylist.rule_tree().maybe_gc();
1195                return Default::default();
1196            }
1197
1198            dirty_root = driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
1199        }
1200
1201        let root_node = root_element.as_node();
1202        let damage_from_environment = if device_has_changed {
1203            RestyleDamage::RELAYOUT
1204        } else {
1205            Default::default()
1206        };
1207
1208        let mut box_tree = self.box_tree.borrow_mut();
1209        let damage = {
1210            let box_tree = &mut *box_tree;
1211            let mut compute_damage_and_build_box_tree = || {
1212                compute_damage_and_rebuild_box_tree(
1213                    box_tree,
1214                    &layout_context,
1215                    dirty_root.layout_node(),
1216                    root_node,
1217                    damage_from_environment,
1218                )
1219            };
1220
1221            if let Some(pool) = rayon_pool {
1222                pool.install(compute_damage_and_build_box_tree)
1223            } else {
1224                compute_damage_and_build_box_tree()
1225            }
1226        };
1227
1228        if damage.contains(RestyleDamage::REBUILD_STACKING_CONTEXT) {
1229            self.need_new_stacking_context_tree.set(true);
1230        }
1231        if damage.contains(RestyleDamage::REPAINT) {
1232            self.need_new_display_list.set(true);
1233        }
1234
1235        if !damage.contains(RestyleDamage::RELAYOUT) {
1236            if damage.contains(RestyleDamage::RECALCULATE_OVERFLOW) {
1237                assert!(self.need_new_display_list.get());
1238                assert!(self.need_new_stacking_context_tree.get());
1239                self.fragment_tree
1240                    .borrow()
1241                    .as_ref()
1242                    .expect("Should always have a FragmentTree when layout unnecessary")
1243                    .clear_scrollable_overflow();
1244            }
1245
1246            layout_context.style_context.stylist.rule_tree().maybe_gc();
1247            return (ReflowPhasesRun::empty(), IFrameSizes::default());
1248        }
1249
1250        let box_tree = &*box_tree;
1251        let viewport_size = self.stylist.device().au_viewport_size();
1252        let run_layout = || {
1253            box_tree
1254                .as_ref()
1255                .unwrap()
1256                .layout(recalc_style_traversal.context(), viewport_size)
1257        };
1258        let fragment_tree = Rc::new(if let Some(pool) = rayon_pool {
1259            pool.install(run_layout)
1260        } else {
1261            run_layout()
1262        });
1263
1264        *self.fragment_tree.borrow_mut() = Some(fragment_tree);
1265
1266        if self.debug.is_enabled(DiagnosticsLoggingOption::StyleTree) {
1267            println!(
1268                "{:?}",
1269                ShowSubtreeDataAndPrimaryValues(dangerous_root_element.as_node())
1270            );
1271        }
1272        if self.debug.is_enabled(DiagnosticsLoggingOption::RuleTree) {
1273            recalc_style_traversal
1274                .context()
1275                .style_context
1276                .stylist
1277                .rule_tree()
1278                .dump_stdout(&layout_context.style_context.guards);
1279        }
1280
1281        // GC the rule tree if some heuristics are met.
1282        layout_context.style_context.stylist.rule_tree().maybe_gc();
1283
1284        let mut iframe_sizes = layout_context.iframe_sizes.lock();
1285        (
1286            ReflowPhasesRun::RanLayout,
1287            std::mem::take(&mut *iframe_sizes),
1288        )
1289    }
1290
1291    fn build_stacking_context_tree_for_reflow(&self, reflow_request: &ReflowRequest) -> bool {
1292        if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1293            .contains(ReflowPhases::StackingContextTreeConstruction)
1294        {
1295            return false;
1296        }
1297        if !self.need_new_stacking_context_tree.get() {
1298            return false;
1299        }
1300
1301        self.build_stacking_context_tree(reflow_request.viewport_details)
1302    }
1303
1304    #[servo_tracing::instrument(name = "Stacking Context Tree Construction", skip_all)]
1305    fn build_stacking_context_tree(&self, viewport_details: ViewportDetails) -> bool {
1306        let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1307            return false;
1308        };
1309
1310        let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1311        let old_scroll_offsets = stacking_context_tree
1312            .as_ref()
1313            .map(|tree| tree.paint_info.scroll_tree.scroll_offsets());
1314
1315        // This will be done during `StackingContextTree::new` below
1316        self.need_containing_block_calculation.set(false);
1317
1318        // Build the StackingContextTree. This turns the `FragmentTree` into a
1319        // tree of fragments in CSS painting order and also creates all
1320        // applicable spatial and clip nodes.
1321        let mut new_stacking_context_tree = StackingContextTree::new(
1322            fragment_tree,
1323            viewport_details,
1324            self.id.into(),
1325            !self.have_ever_generated_display_list.get(),
1326            &self.debug,
1327        );
1328
1329        // When a new StackingContextTree is built, it contains a freshly built
1330        // ScrollTree. We want to preserve any existing scroll offsets in that tree,
1331        // adjusted by any new scroll constraints.
1332        if let Some(old_scroll_offsets) = old_scroll_offsets {
1333            new_stacking_context_tree
1334                .paint_info
1335                .scroll_tree
1336                .set_all_scroll_offsets(&old_scroll_offsets);
1337        }
1338
1339        if self.debug.is_enabled(DiagnosticsLoggingOption::ScrollTree) {
1340            new_stacking_context_tree
1341                .paint_info
1342                .scroll_tree
1343                .debug_print();
1344        }
1345
1346        *stacking_context_tree = Some(new_stacking_context_tree);
1347
1348        // The stacking context tree is up-to-date again.
1349        self.need_new_stacking_context_tree.set(false);
1350        assert!(self.need_new_display_list.get());
1351
1352        true
1353    }
1354
1355    /// Build the display list for the current layout and send it to the renderer. If no display
1356    /// list is built, returns false.
1357    #[servo_tracing::instrument(name = "Display List Construction", skip_all)]
1358    fn build_display_list(
1359        &self,
1360        reflow_request: &ReflowRequest,
1361        image_resolver: &Arc<ImageResolver>,
1362        reflow_statistics: &mut ReflowStatistics,
1363    ) -> bool {
1364        if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1365            .contains(ReflowPhases::DisplayListConstruction)
1366        {
1367            return false;
1368        }
1369        let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1370            return false;
1371        };
1372        let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1373        let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1374            return false;
1375        };
1376
1377        // If a non-display-list-generating reflow updated layout in a previous refow, we
1378        // cannot skip display list generation here the next time a display list is
1379        // requested.
1380        if !self.need_new_display_list.get() {
1381            return false;
1382        }
1383
1384        // TODO: Eventually this should be set when `paint_info` is created, but that requires
1385        // ensuring that the Epoch is passed to any method that can creates `StackingContextTree`.
1386        stacking_context_tree.paint_info.epoch = reflow_request.epoch;
1387
1388        let mut paint_timing_handler = self.paint_timing_handler.borrow_mut();
1389        // This ensures that we only create the PaintTimingHandler once per layout thread.
1390        let paint_timing_handler = match paint_timing_handler.as_mut() {
1391            Some(paint_timing_handler) => paint_timing_handler,
1392            None => {
1393                *paint_timing_handler = Some(PaintTimingHandler::new(
1394                    stacking_context_tree
1395                        .paint_info
1396                        .viewport_details
1397                        .layout_size(),
1398                ));
1399                paint_timing_handler.as_mut().unwrap()
1400            },
1401        };
1402
1403        let built_display_list = DisplayListBuilder::build(
1404            stacking_context_tree,
1405            fragment_tree,
1406            image_resolver.clone(),
1407            self.device().device_pixel_ratio(),
1408            reflow_request.highlighted_dom_node,
1409            &self.debug,
1410            paint_timing_handler,
1411            reflow_statistics,
1412        );
1413        self.paint_api.send_display_list(
1414            self.webview_id,
1415            &stacking_context_tree.paint_info,
1416            built_display_list,
1417        );
1418
1419        if paint_timing_handler.did_lcp_candidate_update() &&
1420            let Some(lcp_candidate) = paint_timing_handler.largest_contentful_paint_candidate()
1421        {
1422            self.paint_api.send_lcp_candidate(
1423                lcp_candidate,
1424                self.webview_id,
1425                self.id,
1426                stacking_context_tree.paint_info.epoch,
1427            );
1428            paint_timing_handler.unset_lcp_candidate_updated();
1429        }
1430
1431        let (keys, instance_keys) = self
1432            .font_context
1433            .collect_unused_webrender_resources(false /* all */);
1434        self.paint_api
1435            .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys);
1436        self.last_display_list_was_empty.set(false);
1437        self.have_ever_generated_display_list.set(true);
1438        self.need_new_display_list.set(false);
1439        self.previously_highlighted_dom_node
1440            .set(reflow_request.highlighted_dom_node);
1441        true
1442    }
1443
1444    fn set_scroll_offset_from_script(
1445        &self,
1446        external_scroll_id: ExternalScrollId,
1447        offset: LayoutVector2D,
1448    ) -> bool {
1449        let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1450        let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1451            return false;
1452        };
1453
1454        if let Some(offset) = stacking_context_tree
1455            .paint_info
1456            .scroll_tree
1457            .set_scroll_offset_for_node_with_external_scroll_id(
1458                external_scroll_id,
1459                offset,
1460                ScrollType::Script,
1461            )
1462        {
1463            self.paint_api.scroll_node_by_delta(
1464                self.webview_id,
1465                self.id.into(),
1466                offset,
1467                external_scroll_id,
1468            );
1469            true
1470        } else {
1471            false
1472        }
1473    }
1474
1475    /// Returns profiling information which is passed to the time profiler.
1476    fn profiler_metadata(&self) -> Option<TimerMetadata> {
1477        Some(TimerMetadata {
1478            url: self.url.to_string(),
1479            iframe: if self.is_iframe {
1480                TimerMetadataFrameType::IFrame
1481            } else {
1482                TimerMetadataFrameType::RootWindow
1483            },
1484            incremental: if self.have_ever_generated_display_list.get() {
1485                TimerMetadataReflowType::Incremental
1486            } else {
1487                TimerMetadataReflowType::FirstReflow
1488            },
1489        })
1490    }
1491
1492    /// Clear all cached layout trees and send an empty display list to paint.
1493    fn clear_layout_trees_and_send_empty_display_list(
1494        &self,
1495        reflow_request: &ReflowRequest,
1496    ) -> Option<ReflowResult> {
1497        // Clear layout trees.
1498        self.box_tree.borrow_mut().take();
1499        self.fragment_tree.borrow_mut().take();
1500        self.stacking_context_tree.borrow_mut().take();
1501
1502        // Send empty display list.
1503        let paint_info = PaintDisplayListInfo::new(
1504            reflow_request.viewport_details,
1505            Size2D::zero(),
1506            self.id.into(),
1507            reflow_request.epoch,
1508            AxesScrollSensitivity {
1509                x: ScrollType::InputEvents | ScrollType::Script,
1510                y: ScrollType::InputEvents | ScrollType::Script,
1511            },
1512            !self.have_ever_generated_display_list.get(),
1513        );
1514        let mut builder = webrender_api::DisplayListBuilder::new(paint_info.pipeline_id);
1515        builder.begin();
1516        let (_, empty_display_list) = builder.end();
1517
1518        self.paint_api
1519            .send_display_list(self.webview_id, &paint_info, empty_display_list);
1520        self.last_display_list_was_empty.set(true);
1521        self.have_ever_generated_display_list.set(true);
1522
1523        Some(ReflowResult {
1524            reflow_phases_run: ReflowPhasesRun::BuiltDisplayList,
1525            ..Default::default()
1526        })
1527    }
1528
1529    pub(crate) fn ensure_containing_block_calculation(&self) {
1530        if !self.need_containing_block_calculation.get() {
1531            return;
1532        }
1533        let fragment_tree = self.fragment_tree.borrow();
1534        fragment_tree.as_ref().expect("missing fragment tree").find(
1535            |fragment, _level, containing_block| {
1536                fragment.set_containing_block(containing_block);
1537                None::<()>
1538            },
1539        );
1540        self.need_containing_block_calculation.set(false)
1541    }
1542}
1543
1544fn get_ua_stylesheets(shared_lock: &SharedRwLock) -> Rc<UserAgentStylesheets> {
1545    // There is an assumption here that there is only a single ScriptThread per thread, which
1546    // is currently the case in Servo. If this were to change, these user agent stylesheets
1547    // would need to be managed by the ScriptThread instance.
1548    thread_local! {
1549        static USER_AGENT_STYLESHEETS: OnceCell<Rc<UserAgentStylesheets>> = const { OnceCell::new() };
1550    }
1551
1552    fn parse_ua_stylesheet(
1553        shared_lock: &SharedRwLock,
1554        filename: &str,
1555        content: &[u8],
1556    ) -> DocumentStyleSheet {
1557        let url = Url::parse(&format!("chrome://resources/{filename}")).unwrap_or_else(|_| {
1558            panic!("Could not parse user stylesheet URL: {filename}");
1559        });
1560        DocumentStyleSheet(ServoArc::new(Stylesheet::from_bytes(
1561            content,
1562            url.into(),
1563            None,
1564            None,
1565            Origin::UserAgent,
1566            ServoArc::new(shared_lock.wrap(MediaList::empty())),
1567            shared_lock.clone(),
1568            None,
1569            None,
1570            QuirksMode::NoQuirks,
1571        )))
1572    }
1573
1574    USER_AGENT_STYLESHEETS.with(|user_stylesheets| {
1575        user_stylesheets
1576            .get_or_init(|| {
1577                // FIXME: presentational-hints.css should be at author origin with zero specificity.
1578                //        (Does it make a difference?)
1579                let user_agent_stylesheets = vec![
1580                    parse_ua_stylesheet(shared_lock, "user-agent.css", USER_AGENT_CSS),
1581                    parse_ua_stylesheet(shared_lock, "servo.css", SERVO_CSS),
1582                    parse_ua_stylesheet(
1583                        shared_lock,
1584                        "presentational-hints.css",
1585                        PRESENTATIONAL_HINTS_CSS,
1586                    ),
1587                ];
1588
1589                let html_mode_stylesheet =
1590                    parse_ua_stylesheet(shared_lock, "html-mode.css", HTML_MODE_CSS);
1591
1592                let quirks_mode_stylesheet =
1593                    parse_ua_stylesheet(shared_lock, "quirks-mode.css", QUIRKS_MODE_CSS);
1594
1595                Rc::new(UserAgentStylesheets {
1596                    user_agent_stylesheets,
1597                    html_mode_stylesheet,
1598                    quirks_mode_stylesheet,
1599                })
1600            })
1601            .clone()
1602    })
1603}
1604
1605/// This structure holds the user-agent stylesheets.
1606pub struct UserAgentStylesheets {
1607    /// The user agent stylesheets.
1608    pub user_agent_stylesheets: Vec<DocumentStyleSheet>,
1609    /// The user agent stylesheet for HTML documents.
1610    pub html_mode_stylesheet: DocumentStyleSheet,
1611    /// The quirks mode stylesheet.
1612    pub quirks_mode_stylesheet: DocumentStyleSheet,
1613}
1614
1615struct RegisteredPainterImpl {
1616    painter: Box<dyn Painter>,
1617    name: Atom,
1618    // FIXME: Should be a PrecomputedHashMap.
1619    properties: FxHashMap<Atom, PropertyId>,
1620}
1621
1622impl SpeculativePainter for RegisteredPainterImpl {
1623    fn speculatively_draw_a_paint_image(
1624        &self,
1625        properties: Vec<(Atom, String)>,
1626        arguments: Vec<String>,
1627    ) {
1628        self.painter
1629            .speculatively_draw_a_paint_image(properties, arguments);
1630    }
1631}
1632
1633impl RegisteredSpeculativePainter for RegisteredPainterImpl {
1634    fn properties(&self) -> &FxHashMap<Atom, PropertyId> {
1635        &self.properties
1636    }
1637    fn name(&self) -> Atom {
1638        self.name.clone()
1639    }
1640}
1641
1642impl Painter for RegisteredPainterImpl {
1643    fn draw_a_paint_image(
1644        &self,
1645        size: Size2D<f32, CSSPixel>,
1646        device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
1647        properties: Vec<(Atom, String)>,
1648        arguments: Vec<String>,
1649    ) -> Result<DrawAPaintImageResult, PaintWorkletError> {
1650        self.painter
1651            .draw_a_paint_image(size, device_pixel_ratio, properties, arguments)
1652    }
1653}
1654
1655struct RegisteredPaintersImpl(HashMap<Atom, RegisteredPainterImpl>);
1656
1657impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1658    fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1659        self.0
1660            .get(name)
1661            .map(|painter| painter as &dyn RegisteredSpeculativePainter)
1662    }
1663}
1664
1665struct LayoutFontMetricsProvider(Arc<FontContext>);
1666
1667impl FontMetricsProvider for LayoutFontMetricsProvider {
1668    fn query_font_metrics(
1669        &self,
1670        _vertical: bool,
1671        font: &Font,
1672        base_size: CSSPixelLength,
1673        _flags: QueryFontMetricsFlags,
1674    ) -> FontMetrics {
1675        let font_context = &self.0;
1676        let font_group = self
1677            .0
1678            .font_group_with_size(ServoArc::new(font.clone()), base_size.into());
1679
1680        let Some(first_font_metrics) = font_group
1681            .first(font_context)
1682            .map(|font| font.metrics.clone())
1683        else {
1684            return Default::default();
1685        };
1686
1687        // Only use the x-height of this font if it is non-zero. Some fonts return
1688        // inaccurate metrics, which shouldn't be used.
1689        let x_height = Some(first_font_metrics.x_height)
1690            .filter(|x_height| !x_height.is_zero())
1691            .map(CSSPixelLength::from);
1692
1693        let zero_advance_measure = first_font_metrics
1694            .zero_horizontal_advance
1695            .or_else(|| {
1696                font_group
1697                    .find_by_codepoint(font_context, '0', None, Language::UND)?
1698                    .metrics
1699                    .zero_horizontal_advance
1700            })
1701            .map(CSSPixelLength::from);
1702
1703        let ic_width = first_font_metrics
1704            .ic_horizontal_advance
1705            .or_else(|| {
1706                font_group
1707                    .find_by_codepoint(font_context, '\u{6C34}', None, Language::UND)?
1708                    .metrics
1709                    .ic_horizontal_advance
1710            })
1711            .map(CSSPixelLength::from);
1712
1713        FontMetrics {
1714            x_height,
1715            zero_advance_measure,
1716            cap_height: None,
1717            ic_width,
1718            ascent: first_font_metrics.ascent.into(),
1719            script_percent_scale_down: None,
1720            script_script_percent_scale_down: None,
1721        }
1722    }
1723
1724    fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
1725        Length::new(match generic {
1726            GenericFontFamily::Monospace => pref!(fonts_default_monospace_size),
1727            _ => pref!(fonts_default_size),
1728        } as f32)
1729        .max(Length::new(0.0))
1730    }
1731}
1732
1733impl Debug for LayoutFontMetricsProvider {
1734    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1735        f.debug_tuple("LayoutFontMetricsProvider").finish()
1736    }
1737}
1738
1739struct SnapshotSetter<'dom> {
1740    elements_with_snapshot: Vec<ServoLayoutElement<'dom>>,
1741}
1742
1743impl SnapshotSetter<'_> {
1744    fn new(restyle: &mut ReflowRequestRestyle, snapshot_map: &mut SnapshotMap) -> Self {
1745        debug!("Draining restyles: {}", restyle.pending_restyles.len());
1746        let restyles = std::mem::take(&mut restyle.pending_restyles);
1747
1748        let elements_with_snapshot: Vec<_> = restyles
1749            .iter()
1750            .filter(|r| r.1.snapshot.is_some())
1751            .map(|r| unsafe { ServoLayoutNode::new(&r.0).as_element().unwrap() })
1752            .collect();
1753
1754        for (element, restyle) in restyles {
1755            let element = unsafe { ServoLayoutNode::new(&element).as_element().unwrap() };
1756
1757            // If we haven't styled this node yet, we don't need to track a
1758            // restyle.
1759            let Some(mut style_data) = element
1760                .style_data()
1761                .map(|data| data.element_data.borrow_mut())
1762            else {
1763                element.unset_snapshot_flags();
1764                continue;
1765            };
1766
1767            debug!("Noting restyle for {:?}: {:?}", element, style_data);
1768            if let Some(s) = restyle.snapshot {
1769                element.set_has_snapshot();
1770                snapshot_map.insert(element.as_node().opaque(), s);
1771            }
1772
1773            // Stash the data on the element for processing by the style system.
1774            style_data.hint.insert(restyle.hint);
1775            style_data.damage = restyle.damage;
1776        }
1777        Self {
1778            elements_with_snapshot,
1779        }
1780    }
1781}
1782
1783impl Drop for SnapshotSetter<'_> {
1784    fn drop(&mut self) {
1785        for element in &self.elements_with_snapshot {
1786            element.unset_snapshot_flags();
1787        }
1788    }
1789}
1790
1791bitflags! {
1792    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1793    pub struct ReflowPhases: u8 {
1794        const StackingContextTreeConstruction = 1 << 0;
1795        const DisplayListConstruction = 1 << 1;
1796    }
1797}
1798
1799impl ReflowPhases {
1800    /// Return the necessary phases of layout for the given [`ReflowGoal`]. Note that all
1801    /// [`ReflowGoals`] need the basic restyle + box tree layout + fragment tree layout,
1802    /// so [`ReflowPhases::empty()`] implies that.
1803    fn necessary(reflow_goal: &ReflowGoal) -> Self {
1804        match reflow_goal {
1805            ReflowGoal::LayoutQuery(query) => match query {
1806                QueryMsg::NodesFromPointQuery => {
1807                    Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1808                },
1809                QueryMsg::BoxArea |
1810                QueryMsg::BoxAreas |
1811                QueryMsg::ElementsFromPoint |
1812                QueryMsg::OffsetParentQuery |
1813                QueryMsg::ResolvedStyleQuery |
1814                QueryMsg::ScrollingAreaOrOffsetQuery |
1815                QueryMsg::TextIndexQuery => Self::StackingContextTreeConstruction,
1816                QueryMsg::ClientRectQuery |
1817                QueryMsg::CurrentCSSZoomQuery |
1818                QueryMsg::EffectiveOverflow |
1819                QueryMsg::ElementInnerOuterTextQuery |
1820                QueryMsg::InnerWindowDimensionsQuery |
1821                QueryMsg::PaddingQuery |
1822                QueryMsg::ResolvedFontStyleQuery |
1823                QueryMsg::ScrollParentQuery |
1824                QueryMsg::StyleQuery => Self::empty(),
1825            },
1826            ReflowGoal::UpdateScrollNode(..) | ReflowGoal::UpdateTheRendering => {
1827                Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1828            },
1829        }
1830    }
1831}