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