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};
20use fonts_traits::{StylesheetWebFontLoadFinishedCallback, WebFontSetDifference};
21use icu_locale_core::subtags::Language;
22use layout_api::{
23    AxesOverflow, BoxAreaType, CSSPixelRectVec, DangerousStyleNode, HitTestFlags, HitTestResult,
24    IFrameSizes, Layout, LayoutConfig, LayoutDamage, LayoutElement, LayoutFactory, LayoutNode,
25    NodeRenderingType, OffsetParentResponse, PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun,
26    ReflowRequest, 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::id::{PipelineId, WebViewId};
48use servo_base::text::Utf32CodeUnits;
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::{AccessibilityContext, AccessibilityDamageMap, 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::force_accessibility_update()].
229    force_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_viewport: Point2D<Au, CSSPixel>,
574    ) -> Option<(OpaqueNode, Utf32CodeUnits)> {
575        with_layout_state(|| {
576            let node = unsafe { ServoLayoutNode::new(&node) };
577            let stacking_context_tree = self.stacking_context_tree.borrow();
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_viewport,
583            )
584        })
585    }
586
587    #[servo_tracing::instrument(skip_all)]
588    fn hit_test(
589        &self,
590        flags: HitTestFlags,
591        point: webrender_api::units::LayoutPoint,
592    ) -> HitTestResult {
593        with_layout_state(|| {
594            self.stacking_context_tree
595                .borrow_mut()
596                .as_mut()
597                .map(|tree| HitTest::run(flags, tree, point))
598                .unwrap_or_default()
599        })
600    }
601
602    #[servo_tracing::instrument(skip_all)]
603    fn query_effective_overflow(&self, node: TrustedNodeAddress) -> Option<AxesOverflow> {
604        with_layout_state(|| {
605            let node = unsafe { ServoLayoutNode::new(&node) };
606            process_effective_overflow_query(node)
607        })
608    }
609
610    fn exit_now(&mut self) {}
611
612    fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps) {
613        // TODO: Measure more than just display list, stylist, and font context.
614        let formatted_url = &format!("url({})", self.url);
615        reports.push(Report {
616            path: path![formatted_url, "layout-thread", "display-list"],
617            kind: ReportKind::ExplicitJemallocHeapSize,
618            size: 0,
619        });
620
621        reports.push(Report {
622            path: path![formatted_url, "layout-thread", "stylist"],
623            kind: ReportKind::ExplicitJemallocHeapSize,
624            size: self.stylist.size_of(ops),
625        });
626
627        reports.push(Report {
628            path: path![formatted_url, "layout-thread", "font-context"],
629            kind: ReportKind::ExplicitJemallocHeapSize,
630            size: self.font_context.conditional_size_of(ops),
631        });
632
633        reports.push(Report {
634            path: path![formatted_url, "layout-thread", "box-tree"],
635            kind: ReportKind::ExplicitJemallocHeapSize,
636            size: self
637                .box_tree
638                .borrow()
639                .as_ref()
640                .map_or(0, |tree| tree.conditional_size_of(ops)),
641        });
642
643        reports.push(Report {
644            path: path![formatted_url, "layout-thread", "fragment-tree"],
645            kind: ReportKind::ExplicitJemallocHeapSize,
646            size: self
647                .fragment_tree
648                .borrow()
649                .as_ref()
650                .map(|tree| tree.conditional_size_of(ops))
651                .unwrap_or_default(),
652        });
653
654        reports.push(Report {
655            path: path![formatted_url, "layout-thread", "stacking-context-tree"],
656            kind: ReportKind::ExplicitJemallocHeapSize,
657            size: self.stacking_context_tree.size_of(ops),
658        });
659
660        reports.extend(self.image_cache.memory_reports(formatted_url, ops));
661    }
662
663    fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
664        self.stylist.set_quirks_mode(quirks_mode);
665    }
666
667    fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult> {
668        time_profile!(
669            profile_time::ProfilerCategory::Layout,
670            self.profiler_metadata(),
671            self.time_profiler_chan.clone(),
672            || with_layout_state(|| self.handle_reflow(reflow_request)),
673        )
674    }
675
676    fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails) {
677        with_layout_state(|| {
678            if self.stacking_context_tree.borrow().is_some() &&
679                !self.need_new_stacking_context_tree.get()
680            {
681                return;
682            }
683            self.build_stacking_context_tree(viewport_details);
684        })
685    }
686
687    fn register_paint_worklet_modules(
688        &mut self,
689        _name: Atom,
690        _properties: Vec<Atom>,
691        _painter: Box<dyn Painter>,
692    ) {
693    }
694
695    fn set_scroll_offsets_from_renderer(
696        &mut self,
697        scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
698    ) {
699        let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
700        let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
701            warn!("Received scroll offsets before finishing layout.");
702            return;
703        };
704
705        let offsets = stacking_context_tree
706            .paint_info
707            .scroll_tree
708            .set_all_scroll_offsets(scroll_states);
709
710        // Accessibility node bounds are relative to the viewport origin, so a renderer scroll
711        // makes every one of them stale without any reflow occurring. Requesting an accessibility
712        // update schedules a rendering update and prevents that update's reflow from being skipped,
713        // allowing the bounds to be recomputed against the new scroll offsets. See #47161 for a
714        // transform-based alternative to recomputing every node.
715        if self.accessibility_active() {
716            let mut accessibility_tree = self.accessibility_tree.borrow_mut();
717            if let Some(accessibility_tree) = accessibility_tree.as_mut() {
718                accessibility_tree.add_pending_scroll_updates(offsets);
719            };
720
721            self.set_force_accessibility_update();
722        }
723    }
724
725    fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D> {
726        self.stacking_context_tree
727            .borrow_mut()
728            .as_mut()
729            .and_then(|tree| tree.paint_info.scroll_tree.scroll_offset(id))
730    }
731
732    fn needs_new_display_list(&self) -> bool {
733        self.need_new_display_list.get()
734    }
735
736    fn set_needs_new_display_list(&self) {
737        self.need_new_display_list.set(true);
738    }
739
740    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function>
741    fn stylist_mut(&mut self) -> &mut Stylist {
742        &mut self.stylist
743    }
744
745    fn set_accessibility_active(&self, active: bool, epoch: Epoch) {
746        self.accessibility_active.set(active);
747        if !active {
748            self.accessibility_tree.replace(None);
749            return;
750        }
751
752        self.set_force_accessibility_update();
753        let mut accessibility_tree = self.accessibility_tree.borrow_mut();
754        if accessibility_tree.is_some() {
755            return;
756        }
757        *accessibility_tree = Some(AccessibilityTree::new(self.id.into(), epoch));
758    }
759
760    fn accessibility_active(&self) -> bool {
761        self.accessibility_active.get()
762    }
763
764    fn force_accessibility_update(&self) -> bool {
765        self.force_accessibility_update.get()
766    }
767
768    fn set_force_accessibility_update(&self) {
769        self.force_accessibility_update.set(true);
770    }
771
772    fn font_context(&self) -> &Arc<FontContext> {
773        &self.font_context
774    }
775}
776
777impl LayoutThread {
778    fn new(config: LayoutConfig) -> LayoutThread {
779        // Let webrender know about this pipeline by sending an empty display list.
780        config
781            .paint_api
782            .send_initial_transaction(config.webview_id, config.id.into());
783
784        let mut font = Font::initial_values();
785        let default_font_size = pref!(fonts_default_size);
786        font.font_size = FontSize {
787            computed_size: NonNegativeLength::new(default_font_size as f32),
788            used_size: NonNegativeLength::new(default_font_size as f32),
789            keyword_info: KeywordInfo::medium(),
790        };
791
792        // The device pixel ratio is incorrect (it does not have the hidpi value),
793        // but it will be set correctly when the initial reflow takes place.
794        let device = Device::new(
795            MediaType::screen(),
796            QuirksMode::NoQuirks,
797            config.viewport_details.size,
798            config.viewport_details.device_size.cast_unit(),
799            Scale::new(config.viewport_details.hidpi_scale_factor.get()),
800            Box::new(LayoutFontMetricsProvider(config.font_context.clone())),
801            ComputedValues::initial_values_with_font_override(font),
802            config.theme.into(),
803            PointerCapabilities::default(),
804            PointerCapabilities::default(),
805        );
806
807        let locked_script_channel = Mutex::new(config.script_chan.clone());
808        let pipeline_id = config.id;
809        let web_font_finished_loading_callback = move |event| {
810            let _ = locked_script_channel
811                .lock()
812                .send(ScriptThreadMessage::WebFontLoadFinished(pipeline_id, event));
813        };
814
815        LayoutThread {
816            id: config.id,
817            webview_id: config.webview_id,
818            url: config.url,
819            is_iframe: config.is_iframe,
820            time_profiler_chan: config.time_profiler_chan,
821            embedder_chan: config.embedder_chan.clone(),
822            registered_painters: RegisteredPaintersImpl(Default::default()),
823            image_cache: config.image_cache,
824            font_context: config.font_context,
825            have_added_user_agent_stylesheets: false,
826            have_ever_generated_display_list: Cell::new(false),
827            last_display_list_was_empty: Cell::new(true),
828            device_has_changed: false,
829            need_containing_block_calculation: Cell::new(false),
830            need_new_display_list: Cell::new(false),
831            need_new_stacking_context_tree: Cell::new(false),
832            box_tree: Default::default(),
833            fragment_tree: Default::default(),
834            stacking_context_tree: Default::default(),
835            paint_api: config.paint_api,
836            stylist: Stylist::new(device, QuirksMode::NoQuirks),
837            resolved_images_cache: Default::default(),
838            debug: opts::get().debug.clone(),
839            previously_highlighted_dom_node: Cell::new(None),
840            paint_timing_handler: Default::default(),
841            user_stylesheets: config.user_stylesheets,
842            accessibility_active: Cell::new(false),
843            accessibility_tree: Default::default(),
844            force_accessibility_update: Cell::new(false),
845            web_font_finished_loading_callback: Arc::new(web_font_finished_loading_callback)
846                as StylesheetWebFontLoadFinishedCallback,
847        }
848    }
849
850    fn build_shared_style_context<'a>(
851        &'a self,
852        guards: StylesheetGuards<'a>,
853        snapshot_map: &'a SnapshotMap,
854        animation_timeline_value: f64,
855        animations: &DocumentAnimationSet,
856        traversal_flags: TraversalFlags,
857    ) -> SharedStyleContext<'a> {
858        SharedStyleContext {
859            stylist: &self.stylist,
860            options: GLOBAL_STYLE_DATA.options.clone(),
861            guards,
862            visited_styles_enabled: false,
863            animations: animations.clone(),
864            registered_speculative_painters: &self.registered_painters,
865            current_time_for_animations: animation_timeline_value,
866            traversal_flags,
867            snapshot_map,
868        }
869    }
870
871    /// In some cases, if a restyle isn't necessary we can skip doing any work for layout
872    /// entirely. This check allows us to return early from layout without doing any work
873    /// at all.
874    fn can_skip_reflow_request_entirely(&self, reflow_request: &ReflowRequest) -> bool {
875        // If a restyle is necessary, restyle and reflow is a necessity.
876        if reflow_request.restyle.is_some() {
877            return false;
878        }
879        // We always need to at least build a fragment tree.
880        if self.fragment_tree.borrow().is_none() {
881            return false;
882        }
883        // If the accessibility tree needs an update, we need reflow to build the accessibility tree.
884        if self.force_accessibility_update() || reflow_request.accessibility_damage.is_some() {
885            return false;
886        }
887
888        // If we have a fragment tree and it's up-to-date and this reflow
889        // doesn't need more reflow results, we can skip the rest of layout.
890        let necessary_phases = ReflowPhases::necessary(&reflow_request.reflow_goal);
891        if necessary_phases.is_empty() {
892            return true;
893        }
894
895        // If only the stacking context tree is required, and it's up-to-date,
896        // layout is unnecessary, otherwise a layout is necessary.
897        if necessary_phases == ReflowPhases::StackingContextTreeConstruction {
898            return self.stacking_context_tree.borrow().is_some() &&
899                !self.need_new_stacking_context_tree.get();
900        }
901
902        // Otherwise, the only interesting thing is whether the current display
903        // list is up-to-date.
904        assert_eq!(
905            necessary_phases,
906            ReflowPhases::StackingContextTreeConstruction | ReflowPhases::DisplayListConstruction
907        );
908        !self.need_new_display_list.get()
909    }
910
911    fn maybe_print_reflow_event(&self, reflow_request: &ReflowRequest) {
912        if !self
913            .debug
914            .is_enabled(DiagnosticsLoggingOption::RelayoutEvent)
915        {
916            return;
917        }
918
919        println!(
920            "**** Reflow({}) => {:?}, {:?}",
921            self.id,
922            reflow_request.reflow_goal,
923            reflow_request
924                .restyle
925                .as_ref()
926                .map(|restyle| restyle.reason)
927                .unwrap_or_default()
928        );
929    }
930
931    /// Checks whether we need to update the scroll node, and report whether the
932    /// node is scrolled. We need to update the scroll node whenever it is requested.
933    fn handle_update_scroll_node_request(&self, reflow_request: &ReflowRequest) -> bool {
934        if let ReflowGoal::UpdateScrollNode(external_scroll_id, offset) = reflow_request.reflow_goal
935        {
936            self.set_scroll_offset_from_script(external_scroll_id, offset)
937        } else {
938            false
939        }
940    }
941
942    fn handle_accessibility_tree_update(
943        &self,
944        root_element: &ServoLayoutNode,
945        reflow_request: &mut ReflowRequest,
946        reflow_statistics: &mut ReflowStatistics,
947    ) -> bool {
948        let Some(accessibility_damage) = std::mem::take(&mut reflow_request.accessibility_damage)
949        else {
950            return false;
951        };
952        if !self.force_accessibility_update() && accessibility_damage.is_empty() {
953            return false;
954        }
955
956        let mut accessibility_tree = self.accessibility_tree.borrow_mut();
957        let Some(accessibility_tree) = accessibility_tree.as_mut() else {
958            return false;
959        };
960
961        let accessibility_tree = &mut *accessibility_tree;
962
963        // Check for the stacking context tree before draining any state out of `reflow_request`, so
964        // that we don't discard accessibility damage if it is missing. In practice it is always
965        // present here, since we only reach this method for an `UpdateTheRendering` reflow.
966        let stacking_context_tree = self.stacking_context_tree.borrow();
967        let Some(stacking_context_tree) = stacking_context_tree.as_ref() else {
968            return false;
969        };
970        debug_assert!(!self.need_new_stacking_context_tree.get());
971
972        let rooted_nodes =
973            std::mem::take(&mut reflow_request.rooted_nodes_for_accessibility_integrity_check);
974
975        let damage: AccessibilityDamageMap = accessibility_damage
976            .into_iter()
977            .map(|(address, damage)| {
978                let node = unsafe { ServoLayoutNode::new(&address) };
979                (node.opaque(), (node, damage))
980            })
981            .collect();
982
983        let accessibility_context = AccessibilityContext {
984            layout_thread: self,
985            stacking_context_tree,
986        };
987
988        let (tree_update, counters) = accessibility_tree.update_tree(
989            root_element,
990            damage,
991            accessibility_context,
992            rooted_nodes,
993        );
994        if let Some(tree_update) = tree_update {
995            // FIXME: Handle send error. Could have a method on accessibility tree to
996            // finalise after sending, removing accessibility damage? On fail, retain damage
997            // for next reflow, as well as retaining document.needs_accessibility_update.
998            let _ = self
999                .embedder_chan
1000                .send(EmbedderMsg::AccessibilityTreeUpdate(
1001                    self.webview_id,
1002                    tree_update,
1003                    accessibility_tree.embedder_epoch(),
1004                ));
1005        }
1006
1007        reflow_statistics.nodes_updated_from_dom = counters.nodes_updated_from_dom;
1008        reflow_statistics.nodes_updated_from_tree = counters.nodes_updated_from_tree;
1009        reflow_statistics.nodes_updated_bounds = counters.nodes_updated_bounds;
1010        reflow_statistics.nodes_in_tree_update = counters.nodes_in_tree_update;
1011
1012        self.force_accessibility_update.set(false);
1013        true
1014    }
1015
1016    /// The high-level routine that performs layout.
1017    #[servo_tracing::instrument(
1018        skip_all,
1019        fields(goal = tracing::field::debug(&reflow_request.reflow_goal))
1020    )]
1021    fn handle_reflow(&mut self, mut reflow_request: ReflowRequest) -> Option<ReflowResult> {
1022        self.maybe_print_reflow_event(&reflow_request);
1023
1024        if self.can_skip_reflow_request_entirely(&reflow_request) {
1025            // We can skip layout, but we might need to update a scroll node.
1026            return self
1027                .handle_update_scroll_node_request(&reflow_request)
1028                .then(|| ReflowResult {
1029                    reflow_phases_run: ReflowPhasesRun::UpdatedScrollNodeOffset,
1030                    ..Default::default()
1031                });
1032        }
1033
1034        let document = unsafe { ServoLayoutNode::new(&reflow_request.document) };
1035        let document = unsafe { document.dangerous_style_node() }
1036            .as_document()
1037            .unwrap();
1038        let Some(root_element) = document.root_element() else {
1039            if !self.last_display_list_was_empty.get() {
1040                return self.clear_layout_trees_and_send_empty_display_list(&reflow_request);
1041            }
1042            debug!("layout: No root node: bailing");
1043            return None;
1044        };
1045
1046        let image_resolver = Arc::new(ImageResolver {
1047            origin: reflow_request.origin.clone(),
1048            image_cache: self.image_cache.clone(),
1049            resolved_images_cache: self.resolved_images_cache.clone(),
1050            pending_images: Mutex::default(),
1051            pending_rasterization_images: Mutex::default(),
1052            pending_svg_elements_for_serialization: Mutex::default(),
1053            animating_images: reflow_request.animating_images.clone(),
1054            animation_timeline_value: reflow_request.animation_timeline_value,
1055        });
1056        let mut reflow_statistics = Default::default();
1057
1058        let (mut reflow_phases_run, iframe_sizes, changed_web_fonts) = self
1059            .restyle_and_build_trees(&mut reflow_request, document, root_element, &image_resolver);
1060        if self.build_stacking_context_tree_for_reflow(&reflow_request) {
1061            reflow_phases_run.insert(ReflowPhasesRun::BuiltStackingContextTree);
1062        }
1063        if self.build_display_list(&reflow_request, &image_resolver, &mut reflow_statistics) {
1064            reflow_phases_run.insert(ReflowPhasesRun::BuiltDisplayList);
1065        }
1066        if self.handle_update_scroll_node_request(&reflow_request) {
1067            reflow_phases_run.insert(ReflowPhasesRun::UpdatedScrollNodeOffset);
1068        }
1069        if self.handle_accessibility_tree_update(
1070            &root_element.as_node(),
1071            &mut reflow_request,
1072            &mut reflow_statistics,
1073        ) {
1074            reflow_phases_run.insert(ReflowPhasesRun::UpdatedAccessibilityTree);
1075        }
1076
1077        if self.debug.is_enabled(DiagnosticsLoggingOption::FlowTree) &&
1078            reflow_phases_run.contains(ReflowPhasesRun::RanLayout) &&
1079            let Some(fragment_tree) = &*self.fragment_tree.borrow()
1080        {
1081            fragment_tree.print();
1082        }
1083
1084        let pending_images = std::mem::take(&mut *image_resolver.pending_images.lock());
1085        let pending_rasterization_images =
1086            std::mem::take(&mut *image_resolver.pending_rasterization_images.lock());
1087        let pending_svg_elements_for_serialization =
1088            std::mem::take(&mut *image_resolver.pending_svg_elements_for_serialization.lock());
1089
1090        let lcp_candidate = self
1091            .paint_timing_handler
1092            .borrow()
1093            .as_ref()
1094            .map(|handler| handler.largest_contentful_paint_candidate())
1095            .unwrap_or_default();
1096
1097        Some(ReflowResult {
1098            reflow_phases_run,
1099            pending_images,
1100            pending_rasterization_images,
1101            pending_svg_elements_for_serialization,
1102            iframe_sizes: Some(iframe_sizes),
1103            reflow_statistics,
1104            changed_web_fonts,
1105            lcp_candidate,
1106        })
1107    }
1108
1109    #[servo_tracing::instrument(skip_all)]
1110    fn prepare_stylist_for_reflow<'dom>(
1111        &mut self,
1112        reflow_request: &ReflowRequest,
1113        document: ServoDangerousStyleDocument<'dom>,
1114        guards: &StylesheetGuards,
1115        ua_stylesheets: &UserAgentStylesheets,
1116    ) -> StylistStylesheetUpdate {
1117        let need_user_agent_stylesheet_addition = !self.have_added_user_agent_stylesheets;
1118        if need_user_agent_stylesheet_addition {
1119            for stylesheet in &ua_stylesheets.user_agent_stylesheets {
1120                self.stylist
1121                    .append_stylesheet(stylesheet.clone(), guards.ua_or_user);
1122            }
1123
1124            if document.is_html_document() {
1125                self.stylist.append_stylesheet(
1126                    ua_stylesheets.html_mode_stylesheet.clone(),
1127                    guards.ua_or_user,
1128                );
1129            }
1130
1131            for user_stylesheet in self.user_stylesheets.iter() {
1132                self.stylist
1133                    .append_stylesheet(user_stylesheet.clone(), guards.ua_or_user);
1134            }
1135
1136            if self.stylist.quirks_mode() == QuirksMode::Quirks {
1137                self.stylist.append_stylesheet(
1138                    ua_stylesheets.quirks_mode_stylesheet.clone(),
1139                    guards.ua_or_user,
1140                );
1141            }
1142            self.have_added_user_agent_stylesheets = true;
1143        }
1144
1145        if reflow_request.stylesheets_changed() {
1146            self.stylist
1147                .force_stylesheet_origins_dirty(Origin::Author.into());
1148        }
1149
1150        document.flush_shadow_root_stylesheets_if_necessary(&mut self.stylist, guards.author);
1151
1152        let invalidation_set = self.stylist.flush(guards);
1153
1154        let changed_web_fonts =
1155            if need_user_agent_stylesheet_addition || reflow_request.stylesheets_changed() {
1156                self.font_context.invalidate_font_feature_values_map();
1157                // Load new @font-face rules and remove old ones if necessary.
1158                // TODO: Can we make the invalidation set tell us whether any @font-face rules changed?
1159                self.font_context.rebuild_font_face_set(
1160                    self.webview_id,
1161                    &self.stylist,
1162                    guards,
1163                    self.web_font_finished_loading_callback.clone(),
1164                    &reflow_request.document_context,
1165                )
1166            } else {
1167                WebFontSetDifference::default()
1168            };
1169
1170        StylistStylesheetUpdate {
1171            invalidation_set,
1172            changed_web_fonts,
1173        }
1174    }
1175
1176    #[servo_tracing::instrument(skip_all)]
1177    fn restyle_and_build_trees(
1178        &mut self,
1179        reflow_request: &mut ReflowRequest,
1180        document: ServoDangerousStyleDocument<'_>,
1181        root_element: ServoLayoutElement<'_>,
1182        image_resolver: &Arc<ImageResolver>,
1183    ) -> (ReflowPhasesRun, IFrameSizes, WebFontSetDifference) {
1184        let mut snapshot_map = SnapshotMap::new();
1185        let _snapshot_setter = match reflow_request.restyle.as_mut() {
1186            Some(restyle) => SnapshotSetter::new(restyle, &mut snapshot_map),
1187            None => return Default::default(),
1188        };
1189
1190        let shared_locks = document.shared_style_locks();
1191        let user_agent_stylesheets = get_ua_stylesheets(&shared_locks.ua_or_user);
1192        let guards = StylesheetGuards {
1193            author: &shared_locks.author.read(),
1194            ua_or_user: &shared_locks.ua_or_user.read(),
1195        };
1196
1197        let rayon_pool = STYLE_THREAD_POOL.lock();
1198        let rayon_pool = rayon_pool.pool();
1199        let rayon_pool = rayon_pool.as_ref();
1200
1201        let device_has_changed = std::mem::replace(&mut self.device_has_changed, false);
1202        let dangerous_root_element = unsafe { root_element.dangerous_style_element() };
1203        if device_has_changed {
1204            let sheet_origins_affected_by_device_change = self
1205                .stylist
1206                .media_features_change_changed_style(&guards, self.device());
1207            self.stylist
1208                .force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
1209
1210            if let Some(mut data) = dangerous_root_element.mutate_data() {
1211                data.hint.insert(RestyleHint::recascade_subtree());
1212            }
1213        }
1214
1215        let stylist_update = self.prepare_stylist_for_reflow(
1216            reflow_request,
1217            document,
1218            &guards,
1219            &user_agent_stylesheets,
1220        );
1221        stylist_update
1222            .invalidation_set
1223            .process_style(dangerous_root_element, Some(&snapshot_map));
1224
1225        if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
1226            // Need to manually force layout to build a new display list regardless of whether the box tree
1227            // changed or not.
1228            self.need_new_display_list.set(true);
1229        }
1230
1231        let layout_context = LayoutContext {
1232            style_context: self.build_shared_style_context(
1233                guards,
1234                &snapshot_map,
1235                reflow_request.animation_timeline_value,
1236                &reflow_request.animations,
1237                match reflow_request.stylesheets_changed() {
1238                    true => TraversalFlags::ForCSSRuleChanges,
1239                    false => TraversalFlags::empty(),
1240                },
1241            ),
1242            font_context: self.font_context.clone(),
1243            iframe_sizes: Mutex::default(),
1244            allow_parallel_layout: rayon_pool.is_some(),
1245            image_resolver: image_resolver.clone(),
1246            painter_id: self.webview_id.into(),
1247            parallelism_job_count_minimum: pref!(layout_parallelism_job_count_minimum) as usize,
1248            parallelism_job_size_minimum: pref!(layout_parallelism_job_size_minimum) as usize,
1249            device_size: reflow_request.viewport_details.device_size.cast_unit(),
1250        };
1251
1252        let restyle = reflow_request
1253            .restyle
1254            .as_ref()
1255            .expect("Should not get here if there is not restyle.");
1256
1257        let recalc_style_traversal;
1258        let dirty_root;
1259        {
1260            let _span = profile_traits::trace_span!("Styling").entered();
1261
1262            let original_dirty_root = unsafe {
1263                ServoLayoutNode::new(&restyle.dirty_root.unwrap())
1264                    .as_element()
1265                    .unwrap()
1266                    .dangerous_style_element()
1267            };
1268
1269            recalc_style_traversal = RecalcStyle::new(&layout_context);
1270            let token = {
1271                let shared = DomTraversal::<ServoDangerousStyleElement>::shared_context(
1272                    &recalc_style_traversal,
1273                );
1274                RecalcStyle::pre_traverse(original_dirty_root, shared)
1275            };
1276
1277            if !token.should_traverse() {
1278                layout_context.style_context.stylist.rule_tree().maybe_gc();
1279                return Default::default();
1280            }
1281
1282            dirty_root = driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
1283        }
1284
1285        let root_node = root_element.as_node();
1286        let damage_from_environment = if device_has_changed {
1287            LayoutDamage::Relayout
1288        } else {
1289            LayoutDamage::empty()
1290        };
1291
1292        let mut box_tree = self.box_tree.borrow_mut();
1293        let mut layout_roots = Vec::new();
1294        let damage = {
1295            let box_tree = &mut *box_tree;
1296            let mut compute_damage_and_build_box_tree = || {
1297                compute_damage_and_rebuild_box_tree(
1298                    box_tree,
1299                    &layout_context,
1300                    dirty_root.layout_node(),
1301                    root_node,
1302                    damage_from_environment,
1303                    &mut layout_roots,
1304                )
1305            };
1306
1307            if let Some(pool) = rayon_pool {
1308                pool.install(compute_damage_and_build_box_tree)
1309            } else {
1310                compute_damage_and_build_box_tree()
1311            }
1312        };
1313
1314        if damage.contains(LayoutDamage::RebuildStackingContextTree) {
1315            self.need_new_stacking_context_tree.set(true);
1316        }
1317        if damage.contains(LayoutDamage::Repaint) {
1318            self.need_new_display_list.set(true);
1319        }
1320
1321        if !damage.contains(LayoutDamage::Relayout) {
1322            if damage.contains(LayoutDamage::RecalculateOverflow) {
1323                assert!(self.need_new_display_list.get());
1324                assert!(self.need_new_stacking_context_tree.get());
1325                self.fragment_tree
1326                    .borrow()
1327                    .as_ref()
1328                    .expect("Should always have a FragmentTree when layout unnecessary")
1329                    .clear_scrollable_overflow();
1330            }
1331
1332            if !damage.contains(LayoutDamage::DescendantCollectedAsLayoutRoot) {
1333                layout_context.style_context.stylist.rule_tree().maybe_gc();
1334                return (
1335                    ReflowPhasesRun::empty(),
1336                    IFrameSizes::default(),
1337                    stylist_update.changed_web_fonts,
1338                );
1339            }
1340
1341            debug_assert!(!layout_roots.is_empty());
1342            if layout_roots
1343                .iter()
1344                .all(|layout_root| layout_root.try_layout(&layout_context))
1345            {
1346                if self.accessibility_active() {
1347                    // TODO(#47162) Compute accessibility damage rather than forcing a full update.
1348                    self.set_force_accessibility_update();
1349                }
1350
1351                return (
1352                    ReflowPhasesRun::RanLayout,
1353                    std::mem::take(&mut *layout_context.iframe_sizes.lock()),
1354                    stylist_update.changed_web_fonts,
1355                );
1356            }
1357
1358            // LayoutRoot layout has failed and now the layout root and descendants may have
1359            // been only partially laid out. As the next step is to do a full `FragmentTree`
1360            // layout, we need to ensure that none of the partial layout results corrupt
1361            // the upcoming full layout.
1362            for layout_root in layout_roots {
1363                layout_root.handle_failed_layout_root_layout();
1364            }
1365        }
1366
1367        let box_tree = &*box_tree;
1368        let viewport_size = self.stylist.device().au_viewport_size();
1369        let run_layout = || {
1370            box_tree
1371                .as_ref()
1372                .unwrap()
1373                .layout(recalc_style_traversal.context(), viewport_size)
1374        };
1375        let fragment_tree = Rc::new(if let Some(pool) = rayon_pool {
1376            pool.install(run_layout)
1377        } else {
1378            run_layout()
1379        });
1380
1381        *self.fragment_tree.borrow_mut() = Some(fragment_tree);
1382
1383        if self.debug.is_enabled(DiagnosticsLoggingOption::StyleTree) {
1384            println!(
1385                "{:?}",
1386                ShowSubtreeDataAndPrimaryValues(dangerous_root_element.as_node())
1387            );
1388        }
1389        if self.debug.is_enabled(DiagnosticsLoggingOption::RuleTree) {
1390            recalc_style_traversal
1391                .context()
1392                .style_context
1393                .stylist
1394                .rule_tree()
1395                .dump_stdout(&layout_context.style_context.guards);
1396        }
1397
1398        if self.accessibility_active() {
1399            // TODO(#47162) Compute accessibility damage rather than forcing a full upate.
1400            self.set_force_accessibility_update();
1401        }
1402
1403        // GC the rule tree if some heuristics are met.
1404        layout_context.style_context.stylist.rule_tree().maybe_gc();
1405
1406        let mut iframe_sizes = layout_context.iframe_sizes.lock();
1407        (
1408            ReflowPhasesRun::RanLayout,
1409            std::mem::take(&mut *iframe_sizes),
1410            stylist_update.changed_web_fonts,
1411        )
1412    }
1413
1414    fn build_stacking_context_tree_for_reflow(&self, reflow_request: &ReflowRequest) -> bool {
1415        if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1416            .contains(ReflowPhases::StackingContextTreeConstruction)
1417        {
1418            return false;
1419        }
1420        if !self.need_new_stacking_context_tree.get() {
1421            return false;
1422        }
1423
1424        self.build_stacking_context_tree(reflow_request.viewport_details)
1425    }
1426
1427    #[servo_tracing::instrument(name = "Stacking Context Tree Construction", skip_all)]
1428    fn build_stacking_context_tree(&self, viewport_details: ViewportDetails) -> bool {
1429        let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1430            return false;
1431        };
1432
1433        let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1434        let old_scroll_offsets = stacking_context_tree
1435            .as_ref()
1436            .map(|tree| tree.paint_info.scroll_tree.scroll_offsets());
1437
1438        // This will be done during `StackingContextTree::new` below
1439        self.need_containing_block_calculation.set(false);
1440
1441        // Build the StackingContextTree. This turns the `FragmentTree` into a
1442        // tree of fragments in CSS painting order and also creates all
1443        // applicable spatial and clip nodes.
1444        let mut new_stacking_context_tree = StackingContextTree::new(
1445            fragment_tree,
1446            viewport_details,
1447            self.id.into(),
1448            !self.have_ever_generated_display_list.get(),
1449            &self.debug,
1450        );
1451
1452        // When a new StackingContextTree is built, it contains a freshly built
1453        // ScrollTree. We want to preserve any existing scroll offsets in that tree,
1454        // adjusted by any new scroll constraints.
1455        if let Some(old_scroll_offsets) = old_scroll_offsets {
1456            new_stacking_context_tree
1457                .paint_info
1458                .scroll_tree
1459                .set_all_scroll_offsets(&old_scroll_offsets);
1460        }
1461
1462        if self.debug.is_enabled(DiagnosticsLoggingOption::ScrollTree) {
1463            new_stacking_context_tree
1464                .paint_info
1465                .scroll_tree
1466                .debug_print();
1467        }
1468
1469        *stacking_context_tree = Some(new_stacking_context_tree);
1470
1471        // The stacking context tree is up-to-date again.
1472        self.need_new_stacking_context_tree.set(false);
1473        assert!(self.need_new_display_list.get());
1474
1475        true
1476    }
1477
1478    /// Build the display list for the current layout and send it to the renderer. If no display
1479    /// list is built, returns false.
1480    #[servo_tracing::instrument(name = "Display List Construction", skip_all)]
1481    fn build_display_list(
1482        &self,
1483        reflow_request: &ReflowRequest,
1484        image_resolver: &Arc<ImageResolver>,
1485        reflow_statistics: &mut ReflowStatistics,
1486    ) -> bool {
1487        if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1488            .contains(ReflowPhases::DisplayListConstruction)
1489        {
1490            return false;
1491        }
1492        let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1493            return false;
1494        };
1495        let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1496        let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1497            return false;
1498        };
1499
1500        // If a non-display-list-generating reflow updated layout in a previous refow, we
1501        // cannot skip display list generation here the next time a display list is
1502        // requested.
1503        if !self.need_new_display_list.get() {
1504            return false;
1505        }
1506
1507        // TODO: Eventually this should be set when `paint_info` is created, but that requires
1508        // ensuring that the Epoch is passed to any method that can creates `StackingContextTree`.
1509        stacking_context_tree.paint_info.epoch = reflow_request.epoch;
1510
1511        let mut paint_timing_handler = self.paint_timing_handler.borrow_mut();
1512        // This ensures that we only create the PaintTimingHandler once per layout thread.
1513        let paint_timing_handler = match paint_timing_handler.as_mut() {
1514            Some(paint_timing_handler) => paint_timing_handler,
1515            None => {
1516                *paint_timing_handler = Some(PaintTimingHandler::new(
1517                    stacking_context_tree
1518                        .paint_info
1519                        .viewport_details
1520                        .layout_size(),
1521                ));
1522                paint_timing_handler.as_mut().unwrap()
1523            },
1524        };
1525
1526        let built_display_list = DisplayListBuilder::build(
1527            stacking_context_tree,
1528            fragment_tree,
1529            image_resolver.clone(),
1530            self.device().device_pixel_ratio(),
1531            reflow_request.highlighted_dom_node,
1532            &self.debug,
1533            paint_timing_handler,
1534            reflow_statistics,
1535        );
1536        paint_timing_handler.mark_paint_timing(reflow_request.halt_lcp);
1537        self.paint_api.send_display_list(
1538            self.webview_id,
1539            &stacking_context_tree.paint_info,
1540            built_display_list,
1541        );
1542
1543        if paint_timing_handler.did_lcp_candidate_update() &&
1544            let Some(lcp_candidate) = paint_timing_handler.largest_contentful_paint_candidate()
1545        {
1546            self.paint_api.send_lcp_candidate(
1547                lcp_candidate.id,
1548                lcp_candidate.area,
1549                self.webview_id,
1550                self.id,
1551                stacking_context_tree.paint_info.epoch,
1552            );
1553            paint_timing_handler.unset_lcp_candidate_updated();
1554        }
1555
1556        let (keys, instance_keys) = self
1557            .font_context
1558            .collect_unused_webrender_resources(false /* all */);
1559        self.paint_api
1560            .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys);
1561        self.last_display_list_was_empty.set(false);
1562        self.have_ever_generated_display_list.set(true);
1563        self.need_new_display_list.set(false);
1564        self.previously_highlighted_dom_node
1565            .set(reflow_request.highlighted_dom_node);
1566        true
1567    }
1568
1569    fn set_scroll_offset_from_script(
1570        &self,
1571        external_scroll_id: ExternalScrollId,
1572        offset: LayoutVector2D,
1573    ) -> bool {
1574        let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1575        let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1576            return false;
1577        };
1578
1579        if let Some(offset) = stacking_context_tree
1580            .paint_info
1581            .scroll_tree
1582            .set_scroll_offset_for_node_with_external_scroll_id(
1583                external_scroll_id,
1584                offset,
1585                ScrollType::Script,
1586            )
1587        {
1588            self.paint_api.scroll_node_by_delta(
1589                self.webview_id,
1590                self.id.into(),
1591                offset,
1592                external_scroll_id,
1593            );
1594
1595            if self.accessibility_active() &&
1596                let Some(accessibility_tree) = self.accessibility_tree.borrow_mut().as_mut()
1597            {
1598                accessibility_tree.add_pending_scroll_update(external_scroll_id, offset);
1599
1600                // Ensure the scroll updates are applied in the accessibility tree and sent to the
1601                // embedder, even if there are no other changes which affect the accessibility tree.
1602                self.set_force_accessibility_update();
1603            }
1604            true
1605        } else {
1606            false
1607        }
1608    }
1609
1610    /// Returns profiling information which is passed to the time profiler.
1611    fn profiler_metadata(&self) -> Option<TimerMetadata> {
1612        Some(TimerMetadata {
1613            url: self.url.to_string(),
1614            iframe: if self.is_iframe {
1615                TimerMetadataFrameType::IFrame
1616            } else {
1617                TimerMetadataFrameType::RootWindow
1618            },
1619            incremental: if self.have_ever_generated_display_list.get() {
1620                TimerMetadataReflowType::Incremental
1621            } else {
1622                TimerMetadataReflowType::FirstReflow
1623            },
1624        })
1625    }
1626
1627    /// Clear all cached layout trees and send an empty display list to paint.
1628    fn clear_layout_trees_and_send_empty_display_list(
1629        &self,
1630        reflow_request: &ReflowRequest,
1631    ) -> Option<ReflowResult> {
1632        // Clear layout trees.
1633        self.box_tree.borrow_mut().take();
1634        self.fragment_tree.borrow_mut().take();
1635        self.stacking_context_tree.borrow_mut().take();
1636
1637        // Send empty display list.
1638        let paint_info = PaintDisplayListInfo::new(
1639            reflow_request.viewport_details,
1640            Size2D::zero(),
1641            self.id.into(),
1642            reflow_request.epoch,
1643            AxesScrollSensitivity {
1644                x: ScrollType::InputEvents | ScrollType::Script,
1645                y: ScrollType::InputEvents | ScrollType::Script,
1646            },
1647            !self.have_ever_generated_display_list.get(),
1648        );
1649        let mut builder = webrender_api::DisplayListBuilder::new(paint_info.pipeline_id);
1650        builder.begin();
1651        let (_, empty_display_list) = builder.end();
1652
1653        self.paint_api
1654            .send_display_list(self.webview_id, &paint_info, empty_display_list);
1655        self.last_display_list_was_empty.set(true);
1656        self.have_ever_generated_display_list.set(true);
1657
1658        Some(ReflowResult {
1659            reflow_phases_run: ReflowPhasesRun::BuiltDisplayList,
1660            ..Default::default()
1661        })
1662    }
1663
1664    pub(crate) fn ensure_containing_block_calculation(&self) {
1665        if !self.need_containing_block_calculation.get() {
1666            return;
1667        }
1668        let fragment_tree = self.fragment_tree.borrow();
1669        fragment_tree.as_ref().expect("missing fragment tree").find(
1670            |fragment, _level, containing_block| {
1671                fragment.set_containing_block(containing_block);
1672                None::<()>
1673            },
1674        );
1675        self.need_containing_block_calculation.set(false)
1676    }
1677}
1678
1679fn get_ua_stylesheets(shared_lock: &SharedRwLock) -> Rc<UserAgentStylesheets> {
1680    // There is an assumption here that there is only a single ScriptThread per thread, which
1681    // is currently the case in Servo. If this were to change, these user agent stylesheets
1682    // would need to be managed by the ScriptThread instance.
1683    thread_local! {
1684        static USER_AGENT_STYLESHEETS: OnceCell<Rc<UserAgentStylesheets>> = const { OnceCell::new() };
1685    }
1686
1687    fn parse_ua_stylesheet(
1688        shared_lock: &SharedRwLock,
1689        filename: &str,
1690        content: &[u8],
1691    ) -> DocumentStyleSheet {
1692        let url = Url::parse(&format!("chrome://resources/{filename}")).unwrap_or_else(|_| {
1693            panic!("Could not parse user stylesheet URL: {filename}");
1694        });
1695        DocumentStyleSheet(ServoArc::new(Stylesheet::from_bytes(
1696            content,
1697            url.into(),
1698            None,
1699            None,
1700            Origin::UserAgent,
1701            ServoArc::new(shared_lock.wrap(MediaList::empty())),
1702            shared_lock.clone(),
1703            None,
1704            None,
1705            QuirksMode::NoQuirks,
1706        )))
1707    }
1708
1709    USER_AGENT_STYLESHEETS.with(|user_stylesheets| {
1710        user_stylesheets
1711            .get_or_init(|| {
1712                // FIXME: presentational-hints.css should be at author origin with zero specificity.
1713                //        (Does it make a difference?)
1714                let user_agent_stylesheets = vec![
1715                    parse_ua_stylesheet(shared_lock, "user-agent.css", USER_AGENT_CSS),
1716                    parse_ua_stylesheet(shared_lock, "servo.css", SERVO_CSS),
1717                    parse_ua_stylesheet(
1718                        shared_lock,
1719                        "presentational-hints.css",
1720                        PRESENTATIONAL_HINTS_CSS,
1721                    ),
1722                ];
1723
1724                let html_mode_stylesheet =
1725                    parse_ua_stylesheet(shared_lock, "html-mode.css", HTML_MODE_CSS);
1726
1727                let quirks_mode_stylesheet =
1728                    parse_ua_stylesheet(shared_lock, "quirks-mode.css", QUIRKS_MODE_CSS);
1729
1730                Rc::new(UserAgentStylesheets {
1731                    user_agent_stylesheets,
1732                    html_mode_stylesheet,
1733                    quirks_mode_stylesheet,
1734                })
1735            })
1736            .clone()
1737    })
1738}
1739
1740/// This structure holds the user-agent stylesheets.
1741pub struct UserAgentStylesheets {
1742    /// The user agent stylesheets.
1743    pub user_agent_stylesheets: Vec<DocumentStyleSheet>,
1744    /// The user agent stylesheet for HTML documents.
1745    pub html_mode_stylesheet: DocumentStyleSheet,
1746    /// The quirks mode stylesheet.
1747    pub quirks_mode_stylesheet: DocumentStyleSheet,
1748}
1749
1750struct RegisteredPainterImpl {
1751    painter: Box<dyn Painter>,
1752    name: Atom,
1753    // FIXME: Should be a PrecomputedHashMap.
1754    properties: FxHashMap<Atom, PropertyId>,
1755}
1756
1757impl SpeculativePainter for RegisteredPainterImpl {
1758    fn speculatively_draw_a_paint_image(
1759        &self,
1760        properties: Vec<(Atom, String)>,
1761        arguments: Vec<String>,
1762    ) {
1763        self.painter
1764            .speculatively_draw_a_paint_image(properties, arguments);
1765    }
1766}
1767
1768impl RegisteredSpeculativePainter for RegisteredPainterImpl {
1769    fn properties(&self) -> &FxHashMap<Atom, PropertyId> {
1770        &self.properties
1771    }
1772    fn name(&self) -> Atom {
1773        self.name.clone()
1774    }
1775}
1776
1777impl Painter for RegisteredPainterImpl {
1778    fn draw_a_paint_image(
1779        &self,
1780        size: Size2D<f32, CSSPixel>,
1781        device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
1782        properties: Vec<(Atom, String)>,
1783        arguments: Vec<String>,
1784    ) -> Result<DrawAPaintImageResult, PaintWorkletError> {
1785        self.painter
1786            .draw_a_paint_image(size, device_pixel_ratio, properties, arguments)
1787    }
1788}
1789
1790struct RegisteredPaintersImpl(HashMap<Atom, RegisteredPainterImpl>);
1791
1792impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1793    fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1794        self.0
1795            .get(name)
1796            .map(|painter| painter as &dyn RegisteredSpeculativePainter)
1797    }
1798}
1799
1800struct LayoutFontMetricsProvider(Arc<FontContext>);
1801
1802impl FontMetricsProvider for LayoutFontMetricsProvider {
1803    fn query_font_metrics(
1804        &self,
1805        _vertical: bool,
1806        font: &Font,
1807        base_size: CSSPixelLength,
1808        _flags: QueryFontMetricsFlags,
1809    ) -> FontMetrics {
1810        let font_context = &self.0;
1811        let font_group = self
1812            .0
1813            .font_group_with_size(ServoArc::new(font.clone()), base_size.into());
1814
1815        let Some(first_font_metrics) = font_group
1816            .first(font_context)
1817            .map(|font| font.metrics.clone())
1818        else {
1819            return Default::default();
1820        };
1821
1822        // Only use the x-height of this font if it is non-zero. Some fonts return
1823        // inaccurate metrics, which shouldn't be used.
1824        let x_height = Some(first_font_metrics.x_height)
1825            .filter(|x_height| !x_height.is_zero())
1826            .map(CSSPixelLength::from);
1827
1828        let zero_advance_measure = first_font_metrics
1829            .zero_horizontal_advance
1830            .or_else(|| {
1831                font_group
1832                    .find_by_codepoint(font_context, '0', None, Language::UNKNOWN)?
1833                    .metrics
1834                    .zero_horizontal_advance
1835            })
1836            .map(CSSPixelLength::from);
1837
1838        let ic_width = first_font_metrics
1839            .ic_horizontal_advance
1840            .or_else(|| {
1841                font_group
1842                    .find_by_codepoint(font_context, '\u{6C34}', None, Language::UNKNOWN)?
1843                    .metrics
1844                    .ic_horizontal_advance
1845            })
1846            .map(CSSPixelLength::from);
1847
1848        FontMetrics {
1849            x_height,
1850            zero_advance_measure,
1851            cap_height: None,
1852            ic_width,
1853            ascent: first_font_metrics.ascent.into(),
1854            script_percent_scale_down: None,
1855            script_script_percent_scale_down: None,
1856        }
1857    }
1858
1859    fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
1860        Length::new(match generic {
1861            GenericFontFamily::Monospace => pref!(fonts_default_monospace_size),
1862            _ => pref!(fonts_default_size),
1863        } as f32)
1864        .max(Length::new(0.0))
1865    }
1866}
1867
1868impl Debug for LayoutFontMetricsProvider {
1869    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1870        f.debug_tuple("LayoutFontMetricsProvider").finish()
1871    }
1872}
1873
1874struct SnapshotSetter<'dom> {
1875    elements_with_snapshot: Vec<ServoLayoutElement<'dom>>,
1876}
1877
1878impl SnapshotSetter<'_> {
1879    fn new(restyle: &mut ReflowRequestRestyle, snapshot_map: &mut SnapshotMap) -> Self {
1880        debug!("Draining restyles: {}", restyle.pending_restyles.len());
1881        let restyles = std::mem::take(&mut restyle.pending_restyles);
1882
1883        let elements_with_snapshot: Vec<_> = restyles
1884            .iter()
1885            .filter(|r| r.1.snapshot.is_some())
1886            .map(|r| unsafe { ServoLayoutNode::new(&r.0).as_element().unwrap() })
1887            .collect();
1888
1889        for (element, restyle) in restyles {
1890            let element = unsafe { ServoLayoutNode::new(&element).as_element().unwrap() };
1891
1892            // If we haven't styled this node yet, we don't need to track a
1893            // restyle.
1894            let Some(mut style_data) = element
1895                .style_data()
1896                .map(|data| data.element_data.borrow_mut())
1897            else {
1898                element.unset_snapshot_flags();
1899                continue;
1900            };
1901
1902            debug!("Noting restyle for {:?}: {:?}", element, style_data);
1903            if let Some(s) = restyle.snapshot {
1904                element.set_has_snapshot();
1905                snapshot_map.insert(element.as_node().opaque(), s);
1906            }
1907
1908            // Stash the data on the element for processing by the style system.
1909            style_data.hint.insert(restyle.hint);
1910            style_data.damage = restyle.damage;
1911        }
1912        Self {
1913            elements_with_snapshot,
1914        }
1915    }
1916}
1917
1918impl Drop for SnapshotSetter<'_> {
1919    fn drop(&mut self) {
1920        for element in &self.elements_with_snapshot {
1921            element.unset_snapshot_flags();
1922        }
1923    }
1924}
1925
1926bitflags! {
1927    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1928    pub struct ReflowPhases: u8 {
1929        const StackingContextTreeConstruction = 1 << 0;
1930        const DisplayListConstruction = 1 << 1;
1931    }
1932}
1933
1934impl ReflowPhases {
1935    /// Return the necessary phases of layout for the given [`ReflowGoal`]. Note that all
1936    /// [`ReflowGoals`] need the basic restyle + box tree layout + fragment tree layout,
1937    /// so [`ReflowPhases::empty()`] implies that.
1938    fn necessary(reflow_goal: &ReflowGoal) -> Self {
1939        let is_inset_longhand = |longhand: LonghandId| {
1940            matches!(
1941                longhand,
1942                LonghandId::Top |
1943                    LonghandId::Right |
1944                    LonghandId::Bottom |
1945                    LonghandId::Left |
1946                    LonghandId::InsetInlineStart |
1947                    LonghandId::InsetInlineEnd |
1948                    LonghandId::InsetBlockStart |
1949                    LonghandId::InsetBlockEnd
1950            )
1951        };
1952
1953        let is_inset_property =
1954            |property: NonCustomPropertyId| match property.longhand_or_shorthand() {
1955                Ok(longhand) => is_inset_longhand(longhand),
1956                // Special case for the `All` shorthand as it has many longhands.
1957                Err(ShorthandId::All) => true,
1958                Err(shorthand) => shorthand.longhands().any(is_inset_longhand),
1959            };
1960
1961        match reflow_goal {
1962            ReflowGoal::LayoutQuery(query) => match query {
1963                // Resolving insets requires the creation of the stacking context, but other style properties
1964                // do not. This should be kept in sync with `LayoutThread::query_resolved_style()`.
1965                QueryMsg::ResolvedStyleQuery(PropertyId::NonCustom(non_custom_property_id))
1966                    if is_inset_property(*non_custom_property_id) =>
1967                {
1968                    Self::StackingContextTreeConstruction
1969                },
1970                QueryMsg::ResolvedStyleQuery(_) => Self::empty(),
1971                QueryMsg::NodesFromPointQuery => {
1972                    Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1973                },
1974                QueryMsg::BoxArea |
1975                QueryMsg::BoxAreas |
1976                QueryMsg::ElementsFromPoint |
1977                QueryMsg::FlushForUpdateTheRenderingQuery |
1978                QueryMsg::OffsetParentQuery |
1979                QueryMsg::ScrollingAreaOrOffsetQuery |
1980                QueryMsg::TextIndexQuery => Self::StackingContextTreeConstruction,
1981                QueryMsg::ClientRectQuery |
1982                QueryMsg::CurrentCSSZoomQuery |
1983                QueryMsg::EffectiveOverflow |
1984                QueryMsg::ElementInnerOuterTextQuery |
1985                QueryMsg::InnerWindowDimensionsQuery |
1986                QueryMsg::PaddingQuery |
1987                QueryMsg::ResolvedFontStyleQuery |
1988                QueryMsg::ScrollParentQuery |
1989                QueryMsg::StyleQuery => Self::empty(),
1990            },
1991            ReflowGoal::UpdateScrollNode(..) | ReflowGoal::UpdateTheRendering => {
1992                Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1993            },
1994        }
1995    }
1996}
1997
1998/// Summarizes changes after flushing stylesheets on the `Stylist`.
1999struct StylistStylesheetUpdate {
2000    /// Information about what kind of selectors changed.
2001    invalidation_set: StylesheetInvalidationSet,
2002    /// A list of changes to the set of web fonts.
2003    changed_web_fonts: WebFontSetDifference,
2004}