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, WebFontDocumentContext};
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, 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(
257 &self,
258 stylesheet: &ServoArc<Stylesheet>,
259 font_context: &WebFontDocumentContext,
260 );
261
262 fn add_stylesheet(
266 &mut self,
267 stylesheet: ServoArc<Stylesheet>,
268 before_stylsheet: Option<ServoArc<Stylesheet>>,
269 font_context: &WebFontDocumentContext,
270 );
271
272 fn exit_now(&mut self);
274
275 fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps);
278
279 fn set_quirks_mode(&mut self, quirks_mode: QuirksMode);
281
282 fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>);
284
285 fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult>;
287
288 fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails);
291
292 fn register_paint_worklet_modules(
294 &mut self,
295 name: Atom,
296 properties: Vec<Atom>,
297 painter: Box<dyn Painter>,
298 );
299
300 fn set_scroll_offsets_from_renderer(
302 &mut self,
303 scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
304 );
305
306 fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D>;
309
310 fn needs_new_display_list(&self) -> bool;
312
313 fn set_needs_new_display_list(&self);
315
316 fn query_padding(&self, node: TrustedNodeAddress) -> Option<PhysicalSides>;
317 fn query_box_area(
318 &self,
319 node: TrustedNodeAddress,
320 area: BoxAreaType,
321 exclude_transform_and_inline: bool,
322 ) -> Option<Rect<Au>>;
323 fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Vec<Rect<Au>>;
324 fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32>;
325 fn query_current_css_zoom(&self, node: TrustedNodeAddress) -> f32;
326 fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String;
327 fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse;
328 fn query_scroll_container(
331 &self,
332 node: Option<TrustedNodeAddress>,
333 flags: ScrollContainerQueryFlags,
334 ) -> Option<ScrollContainerResponse>;
335 fn query_resolved_style(
336 &self,
337 node: TrustedNodeAddress,
338 pseudo: Option<PseudoElement>,
339 property_id: PropertyId,
340 animations: DocumentAnimationSet,
341 animation_timeline_value: f64,
342 ) -> String;
343 fn query_resolved_font_style(
344 &self,
345 node: TrustedNodeAddress,
346 value: &str,
347 animations: DocumentAnimationSet,
348 animation_timeline_value: f64,
349 ) -> Option<ServoArc<Font>>;
350 fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32>;
351 fn query_text_indext(&self, node: OpaqueNode, point: UntypedPoint2D<f32>) -> Option<usize>;
352 fn query_elements_from_point(
353 &self,
354 point: LayoutPoint,
355 flags: ElementsFromPointFlags,
356 ) -> Vec<ElementsFromPointResult>;
357 fn register_custom_property(
358 &mut self,
359 property_registration: PropertyRegistration,
360 ) -> Result<(), RegisterPropertyError>;
361}
362
363pub trait ScriptThreadFactory {
367 fn create(
369 state: InitialScriptState,
370 layout_factory: Arc<dyn LayoutFactory>,
371 image_cache_factory: Arc<dyn ImageCacheFactory>,
372 background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
373 ) -> JoinHandle<()>;
374}
375
376#[derive(Copy, Clone)]
379pub enum BoxAreaType {
380 Content,
381 Padding,
382 Border,
383}
384
385#[derive(Default)]
386pub struct PhysicalSides {
387 pub left: Au,
388 pub top: Au,
389 pub right: Au,
390 pub bottom: Au,
391}
392
393#[derive(Clone, Default)]
394pub struct OffsetParentResponse {
395 pub node_address: Option<UntrustedNodeAddress>,
396 pub rect: Rect<Au>,
397}
398
399bitflags! {
400 #[derive(PartialEq)]
401 pub struct ScrollContainerQueryFlags: u8 {
402 const ForScrollParent = 1 << 0;
404 const Inclusive = 1 << 1;
406 }
407}
408
409#[derive(Clone, Copy, Debug, MallocSizeOf)]
410pub struct AxesOverflow {
411 pub x: Overflow,
412 pub y: Overflow,
413}
414
415impl Default for AxesOverflow {
416 fn default() -> Self {
417 Self {
418 x: Overflow::Visible,
419 y: Overflow::Visible,
420 }
421 }
422}
423
424impl From<&ComputedValues> for AxesOverflow {
425 fn from(style: &ComputedValues) -> Self {
426 Self {
427 x: style.clone_overflow_x(),
428 y: style.clone_overflow_y(),
429 }
430 }
431}
432
433impl AxesOverflow {
434 pub fn to_scrollable(&self) -> Self {
435 Self {
436 x: self.x.to_scrollable(),
437 y: self.y.to_scrollable(),
438 }
439 }
440}
441
442#[derive(Clone)]
443pub enum ScrollContainerResponse {
444 Viewport(AxesOverflow),
445 Element(UntrustedNodeAddress, AxesOverflow),
446}
447
448#[derive(Debug, PartialEq)]
449pub enum QueryMsg {
450 BoxArea,
451 BoxAreas,
452 ClientRectQuery,
453 CurrentCSSZoomQuery,
454 ElementInnerOuterTextQuery,
455 ElementsFromPoint,
456 InnerWindowDimensionsQuery,
457 NodesFromPointQuery,
458 OffsetParentQuery,
459 ScrollParentQuery,
460 ResolvedFontStyleQuery,
461 ResolvedStyleQuery,
462 ScrollingAreaOrOffsetQuery,
463 StyleQuery,
464 TextIndexQuery,
465 PaddingQuery,
466}
467
468#[derive(Debug, PartialEq)]
474pub enum ReflowGoal {
475 UpdateTheRendering,
478
479 LayoutQuery(QueryMsg),
482
483 UpdateScrollNode(ExternalScrollId, LayoutVector2D),
487}
488
489#[derive(Clone, Debug, MallocSizeOf)]
490pub struct IFrameSize {
491 pub browsing_context_id: BrowsingContextId,
492 pub pipeline_id: PipelineId,
493 pub viewport_details: ViewportDetails,
494}
495
496pub type IFrameSizes = FxHashMap<BrowsingContextId, IFrameSize>;
497
498bitflags! {
499 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
502 pub struct RestyleReason: u16 {
503 const StylesheetsChanged = 1 << 0;
504 const DOMChanged = 1 << 1;
505 const PendingRestyles = 1 << 2;
506 const HighlightedDOMNodeChanged = 1 << 3;
507 const ThemeChanged = 1 << 4;
508 const ViewportChanged = 1 << 5;
509 const PaintWorkletLoaded = 1 << 6;
510 }
511}
512
513malloc_size_of_is_0!(RestyleReason);
514
515impl RestyleReason {
516 pub fn needs_restyle(&self) -> bool {
517 !self.is_empty()
518 }
519}
520
521#[derive(Debug, Default)]
523pub struct ReflowResult {
524 pub reflow_phases_run: ReflowPhasesRun,
526 pub pending_images: Vec<PendingImage>,
528 pub pending_rasterization_images: Vec<PendingRasterizationImage>,
530 pub pending_svg_elements_for_serialization: Vec<UntrustedNodeAddress>,
534 pub iframe_sizes: Option<IFrameSizes>,
540}
541
542bitflags! {
543 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
545 pub struct ReflowPhasesRun: u8 {
546 const RanLayout = 1 << 0;
547 const CalculatedOverflow = 1 << 1;
548 const BuiltStackingContextTree = 1 << 2;
549 const BuiltDisplayList = 1 << 3;
550 const UpdatedScrollNodeOffset = 1 << 4;
551 const UpdatedImageData = 1 << 5;
555 }
556}
557
558impl ReflowPhasesRun {
559 pub fn needs_frame(&self) -> bool {
560 self.intersects(
561 Self::BuiltDisplayList | Self::UpdatedScrollNodeOffset | Self::UpdatedImageData,
562 )
563 }
564}
565
566#[derive(Debug)]
569pub struct ReflowRequestRestyle {
570 pub reason: RestyleReason,
572 pub dirty_root: Option<TrustedNodeAddress>,
574 pub stylesheets_changed: bool,
576 pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
578}
579
580#[derive(Debug)]
582pub struct ReflowRequest {
583 pub document: TrustedNodeAddress,
585 pub epoch: Epoch,
587 pub restyle: Option<ReflowRequestRestyle>,
589 pub viewport_details: ViewportDetails,
591 pub reflow_goal: ReflowGoal,
593 pub dom_count: u32,
595 pub origin: ImmutableOrigin,
597 pub animation_timeline_value: f64,
599 pub animations: DocumentAnimationSet,
601 pub animating_images: Arc<RwLock<AnimatingImages>>,
603 pub highlighted_dom_node: Option<OpaqueNode>,
605 pub document_context: WebFontDocumentContext,
607}
608
609impl ReflowRequest {
610 pub fn stylesheets_changed(&self) -> bool {
611 self.restyle
612 .as_ref()
613 .is_some_and(|restyle| restyle.stylesheets_changed)
614 }
615}
616
617#[derive(Debug, Default, MallocSizeOf)]
619pub struct PendingRestyle {
620 pub snapshot: Option<Snapshot>,
623
624 pub hint: RestyleHint,
626
627 pub damage: RestyleDamage,
629}
630
631#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
637pub enum FragmentType {
638 FragmentBody,
640 BeforePseudoContent,
642 AfterPseudoContent,
644}
645
646impl From<Option<PseudoElement>> for FragmentType {
647 fn from(value: Option<PseudoElement>) -> Self {
648 match value {
649 Some(PseudoElement::After) => FragmentType::AfterPseudoContent,
650 Some(PseudoElement::Before) => FragmentType::BeforePseudoContent,
651 _ => FragmentType::FragmentBody,
652 }
653 }
654}
655
656static NEXT_SPECIAL_SCROLL_ROOT_ID: AtomicU64 = AtomicU64::new(0);
660
661const SPECIAL_SCROLL_ROOT_ID_MASK: u64 = 0xffff;
664
665fn next_special_id() -> u64 {
667 ((NEXT_SPECIAL_SCROLL_ROOT_ID.fetch_add(1, Ordering::SeqCst) + 1) << 2) &
669 SPECIAL_SCROLL_ROOT_ID_MASK
670}
671
672pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> u64 {
673 debug_assert_eq!(id & (fragment_type as usize), 0);
674 if fragment_type == FragmentType::FragmentBody {
675 id as u64
676 } else {
677 next_special_id() | (fragment_type as u64)
678 }
679}
680
681pub fn node_id_from_scroll_id(id: usize) -> Option<usize> {
682 if (id as u64 & !SPECIAL_SCROLL_ROOT_ID_MASK) != 0 {
683 return Some(id & !3);
684 }
685 None
686}
687
688#[derive(Clone, Debug, MallocSizeOf)]
689pub struct ImageAnimationState {
690 #[ignore_malloc_size_of = "RasterImage"]
691 pub image: Arc<RasterImage>,
692 pub active_frame: usize,
693 frame_start_time: f64,
694}
695
696impl ImageAnimationState {
697 pub fn new(image: Arc<RasterImage>, last_update_time: f64) -> Self {
698 Self {
699 image,
700 active_frame: 0,
701 frame_start_time: last_update_time,
702 }
703 }
704
705 pub fn image_key(&self) -> Option<ImageKey> {
706 self.image.id
707 }
708
709 pub fn duration_to_next_frame(&self, now: f64) -> Duration {
710 let frame_delay = self
711 .image
712 .frames
713 .get(self.active_frame)
714 .expect("Image frame should always be valid")
715 .delay
716 .unwrap_or_default();
717
718 let time_since_frame_start = (now - self.frame_start_time).max(0.0) * 1000.0;
719 let time_since_frame_start = Duration::from_secs_f64(time_since_frame_start);
720 frame_delay - time_since_frame_start.min(frame_delay)
721 }
722
723 pub fn update_frame_for_animation_timeline_value(&mut self, now: f64) -> bool {
727 if self.image.frames.len() <= 1 {
728 return false;
729 }
730 let image = &self.image;
731 let time_interval_since_last_update = now - self.frame_start_time;
732 let mut remain_time_interval = time_interval_since_last_update -
733 image
734 .frames
735 .get(self.active_frame)
736 .unwrap()
737 .delay()
738 .unwrap()
739 .as_secs_f64();
740 let mut next_active_frame_id = self.active_frame;
741 while remain_time_interval > 0.0 {
742 next_active_frame_id = (next_active_frame_id + 1) % image.frames.len();
743 remain_time_interval -= image
744 .frames
745 .get(next_active_frame_id)
746 .unwrap()
747 .delay()
748 .unwrap()
749 .as_secs_f64();
750 }
751 if self.active_frame == next_active_frame_id {
752 return false;
753 }
754 self.active_frame = next_active_frame_id;
755 self.frame_start_time = now;
756 true
757 }
758}
759
760#[derive(Debug)]
762pub struct ElementsFromPointResult {
763 pub node: OpaqueNode,
766 pub point_in_target: Point2D<f32, CSSPixel>,
769 pub cursor: Cursor,
772}
773
774bitflags! {
775 pub struct ElementsFromPointFlags: u8 {
776 const FindAll = 0b00000001;
779 }
780}
781
782#[derive(Debug, Default, MallocSizeOf)]
783pub struct AnimatingImages {
784 pub node_to_state_map: FxHashMap<OpaqueNode, ImageAnimationState>,
787 pub dirty: bool,
790}
791
792impl AnimatingImages {
793 pub fn maybe_insert_or_update(
794 &mut self,
795 node: OpaqueNode,
796 image: Arc<RasterImage>,
797 current_timeline_value: f64,
798 ) {
799 let entry = self.node_to_state_map.entry(node).or_insert_with(|| {
800 self.dirty = true;
801 ImageAnimationState::new(image.clone(), current_timeline_value)
802 });
803
804 if entry.image.id != image.id {
807 self.dirty = true;
808 *entry = ImageAnimationState::new(image.clone(), current_timeline_value);
809 }
810 }
811
812 pub fn remove(&mut self, node: OpaqueNode) {
813 if self.node_to_state_map.remove(&node).is_some() {
814 self.dirty = true;
815 }
816 }
817
818 pub fn clear_dirty(&mut self) -> bool {
820 std::mem::take(&mut self.dirty)
821 }
822
823 pub fn is_empty(&self) -> bool {
824 self.node_to_state_map.is_empty()
825 }
826}
827
828#[cfg(test)]
829mod test {
830 use std::sync::Arc;
831 use std::time::Duration;
832
833 use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
834
835 use crate::ImageAnimationState;
836
837 #[test]
838 fn test() {
839 let image_frames: Vec<ImageFrame> = std::iter::repeat_with(|| ImageFrame {
840 delay: Some(Duration::from_millis(100)),
841 byte_range: 0..1,
842 width: 100,
843 height: 100,
844 })
845 .take(10)
846 .collect();
847 let image = RasterImage {
848 metadata: ImageMetadata {
849 width: 100,
850 height: 100,
851 },
852 format: PixelFormat::BGRA8,
853 id: None,
854 bytes: Arc::new(vec![1]),
855 frames: image_frames,
856 cors_status: CorsStatus::Unsafe,
857 is_opaque: false,
858 };
859 let mut image_animation_state = ImageAnimationState::new(Arc::new(image), 0.0);
860
861 assert_eq!(image_animation_state.active_frame, 0);
862 assert_eq!(image_animation_state.frame_start_time, 0.0);
863 assert_eq!(
864 image_animation_state.update_frame_for_animation_timeline_value(0.101),
865 true
866 );
867 assert_eq!(image_animation_state.active_frame, 1);
868 assert_eq!(image_animation_state.frame_start_time, 0.101);
869 assert_eq!(
870 image_animation_state.update_frame_for_animation_timeline_value(0.116),
871 false
872 );
873 assert_eq!(image_animation_state.active_frame, 1);
874 assert_eq!(image_animation_state.frame_start_time, 0.101);
875 }
876}