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