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