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