Skip to main content

layout_api/
lib.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//! This module contains traits in script used generically in the rest of Servo.
6//! The traits are here instead of in script so that these modules won't have
7//! to depend on script.
8
9#![deny(unsafe_code)]
10
11mod container_timing_record;
12mod largest_contentful_paint_candidate;
13mod layout_damage;
14mod layout_dom;
15mod layout_element;
16mod layout_node;
17mod pseudo_element_chain;
18
19use std::any::Any;
20use std::rc::Rc;
21use std::sync::Arc;
22use std::sync::atomic::AtomicIsize;
23use std::thread::JoinHandle;
24use std::time::Duration;
25
26use accesskit::{Action, ActionData, ActionRequest};
27use app_units::Au;
28use background_hang_monitor_api::BackgroundHangMonitorRegister;
29use bitflags::bitflags;
30pub use container_timing_record::ContainerTimingRecord;
31use embedder_traits::{Cursor, ScriptToEmbedderChan, Theme, UntrustedNodeAddress, ViewportDetails};
32use euclid::{Point2D, Rect};
33use fonts::{FontContext, WebFontDocumentContext, WebFontSetDifference};
34pub use largest_contentful_paint_candidate::LCPCandidate;
35pub use layout_damage::{AccessibilityDamage, LayoutDamage};
36pub use layout_dom::{
37    DangerousStyleElementOf, DangerousStyleNodeOf, LayoutDomTypeBundle, LayoutElementOf,
38    LayoutNodeOf,
39};
40pub use layout_element::{DangerousStyleElement, LayoutElement};
41pub use layout_node::{DangerousStyleNode, LayoutNode};
42use libc::c_void;
43use malloc_size_of::{MallocSizeOf as MallocSizeOfTrait, MallocSizeOfOps, malloc_size_of_is_0};
44use malloc_size_of_derive::MallocSizeOf;
45use net_traits::image_cache::{ImageCache, ImageCacheFactory, PendingImageId};
46use net_traits::request::InternalRequest;
47use paint_api::CrossProcessPaintApi;
48use paint_api::display_list::PaintTimingInfo;
49use parking_lot::RwLock;
50use pixels::{RasterImage, Repeat};
51use profile_traits::mem::Report;
52use profile_traits::time;
53pub use pseudo_element_chain::PseudoElementChain;
54use rustc_hash::{FxHashMap, FxHashSet};
55use script_traits::{InitialScriptState, Painter, ScriptThreadMessage};
56use serde::{Deserialize, Serialize};
57use servo_arc::Arc as ServoArc;
58use servo_base::Epoch;
59use servo_base::generic_channel::GenericSender;
60use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
61use servo_base::text::{RangeAny, Utf32CodeUnits, Utf32CodeUnitsOrNodeOffset};
62use servo_url::{ImmutableOrigin, ServoUrl};
63use style::Atom;
64use style::animation::DocumentAnimationSet;
65use style::attr::{AttrValue, parse_double};
66use style::context::QuirksMode;
67use style::data::ElementDataWrapper;
68use style::device::Device;
69use style::dom::OpaqueNode;
70use style::invalidation::element::restyle_hints::RestyleHint;
71use style::properties::style_structs::Font;
72use style::properties::{ComputedValues, PropertyId};
73use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot};
74use style::str::char_is_whitespace;
75use style::stylesheets::{DocumentStyleSheet, Stylesheet};
76use style::stylist::Stylist;
77#[cfg(debug_assertions)]
78use style::thread_state::{self, ThreadState};
79use style::values::computed::Overflow;
80use style_traits::CSSPixel;
81use uuid::Uuid;
82use webrender_api::units::{DeviceIntSize, LayoutPoint, LayoutVector2D};
83use webrender_api::{ExternalScrollId, ImageKey};
84
85pub trait GenericLayoutDataTrait: Any + MallocSizeOfTrait + Send + Sync + 'static {
86    fn as_any(&self) -> &dyn Any;
87
88    /// Returns whether `new_range` was successfully set on an existing text run
89    fn set_text_run_selection(&self, new_range: Option<RangeAny<Utf32CodeUnits>>) -> bool;
90
91    /// Set whether or not this node is selected when it is an element. Returns `true`
92    /// if anything changed that requires a new display list.
93    fn set_element_selection(&self, selected: bool) -> bool;
94
95    /// Get the text rendered by this node, if it is a `CharacterData` node and produces
96    /// a `TextRun` in the box tree.
97    fn rendered_text(&self, range: RangeAny<Utf32CodeUnits>) -> Option<String>;
98}
99
100pub trait LayoutDataTrait: GenericLayoutDataTrait + Default {}
101pub type GenericLayoutData = dyn GenericLayoutDataTrait;
102
103#[derive(Default, MallocSizeOf)]
104pub struct StyleData {
105    /// Data that the style system associates with a node. When the
106    /// style system is being used standalone, this is all that hangs
107    /// off the node. This must be first to permit the various
108    /// transmutations between ElementData and PersistentLayoutData.
109    pub element_data: ElementDataWrapper,
110
111    /// Information needed during parallel traversals.
112    pub parallel: DomParallelInfo,
113}
114
115/// Information that we need stored in each DOM node.
116#[derive(Default, MallocSizeOf)]
117pub struct DomParallelInfo {
118    /// The number of children remaining to process during bottom-up traversal.
119    pub children_to_process: AtomicIsize,
120}
121
122#[derive(Clone, Copy, Debug, Eq, PartialEq)]
123pub enum LayoutNodeType {
124    Element(LayoutElementType),
125    Text,
126}
127
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
129pub enum LayoutElementType {
130    Element,
131    HTMLBodyElement,
132    HTMLButtonElement,
133    HTMLBRElement,
134    HTMLCanvasElement,
135    HTMLHtmlElement,
136    HTMLIFrameElement,
137    HTMLImageElement,
138    HTMLInputElement,
139    HTMLMediaElement,
140    HTMLObjectElement,
141    HTMLOptGroupElement,
142    HTMLOptionElement,
143    HTMLParagraphElement,
144    HTMLPreElement,
145    HTMLSelectElement,
146    HTMLTableCellElement,
147    HTMLTableColElement,
148    HTMLTableElement,
149    HTMLTableRowElement,
150    HTMLTableSectionElement,
151    HTMLTextAreaElement,
152    SVGImageElement,
153    SVGSVGElement,
154}
155
156pub struct HTMLCanvasData {
157    pub image_key: Option<ImageKey>,
158    pub width: u32,
159    pub height: u32,
160}
161
162pub struct SVGElementData<'dom> {
163    /// The SVG's XML source represented as a base64 encoded `data:` url.
164    pub source: Option<Result<ServoUrl, ()>>,
165    pub width: Option<&'dom AttrValue>,
166    pub height: Option<&'dom AttrValue>,
167    pub svg_id: Uuid,
168    /// <https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute>
169    pub view_box: Option<&'dom AttrValue>,
170}
171
172impl SVGElementData<'_> {
173    /// <https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute>
174    pub fn ratio_from_view_box(&self) -> Option<f32> {
175        let mut iter = self
176            .view_box?
177            .split(|c| char_is_whitespace(c) || c == ',')
178            .filter(|part| !part.is_empty());
179        let _min_x = parse_double(iter.next()?).ok()?;
180        let _min_y = parse_double(iter.next()?).ok()?;
181
182        let width = parse_double(iter.next()?).ok()?;
183        if width == 0f64 {
184            return None;
185        }
186
187        let height = parse_double(iter.next()?).ok()?;
188        if height == 0f64 {
189            return None;
190        }
191
192        iter.next().is_none().then(|| width as f32 / height as f32)
193    }
194}
195
196/// The address of a node known to be valid. These are sent from script to layout.
197#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
198pub struct TrustedNodeAddress(pub *const c_void);
199
200#[expect(unsafe_code)]
201unsafe impl Send for TrustedNodeAddress {}
202
203/// Whether the pending image needs to be fetched or is waiting on an existing fetch.
204#[derive(Debug)]
205pub enum PendingImageState {
206    Unrequested(ServoUrl),
207    PendingResponse,
208}
209
210/// The destination in layout where an image is needed.
211#[derive(Debug, MallocSizeOf)]
212pub enum LayoutImageDestination {
213    BoxTreeConstruction,
214    DisplayListBuilding,
215}
216
217/// The data associated with an image that is not yet present in the image cache.
218/// Used by the script thread to hold on to DOM elements that need to be repainted
219/// when an image fetch is complete.
220#[derive(Debug)]
221pub struct PendingImage {
222    pub state: PendingImageState,
223    pub node: UntrustedNodeAddress,
224    pub id: PendingImageId,
225    pub origin: ImmutableOrigin,
226    pub destination: LayoutImageDestination,
227    pub is_internal_request: InternalRequest,
228}
229
230/// A data structure to track vector image that are fully loaded (i.e has a parsed SVG
231/// tree) but not yet rasterized to the size needed by layout. The rasterization is
232/// happening in the image cache.
233#[derive(Debug)]
234pub struct PendingRasterizationImage {
235    pub node: UntrustedNodeAddress,
236    pub id: PendingImageId,
237    pub size: DeviceIntSize,
238}
239
240#[derive(Clone, Copy, Debug, MallocSizeOf)]
241pub struct MediaFrame {
242    pub image_key: webrender_api::ImageKey,
243    pub width: i32,
244    pub height: i32,
245}
246
247pub struct MediaMetadata {
248    pub width: u32,
249    pub height: u32,
250}
251
252pub struct HTMLMediaData {
253    pub current_frame: Option<MediaFrame>,
254    pub metadata: Option<MediaMetadata>,
255    pub poster_url: Option<ServoUrl>,
256}
257
258#[derive(Debug)]
259pub struct AccessibilityActionRequest {
260    pub action: Action,
261    pub target: OpaqueNode,
262    pub data: Option<ActionData>,
263}
264
265pub struct LayoutConfig {
266    pub id: PipelineId,
267    pub webview_id: WebViewId,
268    pub url: ServoUrl,
269    pub is_iframe: bool,
270    pub script_chan: GenericSender<ScriptThreadMessage>,
271    pub image_cache: Arc<dyn ImageCache>,
272    pub font_context: Arc<FontContext>,
273    pub time_profiler_chan: time::ProfilerChan,
274    pub paint_api: CrossProcessPaintApi,
275    pub viewport_details: ViewportDetails,
276    pub user_stylesheets: Rc<Vec<DocumentStyleSheet>>,
277    pub theme: Theme,
278    pub embedder_chan: ScriptToEmbedderChan,
279}
280
281bitflags! {
282    #[derive(Copy, Clone)]
283    pub struct HitTestFlags: u8 {
284        /// Whether to populate [`HitTestResult::dom_position_for_selection`]
285        const IncludeDomPosition = 0b0000_0001;
286    }
287}
288
289pub trait LayoutFactory: Send + Sync {
290    fn create(&self, config: LayoutConfig) -> Box<dyn Layout>;
291}
292
293pub trait Layout {
294    /// Get a reference to this Layout's Stylo `Device` used to handle media queries and
295    /// resolve font metrics.
296    fn device(&self) -> &Device;
297
298    /// Set the theme on this [`Layout`]'s [`Device`]. The caller should also trigger a
299    /// new layout when this happens, though it can happen later. Returns `true` if the
300    /// [`Theme`] actually changed or `false` otherwise.
301    fn set_theme(&mut self, theme: Theme) -> bool;
302
303    /// Set the [`ViewportDetails`] on this [`Layout`]'s [`Device`]. The caller should also
304    /// trigger a new layout when this happens, though it can happen later. Returns `true`
305    /// if the [`ViewportDetails`] actually changed or `false` otherwise.
306    fn set_viewport_details(&mut self, viewport_details: ViewportDetails) -> bool;
307
308    /// Add a stylesheet to this Layout's `Stylist`.
309    ///
310    /// The second stylesheet is the insertion point (if it exists, the sheet needs to be
311    /// inserted before it).
312    fn add_stylesheet(
313        &mut self,
314        stylesheet: ServoArc<Stylesheet>,
315        before_stylesheet: Option<ServoArc<Stylesheet>>,
316    );
317
318    /// Inform the layout that its ScriptThread is about to exit.
319    fn exit_now(&mut self);
320
321    /// Requests that layout measure its memory usage. The resulting reports are sent back
322    /// via the supplied channel.
323    fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps);
324
325    /// Sets quirks mode for the document, causing the quirks mode stylesheet to be used.
326    fn set_quirks_mode(&mut self, quirks_mode: QuirksMode);
327
328    /// Removes a stylesheet from the Layout.
329    fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>);
330
331    /// Removes an image from the Layout image resolver cache.
332    fn remove_cached_image(&mut self, image_url: &ServoUrl);
333
334    /// Requests a reflow.
335    fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult>;
336
337    /// Do not request a reflow, but ensure that any previous reflow completes building a stacking
338    /// context tree so that it is ready to query the final size of any elements in script.
339    fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails);
340
341    /// Tells layout that script has added some paint worklet modules.
342    fn register_paint_worklet_modules(
343        &mut self,
344        name: Atom,
345        properties: Vec<Atom>,
346        painter: Box<dyn Painter>,
347    );
348
349    /// Set the scroll states of this layout after a `Paint` scroll.
350    fn set_scroll_offsets_from_renderer(
351        &mut self,
352        scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
353    );
354
355    /// Get the scroll offset of the given scroll node with id of [`ExternalScrollId`] or `None` if it does
356    /// not exist in the tree.
357    fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D>;
358
359    /// Returns true if this layout needs to produce a new display list for rendering updates.
360    fn needs_new_display_list(&self) -> bool;
361
362    /// Marks that this layout needs to produce a new display list for rendering updates.
363    fn set_needs_new_display_list(&self);
364
365    /// Returns the [`NodeRenderingType`] for this node and pseudo. This is used to determine
366    /// if a node is being rendered, delegating its rendering, or not being rendered at all.
367    fn node_rendering_type(
368        &self,
369        node: TrustedNodeAddress,
370        pseudo: Option<PseudoElement>,
371    ) -> NodeRenderingType;
372
373    fn query_containing_block(&self, node: TrustedNodeAddress) -> Option<UntrustedNodeAddress>;
374    fn query_containing_block_is_descendant(
375        &self,
376        root: TrustedNodeAddress,
377        possible_descendant: TrustedNodeAddress,
378    ) -> bool;
379    fn query_padding(&self, node: TrustedNodeAddress) -> Option<PhysicalSides>;
380    fn query_box_area(
381        &self,
382        node: TrustedNodeAddress,
383        area: BoxAreaType,
384        exclude_transform_and_inline: bool,
385    ) -> Option<Rect<Au, CSSPixel>>;
386    fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> CSSPixelRectVec;
387    fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32, CSSPixel>;
388    fn query_current_css_zoom(&self, node: TrustedNodeAddress) -> f32;
389    fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String;
390    fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse;
391    /// Query the scroll container for the given node. If node is `None`, the scroll container for
392    /// the viewport is returned.
393    fn query_scroll_container(
394        &self,
395        node: Option<TrustedNodeAddress>,
396        flags: ScrollContainerQueryFlags,
397    ) -> Option<ScrollContainerResponse>;
398    fn query_resolved_style(
399        &self,
400        node: TrustedNodeAddress,
401        pseudo: Option<PseudoElement>,
402        property_id: PropertyId,
403        animations: DocumentAnimationSet,
404        animation_timeline_value: f64,
405    ) -> String;
406
407    fn query_accesskit_node(&self, node: TrustedNodeAddress) -> Option<accesskit::Node>;
408
409    fn query_resolved_font_style(
410        &self,
411        node: TrustedNodeAddress,
412        value: &str,
413        animations: DocumentAnimationSet,
414        animation_timeline_value: f64,
415    ) -> Option<ServoArc<Font>>;
416    fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32, CSSPixel>;
417    /// Find the closest character offset of the point within descendants of the given
418    /// node, if it has text content. This works even if the point is outside of all of
419    /// the layout boxes of the node.
420    fn query_text_index(
421        &self,
422        node: TrustedNodeAddress,
423        point_in_viewport: Point2D<Au, CSSPixel>,
424    ) -> Option<(OpaqueNode, Utf32CodeUnits)>;
425    fn hit_test(&self, flags: HitTestFlags, point: LayoutPoint) -> HitTestResult;
426    fn query_effective_overflow(&self, node: TrustedNodeAddress) -> Option<AxesOverflow>;
427    fn stylist_mut(&mut self) -> &mut Stylist;
428
429    /// Set whether the accessibility tree should be constructed for this Layout.
430    /// This should be called by the embedder when accessibility is requested by the user.
431    fn set_accessibility_active(&self, enabled: bool, epoch: Epoch);
432
433    /// Returns whether accessibility is active for this Layout.
434    fn accessibility_active(&self) -> bool;
435
436    /// Whether the accessibility tree must be updated. This is true when accessibility is
437    /// activated, and at least one of the following conditions is met:
438    /// - the accessibility tree has not yet been built after activating accessibility;
439    /// - set_force_accessibility_update_if_active() has been called since the accessibility tree
440    ///   was last updated (for example, if a scroll occurred, or the focused element changed);
441    /// - there are pending actions from assistive technology.
442    ///
443    /// Checked in can_skip_reflow_request_entirely(), as a pending accessibility tree update
444    /// should force a reflow, and during reflow to determine whether to update the
445    /// accessibility tree.
446    fn needs_accessibility_update(&self) -> bool;
447
448    /// If accessibility is active, force an accessibility update on the next reflow.
449    /// See [Self::needs_accessibility_update()].
450    fn set_force_accessibility_update_if_active(&self);
451
452    /// Handle an accessibility action.
453    fn handle_accessibility_action(&self, action_request: ActionRequest);
454
455    fn font_context(&self) -> &Arc<FontContext>;
456}
457
458/// This trait is part of `layout_api` because it depends on both `script_traits`
459/// and also `LayoutFactory` from this crate. If it was in `script_traits` there would be a
460/// circular dependency.
461pub trait ScriptThreadFactory {
462    /// Create a `ScriptThread`.
463    fn create(
464        state: InitialScriptState,
465        layout_factory: Arc<dyn LayoutFactory>,
466        image_cache_factory: Arc<dyn ImageCacheFactory>,
467        background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
468    ) -> JoinHandle<()>;
469}
470
471/// Type of the area of CSS box for query.
472/// See <https://www.w3.org/TR/css-box-3/#box-model>.
473#[derive(Copy, Clone)]
474pub enum BoxAreaType {
475    Content,
476    Padding,
477    Border,
478}
479
480pub type CSSPixelRectVec = Vec<Rect<Au, CSSPixel>>;
481
482/// Whether or not this node is being rendered or delegates rendering according
483/// to the HTML standard.
484#[derive(Copy, Clone)]
485pub enum NodeRenderingType {
486    /// <https://html.spec.whatwg.org/multipage/#being-rendered>
487    Rendered,
488    /// <https://html.spec.whatwg.org/multipage/#delegating-its-rendering-to-its-children>
489    DelegatesRendering,
490    /// If neither of the other two cases are true, this is. The node is effectively not
491    /// taking part in the final layout of the page.
492    NotRendered,
493}
494
495#[derive(Default)]
496pub struct PhysicalSides {
497    pub left: Au,
498    pub top: Au,
499    pub right: Au,
500    pub bottom: Au,
501}
502
503#[derive(Clone, Default)]
504pub struct OffsetParentResponse {
505    pub node_address: Option<UntrustedNodeAddress>,
506    pub rect: Rect<Au, CSSPixel>,
507}
508
509bitflags! {
510    #[derive(PartialEq)]
511    pub struct ScrollContainerQueryFlags: u8 {
512        /// Whether or not this query is for the purposes of a `scrollParent` layout query.
513        const ForScrollParent = 1 << 0;
514        /// Whether or not to consider the original element's scroll box for the return value.
515        const Inclusive = 1 << 1;
516    }
517}
518
519#[derive(Clone, Copy, Debug, MallocSizeOf)]
520pub struct AxesOverflow {
521    pub x: Overflow,
522    pub y: Overflow,
523}
524
525impl Default for AxesOverflow {
526    fn default() -> Self {
527        Self {
528            x: Overflow::Visible,
529            y: Overflow::Visible,
530        }
531    }
532}
533
534impl From<&ComputedValues> for AxesOverflow {
535    fn from(style: &ComputedValues) -> Self {
536        Self {
537            x: style.slow_clone_overflow_x(),
538            y: style.slow_clone_overflow_y(),
539        }
540    }
541}
542
543impl AxesOverflow {
544    pub fn to_scrollable(&self) -> Self {
545        Self {
546            x: self.x.to_scrollable(),
547            y: self.y.to_scrollable(),
548        }
549    }
550
551    /// Whether or not the `overflow` value establishes a scroll container.
552    pub fn establishes_scroll_container(&self) -> bool {
553        // Checking one axis suffices, because the computed value ensures that
554        // either both axes are scrollable, or none is scrollable.
555        self.x.is_scrollable()
556    }
557}
558
559#[derive(Clone)]
560pub enum ScrollContainerResponse {
561    Viewport(AxesOverflow),
562    Element(UntrustedNodeAddress, AxesOverflow),
563}
564
565#[derive(Debug, PartialEq)]
566pub enum QueryMsg {
567    BoxArea,
568    BoxAreas,
569    ClientRectQuery,
570    CurrentCSSZoomQuery,
571    EffectiveOverflow,
572    ElementInnerOuterTextQuery,
573    ElementsFromPoint,
574    InnerWindowDimensionsQuery,
575    NodesFromPointQuery,
576    OffsetParentQuery,
577    ScrollParentQuery,
578    ResolvedFontStyleQuery,
579    /// A style query, with an optional [`PropertyId`], used to limit the phases
580    /// of layout run before the query.
581    ResolvedStyleQuery(PropertyId),
582    ScrollingAreaOrOffsetQuery,
583    StyleQuery,
584    TextIndexQuery,
585    PaddingQuery,
586    FlushForUpdateTheRenderingQuery,
587    AccessKitNodeQuery,
588}
589
590/// The goal of a reflow request.
591///
592/// Please do not add any other types of reflows. In general, all reflow should
593/// go through the *update the rendering* step of the HTML specification. Exceptions
594/// should have careful review.
595#[derive(Debug, PartialEq)]
596pub enum ReflowGoal {
597    /// A reflow has been requesting by the *update the rendering* step of the HTML
598    /// event loop. This nominally driven by the display's VSync.
599    UpdateTheRendering,
600
601    /// Script has done a layout query and this reflow ensurs that layout is up-to-date
602    /// with the latest changes to the DOM.
603    LayoutQuery(QueryMsg),
604
605    /// Tells layout about a single new scrolling offset from the script. The rest will
606    /// remain untouched. Layout will forward whether the element is scrolled through
607    /// [ReflowResult].
608    UpdateScrollNode(ExternalScrollId, LayoutVector2D),
609}
610
611#[derive(Clone, Debug, MallocSizeOf)]
612pub struct IFrameSize {
613    pub browsing_context_id: BrowsingContextId,
614    pub pipeline_id: PipelineId,
615    pub viewport_details: ViewportDetails,
616}
617
618pub type IFrameSizes = FxHashMap<BrowsingContextId, IFrameSize>;
619
620bitflags! {
621    /// Conditions which cause a [`Document`] to need to be restyled during reflow, which
622    /// might cause the rest of layout to happen as well.
623    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
624    pub struct RestyleReason: u16 {
625        const StylesheetsChanged = 1 << 0;
626        const DOMChanged = 1 << 1;
627        const PendingRestyles = 1 << 2;
628        const HighlightedDOMNodeChanged = 1 << 3;
629        const ThemeChanged = 1 << 4;
630        const ViewportChanged = 1 << 5;
631        const PaintWorkletLoaded = 1 << 6;
632    }
633}
634
635malloc_size_of_is_0!(RestyleReason);
636
637impl RestyleReason {
638    pub fn needs_restyle(&self) -> bool {
639        !self.is_empty()
640    }
641}
642
643/// Information derived from a layout pass that needs to be returned to the script thread.
644#[derive(Default)]
645pub struct ReflowResult {
646    /// The phases that were run during this reflow.
647    pub reflow_phases_run: ReflowPhasesRun,
648    pub reflow_statistics: ReflowStatistics,
649    /// The list of images that were encountered that are in progress.
650    pub pending_images: Vec<PendingImage>,
651    /// The list of vector images that were encountered that still need to be rasterized.
652    pub pending_rasterization_images: Vec<PendingRasterizationImage>,
653    /// The list of `SVGSVGElement`s encountered in the DOM that need to be serialized.
654    /// This is needed to support inline SVGs as the serialization needs to happen on
655    /// the script thread.
656    pub pending_svg_elements_for_serialization: Vec<UntrustedNodeAddress>,
657    /// The list of iframes in this layout and their sizes, used in order
658    /// to communicate them with the Constellation and also the `Window`
659    /// element of their content pages. Returning None if incremental reflow
660    /// finished before reaching this stage of the layout. I.e., no update
661    /// required.
662    pub iframe_sizes: Option<IFrameSizes>,
663    /// Enumerates web fonts that were added or removed as part of restyling.
664    pub changed_web_fonts: WebFontSetDifference,
665    /// The LCP candidate during this layout pass, if any.
666    pub lcp_candidate: Option<LCPCandidate>,
667    /// Actions which have been requested by assistive technology, if any.
668    pub pending_accessibility_actions: Vec<AccessibilityActionRequest>,
669    /// The containers whose painted area grew during this layout pass, if any.
670    pub container_timing_records: Vec<ContainerTimingRecord>,
671}
672
673bitflags! {
674    /// The phases of reflow that were run when processing a reflow in layout.
675    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
676    pub struct ReflowPhasesRun: u8 {
677        const RanLayout = 1 << 0;
678        const BuiltStackingContextTree = 1 << 2;
679        const BuiltDisplayList = 1 << 3;
680        const UpdatedScrollNodeOffset = 1 << 4;
681        /// Image data for a WebRender image key has been updated, without necessarily
682        /// updating style or layout. This is used when updating canvas contents and
683        /// progressing to a new animated image frame.
684        const UpdatedImageData = 1 << 5;
685        const UpdatedAccessibilityTree = 1 << 6;
686    }
687}
688
689impl ReflowPhasesRun {
690    pub fn needs_frame(&self) -> bool {
691        self.intersects(
692            Self::BuiltDisplayList | Self::UpdatedScrollNodeOffset | Self::UpdatedImageData,
693        )
694    }
695}
696
697#[derive(Debug, Default)]
698pub struct ReflowStatistics {
699    /// A count of the number of fragments that have been completely rebuilt.
700    pub rebuilt_fragment_count: u32,
701    /// A count of the number of fragments that are reused, but have had their style change.
702    pub restyle_fragment_count: u32,
703    /// A count of the number of fragments that are reused, but may have had some descendant
704    /// fragment change.
705    pub only_descendants_changed_count: u32,
706    /// A count of the number of accessibility nodes which were checked for changes based on their
707    /// corresponding DOM nodes (whether the check resulted in changes or not).
708    pub nodes_updated_from_dom: u32,
709    /// A count of the number of accessibility nodes which were checked for changes based on data
710    /// already in the accessibility tree (whether the check resulted in changes or not).
711    pub nodes_updated_from_tree: u32,
712    /// A count of the number of accessibility nodes which had their bounds recomputed from layout
713    /// geometry (whether the recomputation resulted in changes or not).
714    pub nodes_updated_bounds: u32,
715    /// A count of the number of accessibility nodes actually serialized to the TreeUpdate.
716    pub nodes_in_tree_update: u32,
717}
718
719/// Information needed for a script-initiated reflow that requires a restyle
720/// and reconstruction of box and fragment trees.
721#[derive(Debug)]
722pub struct ReflowRequestRestyle {
723    /// Whether or not (and for what reasons) restyle needs to happen.
724    pub reason: RestyleReason,
725    /// The dirty root from which to restyle.
726    pub dirty_root: Option<TrustedNodeAddress>,
727    /// Whether the document's stylesheets have changed since the last script reflow.
728    pub stylesheets_changed: bool,
729    /// Restyle snapshot map.
730    pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
731}
732
733/// Information needed for a script-initiated reflow that requires the accessibility tree to be
734/// updated.
735#[derive(Debug)]
736pub struct ReflowRequestAccessibility {
737    /// Damage to the accessibility tree from DOM mutations.
738    pub damage: Vec<(TrustedNodeAddress, AccessibilityDamage)>,
739    /// The document's focused element at the time of the reflow, if any.
740    pub focused_element: Option<OpaqueNode>,
741    /// Nodes which were removed from the DOM tree since the last reflow, which were rooted in
742    /// [`AccessibilityData`]. Only set if [`pref::expensive_accessibility_test_assertions_enabled`]
743    /// is set.
744    pub rooted_nodes_for_integrity_check: Option<FxHashSet<OpaqueNode>>,
745}
746
747/// Information needed for a script-initiated reflow.
748#[derive(Debug)]
749pub struct ReflowRequest {
750    /// The document node.
751    pub document: TrustedNodeAddress,
752    /// The current layout [`Epoch`] managed by the script thread.
753    pub epoch: Epoch,
754    /// If a restyle is necessary, all of the information needed to do that restyle.
755    pub restyle: Option<ReflowRequestRestyle>,
756    /// The current [`ViewportDetails`] to use for this reflow.
757    pub viewport_details: ViewportDetails,
758    /// The goal of this reflow.
759    pub reflow_goal: ReflowGoal,
760    /// The current window origin
761    pub origin: ImmutableOrigin,
762    /// The current animation timeline value.
763    pub animation_timeline_value: f64,
764    /// The set of animations for this document.
765    pub animations: DocumentAnimationSet,
766    /// An [`AnimatingImages`] struct used to track images that are animating.
767    pub animating_images: Arc<RwLock<AnimatingImages>>,
768    /// The node highlighted by the devtools, if any
769    pub highlighted_dom_node: Option<OpaqueNode>,
770    /// Whether or not this document has system focus and is the focused frame
771    /// in the frame tree.
772    pub frame_focused: bool,
773    /// Whether LCP computation should be halted for this reflow.
774    /// From <https://www.w3.org/TR/largest-contentful-paint/#limitations>:
775    /// > The LargestContentfulPaint ... algorithm halts ... inputs.
776    pub halt_lcp: bool,
777    /// Whether the document's browsing context is paint-timing eligible.
778    /// <https://www.w3.org/TR/paint-timing/#paint-timing-eligible>
779    pub paint_timing_eligible: bool,
780    /// The [`PaintTimingInfo`] for this reflow.
781    /// <https://www.w3.org/TR/paint-timing/#paint-timing-info>
782    pub paint_timing_info: PaintTimingInfo,
783    /// The current font context.
784    pub document_context: WebFontDocumentContext,
785    /// If an accessibility tree update is necessary, all the information needed to do the update.
786    pub accessibility: Option<ReflowRequestAccessibility>,
787}
788
789impl ReflowRequest {
790    pub fn stylesheets_changed(&self) -> bool {
791        self.restyle
792            .as_ref()
793            .is_some_and(|restyle| restyle.stylesheets_changed)
794    }
795}
796
797/// A pending restyle.
798#[derive(Debug, Default, MallocSizeOf)]
799pub struct PendingRestyle {
800    /// If this element had a state or attribute change since the last restyle, track
801    /// the original condition of the element.
802    pub snapshot: Option<Snapshot>,
803
804    /// Any explicit restyles hints that have been accumulated for this element.
805    pub hint: RestyleHint,
806
807    /// Any explicit restyles damage that have been accumulated for this element.
808    pub damage: RestyleDamage,
809}
810
811/// The type of fragment that a scroll root is created for.
812///
813/// This can only ever grow to maximum 4 entries. That's because we cram the value of this enum
814/// into the lower 2 bits of the `OpaqueNodeId`, which otherwise contains a 32-bit-aligned
815/// or 64-bit-aligned heap address depending on the machine.
816#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
817pub enum FragmentType {
818    /// A StackingContext for the fragment body itself.
819    FragmentBody,
820    /// A StackingContext created to contain ::before pseudo-element content.
821    BeforePseudoContent,
822    /// A StackingContext created to contain ::after pseudo-element content.
823    AfterPseudoContent,
824}
825
826impl From<Option<PseudoElement>> for FragmentType {
827    fn from(value: Option<PseudoElement>) -> Self {
828        match value {
829            Some(PseudoElement::After) => FragmentType::AfterPseudoContent,
830            Some(PseudoElement::Before) => FragmentType::BeforePseudoContent,
831            _ => FragmentType::FragmentBody,
832        }
833    }
834}
835
836pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> u64 {
837    debug_assert_eq!(id & (fragment_type as usize), 0);
838    (id as u64) | (fragment_type as u64)
839}
840
841pub fn node_id_from_scroll_id(id: usize) -> usize {
842    id & !3
843}
844
845#[derive(Clone, Debug, MallocSizeOf)]
846pub struct ImageAnimationState {
847    #[conditional_malloc_size_of]
848    pub image: Arc<RasterImage>,
849    pub active_frame: usize,
850    frame_start_time: f64,
851
852    /// The number of loops that have fully completed in this [`ImageAnimationState`].
853    /// If this is greater than or equal to the maximum number of loops in the
854    /// [`RasterImage`], then the animation has ended. If it is `None`, then the image
855    /// will loop infinitely.
856    pub completed_loops: Option<u32>,
857}
858
859impl ImageAnimationState {
860    pub fn new(image: Arc<RasterImage>, last_update_time: f64) -> Self {
861        let completd_loops = match &image.loop_count {
862            None => unreachable!("Loop count of an animated Image should never be None"),
863            Some(repeat) if Repeat::Infinite == *repeat => None,
864            _ => Some(0),
865        };
866
867        Self {
868            image,
869            active_frame: 0,
870            frame_start_time: last_update_time,
871            completed_loops: completd_loops,
872        }
873    }
874
875    pub fn image_key(&self) -> Option<ImageKey> {
876        self.image.id
877    }
878
879    pub fn duration_to_next_frame(&self, now: f64) -> Option<Duration> {
880        if self.is_finished() {
881            return None;
882        }
883        let frame_delay = self
884            .image
885            .frames
886            .get(self.active_frame)
887            .expect("Image frame should always be valid")
888            .delay
889            .unwrap_or_default();
890
891        let time_since_frame_start = (now - self.frame_start_time).max(0.0) * 1000.0;
892        let time_since_frame_start = Duration::from_secs_f64(time_since_frame_start);
893        Some(frame_delay - time_since_frame_start.min(frame_delay))
894    }
895
896    /// check whether image active frame need to be updated given current time,
897    /// return true if there are image that need to be updated.
898    /// false otherwise.
899    pub fn update_frame_for_animation_timeline_value(&mut self, now: f64) -> bool {
900        if self.image.frames.len() <= 1 || self.is_finished() {
901            return false;
902        }
903        let time_interval_since_last_update = now - self.frame_start_time;
904        let mut remain_time_interval = time_interval_since_last_update -
905            self.image
906                .frames
907                .get(self.active_frame)
908                .unwrap()
909                .delay()
910                .unwrap()
911                .as_secs_f64();
912        let mut next_active_frame_id = self.active_frame;
913
914        let frame_count = self.image.frames.len();
915        while remain_time_interval > 0.0 {
916            next_active_frame_id = (next_active_frame_id + 1) % frame_count;
917
918            // If the next active frame is 0, this means the animation is about to loop.
919            if next_active_frame_id == 0 {
920                self.advance_completed_loops();
921
922                // If we have just finished the animation, advance to the final frame if
923                // necessary and stop walking through frames.
924                if self.is_finished() {
925                    if self.active_frame == frame_count - 1 {
926                        return false;
927                    }
928                    self.active_frame = frame_count - 1;
929                    self.frame_start_time = now;
930                    return true;
931                }
932            }
933
934            remain_time_interval -= self
935                .image
936                .frames
937                .get(next_active_frame_id)
938                .unwrap()
939                .delay()
940                .unwrap()
941                .as_secs_f64();
942        }
943        if self.active_frame == next_active_frame_id {
944            return false;
945        }
946        self.active_frame = next_active_frame_id;
947        self.frame_start_time = now;
948        true
949    }
950
951    /// Whether or not this animation has finished looping and has reached its final frame.
952    fn is_finished(&self) -> bool {
953        let Some(Repeat::Finite(maximum_loops)) = self.image.loop_count.as_ref() else {
954            return false;
955        };
956        self.completed_loops
957            .is_some_and(|completed_loops| completed_loops >= maximum_loops.get())
958    }
959
960    /// If this animation has a finite number of loops, advance the count of completed loops.
961    fn advance_completed_loops(&mut self) {
962        if let Some(completed_loops) = self.completed_loops.as_mut() {
963            *completed_loops += 1;
964        }
965    }
966}
967
968/// The result of a hit test query.
969#[derive(Debug, Default)]
970pub struct HitTestResult {
971    pub items: Vec<HitTestResultItem>,
972    pub dom_position_for_selection: Option<(OpaqueNode, Utf32CodeUnitsOrNodeOffset)>,
973}
974
975/// Describe an item that matched a hit-test query.
976#[derive(Debug)]
977pub struct HitTestResultItem {
978    /// An [`OpaqueNode`] that contains a pointer to the node hit by
979    /// this hit test result.
980    pub node: OpaqueNode,
981    /// The [`Point2D`] of the original query point relative to the
982    /// node fragment rectangle.
983    pub point_in_target: Point2D<f32, CSSPixel>,
984    /// The [`Cursor`] that's defined on the item that is hit by this
985    /// hit test result.
986    pub cursor: Cursor,
987}
988
989#[derive(Debug, Default, MallocSizeOf)]
990pub struct AnimatingImages {
991    /// A map from the [`OpaqueNode`] to the state of an animating image. This is used
992    /// to update frames in script and to track newly animating nodes.
993    pub node_to_state_map: FxHashMap<OpaqueNode, ImageAnimationState>,
994    /// Whether or not this map has changed during a layout. This is used by script to
995    /// trigger future animation updates.
996    pub dirty: bool,
997}
998
999impl AnimatingImages {
1000    pub fn maybe_insert_or_update(
1001        &mut self,
1002        node: OpaqueNode,
1003        image: Arc<RasterImage>,
1004        current_timeline_value: f64,
1005    ) {
1006        let entry = self.node_to_state_map.entry(node).or_insert_with(|| {
1007            self.dirty = true;
1008            ImageAnimationState::new(image.clone(), current_timeline_value)
1009        });
1010
1011        // If the entry exists, but it is for a different image id, replace it as the image
1012        // has changed during this layout.
1013        if entry.image.id != image.id {
1014            self.dirty = true;
1015            *entry = ImageAnimationState::new(image.clone(), current_timeline_value);
1016        }
1017    }
1018
1019    pub fn remove(&mut self, node: OpaqueNode) {
1020        if self.node_to_state_map.remove(&node).is_some() {
1021            self.dirty = true;
1022        }
1023    }
1024
1025    /// Clear the dirty bit on this [`AnimatingImages`] and return the previous value.
1026    pub fn clear_dirty(&mut self) -> bool {
1027        std::mem::take(&mut self.dirty)
1028    }
1029
1030    pub fn is_empty(&self) -> bool {
1031        self.node_to_state_map.is_empty()
1032    }
1033}
1034
1035struct ThreadStateRestorer;
1036
1037impl ThreadStateRestorer {
1038    fn new() -> Self {
1039        #[cfg(debug_assertions)]
1040        {
1041            thread_state::exit(ThreadState::SCRIPT);
1042            thread_state::enter(ThreadState::LAYOUT);
1043        }
1044        Self
1045    }
1046}
1047
1048impl Drop for ThreadStateRestorer {
1049    fn drop(&mut self) {
1050        #[cfg(debug_assertions)]
1051        {
1052            thread_state::exit(ThreadState::LAYOUT);
1053            thread_state::enter(ThreadState::SCRIPT);
1054        }
1055    }
1056}
1057
1058/// Set up the thread-local state to reflect that layout code is about to run,
1059/// then call the provided function.
1060/// This must be used when running code that will interact with the DOM tree
1061/// through types like `ServoLayoutNode`, `ServoLayoutElement`, and `LayoutDom`,
1062/// which have rules about how they must be used from layout worker threads.
1063pub fn with_layout_state<R>(f: impl FnOnce() -> R) -> R {
1064    let _guard = ThreadStateRestorer::new();
1065    f()
1066}
1067
1068#[cfg(test)]
1069mod test {
1070    use std::num::NonZeroU32;
1071    use std::sync::Arc;
1072    use std::time::Duration;
1073
1074    use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage, Repeat};
1075
1076    use crate::ImageAnimationState;
1077
1078    #[test]
1079    fn test_animated_image_update() {
1080        let image_frames: Vec<ImageFrame> = std::iter::repeat_with(|| ImageFrame {
1081            delay: Some(Duration::from_millis(100)),
1082            byte_range: 0..1,
1083            width: 100,
1084            height: 100,
1085        })
1086        .take(10)
1087        .collect();
1088        let image = RasterImage {
1089            metadata: ImageMetadata {
1090                width: 100,
1091                height: 100,
1092            },
1093            format: PixelFormat::BGRA8,
1094            id: None,
1095            bytes: Arc::new(vec![1]),
1096            frames: image_frames,
1097            cors_status: CorsStatus::Unsafe,
1098            load_time: None,
1099            loop_count: Some(Repeat::Infinite),
1100            is_opaque: false,
1101        };
1102        let mut image_animation_state = ImageAnimationState::new(Arc::new(image), 0.0);
1103
1104        assert_eq!(image_animation_state.active_frame, 0);
1105        assert_eq!(image_animation_state.frame_start_time, 0.0);
1106        assert_eq!(
1107            image_animation_state.update_frame_for_animation_timeline_value(0.101),
1108            true
1109        );
1110        assert_eq!(image_animation_state.active_frame, 1);
1111        assert_eq!(image_animation_state.frame_start_time, 0.101);
1112        assert_eq!(
1113            image_animation_state.update_frame_for_animation_timeline_value(0.116),
1114            false
1115        );
1116        assert_eq!(image_animation_state.active_frame, 1);
1117        assert_eq!(image_animation_state.frame_start_time, 0.101);
1118    }
1119
1120    #[test]
1121    fn test_finite_image_repeat() {
1122        let image_frames: Vec<ImageFrame> = std::iter::repeat_with(|| ImageFrame {
1123            delay: Some(Duration::from_millis(100)),
1124            byte_range: 0..1,
1125            width: 100,
1126            height: 100,
1127        })
1128        .take(2)
1129        .collect();
1130        let image = RasterImage {
1131            metadata: ImageMetadata {
1132                width: 100,
1133                height: 100,
1134            },
1135            format: PixelFormat::BGRA8,
1136            id: None,
1137            bytes: Arc::new(vec![1]),
1138            frames: image_frames,
1139            cors_status: CorsStatus::Unsafe,
1140            load_time: None,
1141            loop_count: Some(Repeat::Finite(NonZeroU32::new(1).unwrap())),
1142            is_opaque: false,
1143        };
1144        let mut image_animation_state = ImageAnimationState::new(Arc::new(image), 0.0);
1145
1146        assert_eq!(image_animation_state.active_frame, 0);
1147        assert_eq!(image_animation_state.frame_start_time, 0.0);
1148        assert_eq!(
1149            image_animation_state.update_frame_for_animation_timeline_value(0.101),
1150            true
1151        );
1152        assert_eq!(image_animation_state.active_frame, 1);
1153        assert_eq!(image_animation_state.frame_start_time, 0.101);
1154        assert_eq!(
1155            image_animation_state.update_frame_for_animation_timeline_value(0.202),
1156            false
1157        );
1158        assert_eq!(
1159            image_animation_state.update_frame_for_animation_timeline_value(0.303),
1160            false
1161        );
1162
1163        assert_eq!(image_animation_state.active_frame, 1);
1164    }
1165}