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