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 base::Epoch;
23use base::generic_channel::GenericSender;
24use base::id::{BrowsingContextId, PipelineId, WebViewId};
25use bitflags::bitflags;
26use compositing_traits::CrossProcessCompositorApi;
27use constellation_traits::LoadData;
28use embedder_traits::{Cursor, Theme, UntrustedNodeAddress, ViewportDetails};
29use euclid::Point2D;
30use euclid::default::{Point2D as UntypedPoint2D, Rect};
31use fonts::{FontContext, SystemFontServiceProxy};
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, 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 source: 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#[allow(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 load_web_fonts_from_stylesheet(&self, stylesheet: &ServoArc<Stylesheet>);
247
248 fn add_stylesheet(
252 &mut self,
253 stylesheet: ServoArc<Stylesheet>,
254 before_stylsheet: Option<ServoArc<Stylesheet>>,
255 );
256
257 fn exit_now(&mut self);
259
260 fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps);
263
264 fn set_quirks_mode(&mut self, quirks_mode: QuirksMode);
266
267 fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>);
269
270 fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult>;
272
273 fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails);
276
277 fn register_paint_worklet_modules(
279 &mut self,
280 name: Atom,
281 properties: Vec<Atom>,
282 painter: Box<dyn Painter>,
283 );
284
285 fn set_scroll_offsets_from_renderer(
287 &mut self,
288 scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
289 );
290
291 fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D>;
294
295 fn needs_new_display_list(&self) -> bool;
297
298 fn set_needs_new_display_list(&self);
300
301 fn query_padding(&self, node: TrustedNodeAddress) -> Option<PhysicalSides>;
302 fn query_box_area(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Option<Rect<Au>>;
303 fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Vec<Rect<Au>>;
304 fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32>;
305 fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String;
306 fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse;
307 fn query_scroll_container(
310 &self,
311 node: Option<TrustedNodeAddress>,
312 flags: ScrollContainerQueryFlags,
313 ) -> Option<ScrollContainerResponse>;
314 fn query_resolved_style(
315 &self,
316 node: TrustedNodeAddress,
317 pseudo: Option<PseudoElement>,
318 property_id: PropertyId,
319 animations: DocumentAnimationSet,
320 animation_timeline_value: f64,
321 ) -> String;
322 fn query_resolved_font_style(
323 &self,
324 node: TrustedNodeAddress,
325 value: &str,
326 animations: DocumentAnimationSet,
327 animation_timeline_value: f64,
328 ) -> Option<ServoArc<Font>>;
329 fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32>;
330 fn query_text_indext(&self, node: OpaqueNode, point: UntypedPoint2D<f32>) -> Option<usize>;
331 fn query_elements_from_point(
332 &self,
333 point: LayoutPoint,
334 flags: ElementsFromPointFlags,
335 ) -> Vec<ElementsFromPointResult>;
336 fn register_custom_property(
337 &mut self,
338 property_registration: PropertyRegistration,
339 ) -> Result<(), RegisterPropertyError>;
340}
341
342pub trait ScriptThreadFactory {
346 fn create(
348 state: InitialScriptState,
349 layout_factory: Arc<dyn LayoutFactory>,
350 system_font_service: Arc<SystemFontServiceProxy>,
351 load_data: LoadData,
352 ) -> JoinHandle<()>;
353}
354
355#[derive(Copy, Clone)]
358pub enum BoxAreaType {
359 Content,
360 Padding,
361 Border,
362}
363
364#[derive(Default)]
365pub struct PhysicalSides {
366 pub left: Au,
367 pub top: Au,
368 pub right: Au,
369 pub bottom: Au,
370}
371
372#[derive(Clone, Default)]
373pub struct OffsetParentResponse {
374 pub node_address: Option<UntrustedNodeAddress>,
375 pub rect: Rect<Au>,
376}
377
378bitflags! {
379 #[derive(PartialEq)]
380 pub struct ScrollContainerQueryFlags: u8 {
381 const ForScrollParent = 1 << 0;
383 const Inclusive = 1 << 1;
385 }
386}
387
388#[derive(Clone, Copy, Debug, MallocSizeOf)]
389pub struct AxesOverflow {
390 pub x: Overflow,
391 pub y: Overflow,
392}
393
394impl Default for AxesOverflow {
395 fn default() -> Self {
396 Self {
397 x: Overflow::Visible,
398 y: Overflow::Visible,
399 }
400 }
401}
402
403impl From<&ComputedValues> for AxesOverflow {
404 fn from(style: &ComputedValues) -> Self {
405 Self {
406 x: style.clone_overflow_x(),
407 y: style.clone_overflow_y(),
408 }
409 }
410}
411
412impl AxesOverflow {
413 pub fn to_scrollable(&self) -> Self {
414 Self {
415 x: self.x.to_scrollable(),
416 y: self.y.to_scrollable(),
417 }
418 }
419}
420
421#[derive(Clone)]
422pub enum ScrollContainerResponse {
423 Viewport(AxesOverflow),
424 Element(UntrustedNodeAddress, AxesOverflow),
425}
426
427#[derive(Debug, PartialEq)]
428pub enum QueryMsg {
429 BoxArea,
430 BoxAreas,
431 ClientRectQuery,
432 ElementInnerOuterTextQuery,
433 ElementsFromPoint,
434 InnerWindowDimensionsQuery,
435 NodesFromPointQuery,
436 OffsetParentQuery,
437 ScrollParentQuery,
438 ResolvedFontStyleQuery,
439 ResolvedStyleQuery,
440 ScrollingAreaOrOffsetQuery,
441 StyleQuery,
442 TextIndexQuery,
443 PaddingQuery,
444}
445
446#[derive(Debug, PartialEq)]
452pub enum ReflowGoal {
453 UpdateTheRendering,
456
457 LayoutQuery(QueryMsg),
460
461 UpdateScrollNode(ExternalScrollId, LayoutVector2D),
465}
466
467#[derive(Clone, Debug, MallocSizeOf)]
468pub struct IFrameSize {
469 pub browsing_context_id: BrowsingContextId,
470 pub pipeline_id: PipelineId,
471 pub viewport_details: ViewportDetails,
472}
473
474pub type IFrameSizes = FxHashMap<BrowsingContextId, IFrameSize>;
475
476bitflags! {
477 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
480 pub struct RestyleReason: u16 {
481 const StylesheetsChanged = 1 << 0;
482 const DOMChanged = 1 << 1;
483 const PendingRestyles = 1 << 2;
484 const HighlightedDOMNodeChanged = 1 << 3;
485 const ThemeChanged = 1 << 4;
486 const ViewportSizeChanged = 1 << 5;
487 const PaintWorkletLoaded = 1 << 6;
488 }
489}
490
491malloc_size_of_is_0!(RestyleReason);
492
493impl RestyleReason {
494 pub fn needs_restyle(&self) -> bool {
495 !self.is_empty()
496 }
497}
498
499#[derive(Debug, Default)]
501pub struct ReflowResult {
502 pub reflow_phases_run: ReflowPhasesRun,
504 pub pending_images: Vec<PendingImage>,
506 pub pending_rasterization_images: Vec<PendingRasterizationImage>,
508 pub pending_svg_elements_for_serialization: Vec<UntrustedNodeAddress>,
512 pub iframe_sizes: Option<IFrameSizes>,
518}
519
520bitflags! {
521 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
523 pub struct ReflowPhasesRun: u8 {
524 const RanLayout = 1 << 0;
525 const CalculatedOverflow = 1 << 1;
526 const BuiltStackingContextTree = 1 << 2;
527 const BuiltDisplayList = 1 << 3;
528 const UpdatedScrollNodeOffset = 1 << 4;
529 const UpdatedImageData = 1 << 5;
533 }
534}
535
536impl ReflowPhasesRun {
537 pub fn needs_frame(&self) -> bool {
538 self.intersects(
539 Self::BuiltDisplayList | Self::UpdatedScrollNodeOffset | Self::UpdatedImageData,
540 )
541 }
542}
543
544#[derive(Debug)]
547pub struct ReflowRequestRestyle {
548 pub reason: RestyleReason,
550 pub dirty_root: Option<TrustedNodeAddress>,
552 pub stylesheets_changed: bool,
554 pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
556}
557
558#[derive(Debug)]
560pub struct ReflowRequest {
561 pub document: TrustedNodeAddress,
563 pub epoch: Epoch,
565 pub restyle: Option<ReflowRequestRestyle>,
567 pub viewport_details: ViewportDetails,
569 pub reflow_goal: ReflowGoal,
571 pub dom_count: u32,
573 pub origin: ImmutableOrigin,
575 pub animation_timeline_value: f64,
577 pub animations: DocumentAnimationSet,
579 pub animating_images: Arc<RwLock<AnimatingImages>>,
581 pub theme: Theme,
583 pub highlighted_dom_node: Option<OpaqueNode>,
585}
586
587impl ReflowRequest {
588 pub fn stylesheets_changed(&self) -> bool {
589 self.restyle
590 .as_ref()
591 .is_some_and(|restyle| restyle.stylesheets_changed)
592 }
593}
594
595#[derive(Debug, Default, MallocSizeOf)]
597pub struct PendingRestyle {
598 pub snapshot: Option<Snapshot>,
601
602 pub hint: RestyleHint,
604
605 pub damage: RestyleDamage,
607}
608
609#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
615pub enum FragmentType {
616 FragmentBody,
618 BeforePseudoContent,
620 AfterPseudoContent,
622}
623
624impl From<Option<PseudoElement>> for FragmentType {
625 fn from(value: Option<PseudoElement>) -> Self {
626 match value {
627 Some(PseudoElement::After) => FragmentType::AfterPseudoContent,
628 Some(PseudoElement::Before) => FragmentType::BeforePseudoContent,
629 _ => FragmentType::FragmentBody,
630 }
631 }
632}
633
634static NEXT_SPECIAL_SCROLL_ROOT_ID: AtomicU64 = AtomicU64::new(0);
638
639const SPECIAL_SCROLL_ROOT_ID_MASK: u64 = 0xffff;
642
643fn next_special_id() -> u64 {
645 ((NEXT_SPECIAL_SCROLL_ROOT_ID.fetch_add(1, Ordering::SeqCst) + 1) << 2) &
647 SPECIAL_SCROLL_ROOT_ID_MASK
648}
649
650pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> u64 {
651 debug_assert_eq!(id & (fragment_type as usize), 0);
652 if fragment_type == FragmentType::FragmentBody {
653 id as u64
654 } else {
655 next_special_id() | (fragment_type as u64)
656 }
657}
658
659pub fn node_id_from_scroll_id(id: usize) -> Option<usize> {
660 if (id as u64 & !SPECIAL_SCROLL_ROOT_ID_MASK) != 0 {
661 return Some(id & !3);
662 }
663 None
664}
665
666#[derive(Clone, Debug, MallocSizeOf)]
667pub struct ImageAnimationState {
668 #[ignore_malloc_size_of = "RasterImage"]
669 pub image: Arc<RasterImage>,
670 pub active_frame: usize,
671 frame_start_time: f64,
672}
673
674impl ImageAnimationState {
675 pub fn new(image: Arc<RasterImage>, last_update_time: f64) -> Self {
676 Self {
677 image,
678 active_frame: 0,
679 frame_start_time: last_update_time,
680 }
681 }
682
683 pub fn image_key(&self) -> Option<ImageKey> {
684 self.image.id
685 }
686
687 pub fn duration_to_next_frame(&self, now: f64) -> Duration {
688 let frame_delay = self
689 .image
690 .frames
691 .get(self.active_frame)
692 .expect("Image frame should always be valid")
693 .delay
694 .unwrap_or_default();
695
696 let time_since_frame_start = (now - self.frame_start_time).max(0.0) * 1000.0;
697 let time_since_frame_start = Duration::from_secs_f64(time_since_frame_start);
698 frame_delay - time_since_frame_start.min(frame_delay)
699 }
700
701 pub fn update_frame_for_animation_timeline_value(&mut self, now: f64) -> bool {
705 if self.image.frames.len() <= 1 {
706 return false;
707 }
708 let image = &self.image;
709 let time_interval_since_last_update = now - self.frame_start_time;
710 let mut remain_time_interval = time_interval_since_last_update -
711 image
712 .frames
713 .get(self.active_frame)
714 .unwrap()
715 .delay()
716 .unwrap()
717 .as_secs_f64();
718 let mut next_active_frame_id = self.active_frame;
719 while remain_time_interval > 0.0 {
720 next_active_frame_id = (next_active_frame_id + 1) % image.frames.len();
721 remain_time_interval -= image
722 .frames
723 .get(next_active_frame_id)
724 .unwrap()
725 .delay()
726 .unwrap()
727 .as_secs_f64();
728 }
729 if self.active_frame == next_active_frame_id {
730 return false;
731 }
732 self.active_frame = next_active_frame_id;
733 self.frame_start_time = now;
734 true
735 }
736}
737
738#[derive(Debug)]
740pub struct ElementsFromPointResult {
741 pub node: OpaqueNode,
744 pub point_in_target: Point2D<f32, CSSPixel>,
747 pub cursor: Cursor,
750}
751
752bitflags! {
753 pub struct ElementsFromPointFlags: u8 {
754 const FindAll = 0b00000001;
757 }
758}
759
760#[derive(Debug, Default, MallocSizeOf)]
761pub struct AnimatingImages {
762 pub node_to_state_map: FxHashMap<OpaqueNode, ImageAnimationState>,
765 pub dirty: bool,
768}
769
770impl AnimatingImages {
771 pub fn maybe_insert_or_update(
772 &mut self,
773 node: OpaqueNode,
774 image: Arc<RasterImage>,
775 current_timeline_value: f64,
776 ) {
777 let entry = self.node_to_state_map.entry(node).or_insert_with(|| {
778 self.dirty = true;
779 ImageAnimationState::new(image.clone(), current_timeline_value)
780 });
781
782 if entry.image.id != image.id {
785 self.dirty = true;
786 *entry = ImageAnimationState::new(image.clone(), current_timeline_value);
787 }
788 }
789
790 pub fn remove(&mut self, node: OpaqueNode) {
791 if self.node_to_state_map.remove(&node).is_some() {
792 self.dirty = true;
793 }
794 }
795
796 pub fn clear_dirty(&mut self) -> bool {
798 std::mem::take(&mut self.dirty)
799 }
800
801 pub fn is_empty(&self) -> bool {
802 self.node_to_state_map.is_empty()
803 }
804}
805
806#[cfg(test)]
807mod test {
808 use std::sync::Arc;
809 use std::time::Duration;
810
811 use ipc_channel::ipc::IpcSharedMemory;
812 use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
813
814 use crate::ImageAnimationState;
815
816 #[test]
817 fn test() {
818 let image_frames: Vec<ImageFrame> = std::iter::repeat_with(|| ImageFrame {
819 delay: Some(Duration::from_millis(100)),
820 byte_range: 0..1,
821 width: 100,
822 height: 100,
823 })
824 .take(10)
825 .collect();
826 let image = RasterImage {
827 metadata: ImageMetadata {
828 width: 100,
829 height: 100,
830 },
831 format: PixelFormat::BGRA8,
832 id: None,
833 bytes: Arc::new(IpcSharedMemory::from_byte(1, 1)),
834 frames: image_frames,
835 cors_status: CorsStatus::Unsafe,
836 is_opaque: false,
837 };
838 let mut image_animation_state = ImageAnimationState::new(Arc::new(image), 0.0);
839
840 assert_eq!(image_animation_state.active_frame, 0);
841 assert_eq!(image_animation_state.frame_start_time, 0.0);
842 assert_eq!(
843 image_animation_state.update_frame_for_animation_timeline_value(0.101),
844 true
845 );
846 assert_eq!(image_animation_state.active_frame, 1);
847 assert_eq!(image_animation_state.frame_start_time, 0.101);
848 assert_eq!(
849 image_animation_state.update_frame_for_animation_timeline_value(0.116),
850 false
851 );
852 assert_eq!(image_animation_state.active_frame, 1);
853 assert_eq!(image_animation_state.frame_start_time, 0.101);
854 }
855}