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