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