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