1#![deny(unsafe_code)]
10
11mod layout_damage;
12pub mod wrapper_traits;
13
14use std::any::Any;
15use std::sync::Arc;
16use std::sync::atomic::{AtomicIsize, AtomicU64, Ordering};
17use std::thread::JoinHandle;
18use std::time::Duration;
19
20use app_units::Au;
21use atomic_refcell::AtomicRefCell;
22use background_hang_monitor_api::BackgroundHangMonitorRegister;
23use base::Epoch;
24use base::generic_channel::GenericSender;
25use base::id::{BrowsingContextId, PipelineId, WebViewId};
26use bitflags::bitflags;
27use compositing_traits::CrossProcessCompositorApi;
28use embedder_traits::{Cursor, Theme, UntrustedNodeAddress, ViewportDetails};
29use euclid::Point2D;
30use euclid::default::{Point2D as UntypedPoint2D, Rect};
31use fonts::FontContext;
32pub use layout_damage::LayoutDamage;
33use libc::c_void;
34use malloc_size_of::{MallocSizeOf as MallocSizeOfTrait, MallocSizeOfOps, malloc_size_of_is_0};
35use malloc_size_of_derive::MallocSizeOf;
36use net_traits::image_cache::{ImageCache, ImageCacheFactory, PendingImageId};
37use parking_lot::RwLock;
38use pixels::RasterImage;
39use profile_traits::mem::Report;
40use profile_traits::time;
41use rustc_hash::FxHashMap;
42use script_traits::{InitialScriptState, NewPipelineInfo, Painter, ScriptThreadMessage};
43use serde::{Deserialize, Serialize};
44use servo_arc::Arc as ServoArc;
45use servo_url::{ImmutableOrigin, ServoUrl};
46use style::Atom;
47use style::animation::DocumentAnimationSet;
48use style::context::QuirksMode;
49use style::data::ElementData;
50use style::dom::OpaqueNode;
51use style::invalidation::element::restyle_hints::RestyleHint;
52use style::media_queries::Device;
53use style::properties::style_structs::Font;
54use style::properties::{ComputedValues, PropertyId};
55use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot};
56use style::stylesheets::{Stylesheet, UrlExtraData};
57use style::values::computed::Overflow;
58use style_traits::CSSPixel;
59use webrender_api::units::{DeviceIntSize, LayoutPoint, LayoutVector2D};
60use webrender_api::{ExternalScrollId, ImageKey};
61
62pub trait GenericLayoutDataTrait: Any + MallocSizeOfTrait {
63 fn as_any(&self) -> &dyn Any;
64}
65
66pub type GenericLayoutData = dyn GenericLayoutDataTrait + Send + Sync;
67
68#[derive(MallocSizeOf)]
69pub struct StyleData {
70 pub element_data: AtomicRefCell<ElementData>,
75
76 pub parallel: DomParallelInfo,
78}
79
80impl Default for StyleData {
81 fn default() -> Self {
82 Self {
83 element_data: AtomicRefCell::new(ElementData::default()),
84 parallel: DomParallelInfo::default(),
85 }
86 }
87}
88
89#[derive(Default, MallocSizeOf)]
91pub struct DomParallelInfo {
92 pub children_to_process: AtomicIsize,
94}
95
96#[derive(Clone, Copy, Debug, Eq, PartialEq)]
97pub enum LayoutNodeType {
98 Element(LayoutElementType),
99 Text,
100}
101
102#[derive(Clone, Copy, Debug, Eq, PartialEq)]
103pub enum LayoutElementType {
104 Element,
105 HTMLBodyElement,
106 HTMLBRElement,
107 HTMLCanvasElement,
108 HTMLHtmlElement,
109 HTMLIFrameElement,
110 HTMLImageElement,
111 HTMLInputElement,
112 HTMLMediaElement,
113 HTMLObjectElement,
114 HTMLOptGroupElement,
115 HTMLOptionElement,
116 HTMLParagraphElement,
117 HTMLPreElement,
118 HTMLSelectElement,
119 HTMLTableCellElement,
120 HTMLTableColElement,
121 HTMLTableElement,
122 HTMLTableRowElement,
123 HTMLTableSectionElement,
124 HTMLTextAreaElement,
125 SVGImageElement,
126 SVGSVGElement,
127}
128
129pub struct HTMLCanvasData {
130 pub image_key: Option<ImageKey>,
131 pub width: u32,
132 pub height: u32,
133}
134
135pub struct SVGElementData {
136 pub source: Option<Result<ServoUrl, ()>>,
138 pub width: Option<i32>,
139 pub height: Option<i32>,
140 pub ratio: Option<f32>,
141}
142
143#[derive(Clone, Copy, Debug, Eq, PartialEq)]
145pub struct TrustedNodeAddress(pub *const c_void);
146
147#[expect(unsafe_code)]
148unsafe impl Send for TrustedNodeAddress {}
149
150#[derive(Debug)]
152pub enum PendingImageState {
153 Unrequested(ServoUrl),
154 PendingResponse,
155}
156
157#[derive(Debug, MallocSizeOf)]
159pub enum LayoutImageDestination {
160 BoxTreeConstruction,
161 DisplayListBuilding,
162}
163
164#[derive(Debug)]
168pub struct PendingImage {
169 pub state: PendingImageState,
170 pub node: UntrustedNodeAddress,
171 pub id: PendingImageId,
172 pub origin: ImmutableOrigin,
173 pub destination: LayoutImageDestination,
174}
175
176#[derive(Debug)]
180pub struct PendingRasterizationImage {
181 pub node: UntrustedNodeAddress,
182 pub id: PendingImageId,
183 pub size: DeviceIntSize,
184}
185
186#[derive(Clone, Copy, Debug, MallocSizeOf)]
187pub struct MediaFrame {
188 pub image_key: webrender_api::ImageKey,
189 pub width: i32,
190 pub height: i32,
191}
192
193pub struct MediaMetadata {
194 pub width: u32,
195 pub height: u32,
196}
197
198pub struct HTMLMediaData {
199 pub current_frame: Option<MediaFrame>,
200 pub metadata: Option<MediaMetadata>,
201}
202
203pub struct LayoutConfig {
204 pub id: PipelineId,
205 pub webview_id: WebViewId,
206 pub url: ServoUrl,
207 pub is_iframe: bool,
208 pub script_chan: GenericSender<ScriptThreadMessage>,
209 pub image_cache: Arc<dyn ImageCache>,
210 pub font_context: Arc<FontContext>,
211 pub time_profiler_chan: time::ProfilerChan,
212 pub compositor_api: CrossProcessCompositorApi,
213 pub viewport_details: ViewportDetails,
214 pub theme: Theme,
215}
216
217pub struct PropertyRegistration {
218 pub name: String,
219 pub syntax: String,
220 pub initial_value: Option<String>,
221 pub inherits: bool,
222 pub url_data: UrlExtraData,
223}
224
225#[derive(Debug)]
226pub enum RegisterPropertyError {
227 InvalidName,
228 AlreadyRegistered,
229 InvalidSyntax,
230 InvalidInitialValue,
231 InitialValueNotComputationallyIndependent,
232 NoInitialValue,
233}
234
235pub trait LayoutFactory: Send + Sync {
236 fn create(&self, config: LayoutConfig) -> Box<dyn Layout>;
237}
238
239pub trait Layout {
240 fn device(&self) -> &Device;
243
244 fn set_theme(&mut self, theme: Theme) -> bool;
248
249 fn set_viewport_details(&mut self, viewport_details: ViewportDetails) -> bool;
253
254 fn load_web_fonts_from_stylesheet(&self, stylesheet: &ServoArc<Stylesheet>);
257
258 fn add_stylesheet(
262 &mut self,
263 stylesheet: ServoArc<Stylesheet>,
264 before_stylsheet: Option<ServoArc<Stylesheet>>,
265 );
266
267 fn exit_now(&mut self);
269
270 fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps);
273
274 fn set_quirks_mode(&mut self, quirks_mode: QuirksMode);
276
277 fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>);
279
280 fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult>;
282
283 fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails);
286
287 fn register_paint_worklet_modules(
289 &mut self,
290 name: Atom,
291 properties: Vec<Atom>,
292 painter: Box<dyn Painter>,
293 );
294
295 fn set_scroll_offsets_from_renderer(
297 &mut self,
298 scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
299 );
300
301 fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D>;
304
305 fn needs_new_display_list(&self) -> bool;
307
308 fn set_needs_new_display_list(&self);
310
311 fn query_padding(&self, node: TrustedNodeAddress) -> Option<PhysicalSides>;
312 fn query_box_area(
313 &self,
314 node: TrustedNodeAddress,
315 area: BoxAreaType,
316 exclude_transform_and_inline: bool,
317 ) -> Option<Rect<Au>>;
318 fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Vec<Rect<Au>>;
319 fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32>;
320 fn query_current_css_zoom(&self, node: TrustedNodeAddress) -> f32;
321 fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String;
322 fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse;
323 fn query_scroll_container(
326 &self,
327 node: Option<TrustedNodeAddress>,
328 flags: ScrollContainerQueryFlags,
329 ) -> Option<ScrollContainerResponse>;
330 fn query_resolved_style(
331 &self,
332 node: TrustedNodeAddress,
333 pseudo: Option<PseudoElement>,
334 property_id: PropertyId,
335 animations: DocumentAnimationSet,
336 animation_timeline_value: f64,
337 ) -> String;
338 fn query_resolved_font_style(
339 &self,
340 node: TrustedNodeAddress,
341 value: &str,
342 animations: DocumentAnimationSet,
343 animation_timeline_value: f64,
344 ) -> Option<ServoArc<Font>>;
345 fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32>;
346 fn query_text_indext(&self, node: OpaqueNode, point: UntypedPoint2D<f32>) -> Option<usize>;
347 fn query_elements_from_point(
348 &self,
349 point: LayoutPoint,
350 flags: ElementsFromPointFlags,
351 ) -> Vec<ElementsFromPointResult>;
352 fn register_custom_property(
353 &mut self,
354 property_registration: PropertyRegistration,
355 ) -> Result<(), RegisterPropertyError>;
356}
357
358pub trait ScriptThreadFactory {
362 fn create(
364 state: InitialScriptState,
365 new_pipeline_info: NewPipelineInfo,
366 layout_factory: Arc<dyn LayoutFactory>,
367 image_cache_factory: Arc<dyn ImageCacheFactory>,
368 background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
369 ) -> JoinHandle<()>;
370}
371
372#[derive(Copy, Clone)]
375pub enum BoxAreaType {
376 Content,
377 Padding,
378 Border,
379}
380
381#[derive(Default)]
382pub struct PhysicalSides {
383 pub left: Au,
384 pub top: Au,
385 pub right: Au,
386 pub bottom: Au,
387}
388
389#[derive(Clone, Default)]
390pub struct OffsetParentResponse {
391 pub node_address: Option<UntrustedNodeAddress>,
392 pub rect: Rect<Au>,
393}
394
395bitflags! {
396 #[derive(PartialEq)]
397 pub struct ScrollContainerQueryFlags: u8 {
398 const ForScrollParent = 1 << 0;
400 const Inclusive = 1 << 1;
402 }
403}
404
405#[derive(Clone, Copy, Debug, MallocSizeOf)]
406pub struct AxesOverflow {
407 pub x: Overflow,
408 pub y: Overflow,
409}
410
411impl Default for AxesOverflow {
412 fn default() -> Self {
413 Self {
414 x: Overflow::Visible,
415 y: Overflow::Visible,
416 }
417 }
418}
419
420impl From<&ComputedValues> for AxesOverflow {
421 fn from(style: &ComputedValues) -> Self {
422 Self {
423 x: style.clone_overflow_x(),
424 y: style.clone_overflow_y(),
425 }
426 }
427}
428
429impl AxesOverflow {
430 pub fn to_scrollable(&self) -> Self {
431 Self {
432 x: self.x.to_scrollable(),
433 y: self.y.to_scrollable(),
434 }
435 }
436}
437
438#[derive(Clone)]
439pub enum ScrollContainerResponse {
440 Viewport(AxesOverflow),
441 Element(UntrustedNodeAddress, AxesOverflow),
442}
443
444#[derive(Debug, PartialEq)]
445pub enum QueryMsg {
446 BoxArea,
447 BoxAreas,
448 ClientRectQuery,
449 CurrentCSSZoomQuery,
450 ElementInnerOuterTextQuery,
451 ElementsFromPoint,
452 InnerWindowDimensionsQuery,
453 NodesFromPointQuery,
454 OffsetParentQuery,
455 ScrollParentQuery,
456 ResolvedFontStyleQuery,
457 ResolvedStyleQuery,
458 ScrollingAreaOrOffsetQuery,
459 StyleQuery,
460 TextIndexQuery,
461 PaddingQuery,
462}
463
464#[derive(Debug, PartialEq)]
470pub enum ReflowGoal {
471 UpdateTheRendering,
474
475 LayoutQuery(QueryMsg),
478
479 UpdateScrollNode(ExternalScrollId, LayoutVector2D),
483}
484
485#[derive(Clone, Debug, MallocSizeOf)]
486pub struct IFrameSize {
487 pub browsing_context_id: BrowsingContextId,
488 pub pipeline_id: PipelineId,
489 pub viewport_details: ViewportDetails,
490}
491
492pub type IFrameSizes = FxHashMap<BrowsingContextId, IFrameSize>;
493
494bitflags! {
495 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
498 pub struct RestyleReason: u16 {
499 const StylesheetsChanged = 1 << 0;
500 const DOMChanged = 1 << 1;
501 const PendingRestyles = 1 << 2;
502 const HighlightedDOMNodeChanged = 1 << 3;
503 const ThemeChanged = 1 << 4;
504 const ViewportChanged = 1 << 5;
505 const PaintWorkletLoaded = 1 << 6;
506 }
507}
508
509malloc_size_of_is_0!(RestyleReason);
510
511impl RestyleReason {
512 pub fn needs_restyle(&self) -> bool {
513 !self.is_empty()
514 }
515}
516
517#[derive(Debug, Default)]
519pub struct ReflowResult {
520 pub reflow_phases_run: ReflowPhasesRun,
522 pub pending_images: Vec<PendingImage>,
524 pub pending_rasterization_images: Vec<PendingRasterizationImage>,
526 pub pending_svg_elements_for_serialization: Vec<UntrustedNodeAddress>,
530 pub iframe_sizes: Option<IFrameSizes>,
536}
537
538bitflags! {
539 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
541 pub struct ReflowPhasesRun: u8 {
542 const RanLayout = 1 << 0;
543 const CalculatedOverflow = 1 << 1;
544 const BuiltStackingContextTree = 1 << 2;
545 const BuiltDisplayList = 1 << 3;
546 const UpdatedScrollNodeOffset = 1 << 4;
547 const UpdatedImageData = 1 << 5;
551 }
552}
553
554impl ReflowPhasesRun {
555 pub fn needs_frame(&self) -> bool {
556 self.intersects(
557 Self::BuiltDisplayList | Self::UpdatedScrollNodeOffset | Self::UpdatedImageData,
558 )
559 }
560}
561
562#[derive(Debug)]
565pub struct ReflowRequestRestyle {
566 pub reason: RestyleReason,
568 pub dirty_root: Option<TrustedNodeAddress>,
570 pub stylesheets_changed: bool,
572 pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
574}
575
576#[derive(Debug)]
578pub struct ReflowRequest {
579 pub document: TrustedNodeAddress,
581 pub epoch: Epoch,
583 pub restyle: Option<ReflowRequestRestyle>,
585 pub viewport_details: ViewportDetails,
587 pub reflow_goal: ReflowGoal,
589 pub dom_count: u32,
591 pub origin: ImmutableOrigin,
593 pub animation_timeline_value: f64,
595 pub animations: DocumentAnimationSet,
597 pub animating_images: Arc<RwLock<AnimatingImages>>,
599 pub highlighted_dom_node: Option<OpaqueNode>,
601}
602
603impl ReflowRequest {
604 pub fn stylesheets_changed(&self) -> bool {
605 self.restyle
606 .as_ref()
607 .is_some_and(|restyle| restyle.stylesheets_changed)
608 }
609}
610
611#[derive(Debug, Default, MallocSizeOf)]
613pub struct PendingRestyle {
614 pub snapshot: Option<Snapshot>,
617
618 pub hint: RestyleHint,
620
621 pub damage: RestyleDamage,
623}
624
625#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
631pub enum FragmentType {
632 FragmentBody,
634 BeforePseudoContent,
636 AfterPseudoContent,
638}
639
640impl From<Option<PseudoElement>> for FragmentType {
641 fn from(value: Option<PseudoElement>) -> Self {
642 match value {
643 Some(PseudoElement::After) => FragmentType::AfterPseudoContent,
644 Some(PseudoElement::Before) => FragmentType::BeforePseudoContent,
645 _ => FragmentType::FragmentBody,
646 }
647 }
648}
649
650static NEXT_SPECIAL_SCROLL_ROOT_ID: AtomicU64 = AtomicU64::new(0);
654
655const SPECIAL_SCROLL_ROOT_ID_MASK: u64 = 0xffff;
658
659fn next_special_id() -> u64 {
661 ((NEXT_SPECIAL_SCROLL_ROOT_ID.fetch_add(1, Ordering::SeqCst) + 1) << 2) &
663 SPECIAL_SCROLL_ROOT_ID_MASK
664}
665
666pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> u64 {
667 debug_assert_eq!(id & (fragment_type as usize), 0);
668 if fragment_type == FragmentType::FragmentBody {
669 id as u64
670 } else {
671 next_special_id() | (fragment_type as u64)
672 }
673}
674
675pub fn node_id_from_scroll_id(id: usize) -> Option<usize> {
676 if (id as u64 & !SPECIAL_SCROLL_ROOT_ID_MASK) != 0 {
677 return Some(id & !3);
678 }
679 None
680}
681
682#[derive(Clone, Debug, MallocSizeOf)]
683pub struct ImageAnimationState {
684 #[ignore_malloc_size_of = "RasterImage"]
685 pub image: Arc<RasterImage>,
686 pub active_frame: usize,
687 frame_start_time: f64,
688}
689
690impl ImageAnimationState {
691 pub fn new(image: Arc<RasterImage>, last_update_time: f64) -> Self {
692 Self {
693 image,
694 active_frame: 0,
695 frame_start_time: last_update_time,
696 }
697 }
698
699 pub fn image_key(&self) -> Option<ImageKey> {
700 self.image.id
701 }
702
703 pub fn duration_to_next_frame(&self, now: f64) -> Duration {
704 let frame_delay = self
705 .image
706 .frames
707 .get(self.active_frame)
708 .expect("Image frame should always be valid")
709 .delay
710 .unwrap_or_default();
711
712 let time_since_frame_start = (now - self.frame_start_time).max(0.0) * 1000.0;
713 let time_since_frame_start = Duration::from_secs_f64(time_since_frame_start);
714 frame_delay - time_since_frame_start.min(frame_delay)
715 }
716
717 pub fn update_frame_for_animation_timeline_value(&mut self, now: f64) -> bool {
721 if self.image.frames.len() <= 1 {
722 return false;
723 }
724 let image = &self.image;
725 let time_interval_since_last_update = now - self.frame_start_time;
726 let mut remain_time_interval = time_interval_since_last_update -
727 image
728 .frames
729 .get(self.active_frame)
730 .unwrap()
731 .delay()
732 .unwrap()
733 .as_secs_f64();
734 let mut next_active_frame_id = self.active_frame;
735 while remain_time_interval > 0.0 {
736 next_active_frame_id = (next_active_frame_id + 1) % image.frames.len();
737 remain_time_interval -= image
738 .frames
739 .get(next_active_frame_id)
740 .unwrap()
741 .delay()
742 .unwrap()
743 .as_secs_f64();
744 }
745 if self.active_frame == next_active_frame_id {
746 return false;
747 }
748 self.active_frame = next_active_frame_id;
749 self.frame_start_time = now;
750 true
751 }
752}
753
754#[derive(Debug)]
756pub struct ElementsFromPointResult {
757 pub node: OpaqueNode,
760 pub point_in_target: Point2D<f32, CSSPixel>,
763 pub cursor: Cursor,
766}
767
768bitflags! {
769 pub struct ElementsFromPointFlags: u8 {
770 const FindAll = 0b00000001;
773 }
774}
775
776#[derive(Debug, Default, MallocSizeOf)]
777pub struct AnimatingImages {
778 pub node_to_state_map: FxHashMap<OpaqueNode, ImageAnimationState>,
781 pub dirty: bool,
784}
785
786impl AnimatingImages {
787 pub fn maybe_insert_or_update(
788 &mut self,
789 node: OpaqueNode,
790 image: Arc<RasterImage>,
791 current_timeline_value: f64,
792 ) {
793 let entry = self.node_to_state_map.entry(node).or_insert_with(|| {
794 self.dirty = true;
795 ImageAnimationState::new(image.clone(), current_timeline_value)
796 });
797
798 if entry.image.id != image.id {
801 self.dirty = true;
802 *entry = ImageAnimationState::new(image.clone(), current_timeline_value);
803 }
804 }
805
806 pub fn remove(&mut self, node: OpaqueNode) {
807 if self.node_to_state_map.remove(&node).is_some() {
808 self.dirty = true;
809 }
810 }
811
812 pub fn clear_dirty(&mut self) -> bool {
814 std::mem::take(&mut self.dirty)
815 }
816
817 pub fn is_empty(&self) -> bool {
818 self.node_to_state_map.is_empty()
819 }
820}
821
822#[cfg(test)]
823mod test {
824 use std::sync::Arc;
825 use std::time::Duration;
826
827 use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
828
829 use crate::ImageAnimationState;
830
831 #[test]
832 fn test() {
833 let image_frames: Vec<ImageFrame> = std::iter::repeat_with(|| ImageFrame {
834 delay: Some(Duration::from_millis(100)),
835 byte_range: 0..1,
836 width: 100,
837 height: 100,
838 })
839 .take(10)
840 .collect();
841 let image = RasterImage {
842 metadata: ImageMetadata {
843 width: 100,
844 height: 100,
845 },
846 format: PixelFormat::BGRA8,
847 id: None,
848 bytes: Arc::new(vec![1]),
849 frames: image_frames,
850 cors_status: CorsStatus::Unsafe,
851 is_opaque: false,
852 };
853 let mut image_animation_state = ImageAnimationState::new(Arc::new(image), 0.0);
854
855 assert_eq!(image_animation_state.active_frame, 0);
856 assert_eq!(image_animation_state.frame_start_time, 0.0);
857 assert_eq!(
858 image_animation_state.update_frame_for_animation_timeline_value(0.101),
859 true
860 );
861 assert_eq!(image_animation_state.active_frame, 1);
862 assert_eq!(image_animation_state.frame_start_time, 0.101);
863 assert_eq!(
864 image_animation_state.update_frame_for_animation_timeline_value(0.116),
865 false
866 );
867 assert_eq!(image_animation_state.active_frame, 1);
868 assert_eq!(image_animation_state.frame_start_time, 0.101);
869 }
870}