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::PropertyId;
54use style::properties::style_structs::Font;
55use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot};
56use style::stylesheets::{Stylesheet, UrlExtraData};
57use style_traits::CSSPixel;
58use webrender_api::units::{DeviceIntSize, LayoutPoint, LayoutVector2D};
59use webrender_api::{ExternalScrollId, ImageKey};
60
61pub trait GenericLayoutDataTrait: Any + MallocSizeOfTrait {
62 fn as_any(&self) -> &dyn Any;
63}
64
65pub type GenericLayoutData = dyn GenericLayoutDataTrait + Send + Sync;
66
67#[derive(MallocSizeOf)]
68pub struct StyleData {
69 pub element_data: AtomicRefCell<ElementData>,
74
75 pub parallel: DomParallelInfo,
77}
78
79impl Default for StyleData {
80 fn default() -> Self {
81 Self {
82 element_data: AtomicRefCell::new(ElementData::default()),
83 parallel: DomParallelInfo::default(),
84 }
85 }
86}
87
88#[derive(Default, MallocSizeOf)]
90pub struct DomParallelInfo {
91 pub children_to_process: AtomicIsize,
93}
94
95#[derive(Clone, Copy, Debug, Eq, PartialEq)]
96pub enum LayoutNodeType {
97 Element(LayoutElementType),
98 Text,
99}
100
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub enum LayoutElementType {
103 Element,
104 HTMLBodyElement,
105 HTMLBRElement,
106 HTMLCanvasElement,
107 HTMLHtmlElement,
108 HTMLIFrameElement,
109 HTMLImageElement,
110 HTMLInputElement,
111 HTMLMediaElement,
112 HTMLObjectElement,
113 HTMLOptGroupElement,
114 HTMLOptionElement,
115 HTMLParagraphElement,
116 HTMLPreElement,
117 HTMLSelectElement,
118 HTMLTableCellElement,
119 HTMLTableColElement,
120 HTMLTableElement,
121 HTMLTableRowElement,
122 HTMLTableSectionElement,
123 HTMLTextAreaElement,
124 SVGImageElement,
125 SVGSVGElement,
126}
127
128pub struct HTMLCanvasData {
129 pub source: Option<ImageKey>,
130 pub width: u32,
131 pub height: u32,
132}
133
134pub struct SVGElementData {
135 pub source: Option<Result<ServoUrl, ()>>,
137 pub width: Option<i32>,
138 pub height: Option<i32>,
139 pub ratio: Option<f32>,
140}
141
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
144pub struct TrustedNodeAddress(pub *const c_void);
145
146#[allow(unsafe_code)]
147unsafe impl Send for TrustedNodeAddress {}
148
149#[derive(Debug)]
151pub enum PendingImageState {
152 Unrequested(ServoUrl),
153 PendingResponse,
154}
155
156#[derive(Debug, MallocSizeOf)]
158pub enum LayoutImageDestination {
159 BoxTreeConstruction,
160 DisplayListBuilding,
161}
162
163#[derive(Debug)]
167pub struct PendingImage {
168 pub state: PendingImageState,
169 pub node: UntrustedNodeAddress,
170 pub id: PendingImageId,
171 pub origin: ImmutableOrigin,
172 pub destination: LayoutImageDestination,
173}
174
175#[derive(Debug)]
179pub struct PendingRasterizationImage {
180 pub node: UntrustedNodeAddress,
181 pub id: PendingImageId,
182 pub size: DeviceIntSize,
183}
184
185#[derive(Clone, Copy, Debug, MallocSizeOf)]
186pub struct MediaFrame {
187 pub image_key: webrender_api::ImageKey,
188 pub width: i32,
189 pub height: i32,
190}
191
192pub struct MediaMetadata {
193 pub width: u32,
194 pub height: u32,
195}
196
197pub struct HTMLMediaData {
198 pub current_frame: Option<MediaFrame>,
199 pub metadata: Option<MediaMetadata>,
200}
201
202pub struct LayoutConfig {
203 pub id: PipelineId,
204 pub webview_id: WebViewId,
205 pub url: ServoUrl,
206 pub is_iframe: bool,
207 pub script_chan: GenericSender<ScriptThreadMessage>,
208 pub image_cache: Arc<dyn ImageCache>,
209 pub font_context: Arc<FontContext>,
210 pub time_profiler_chan: time::ProfilerChan,
211 pub compositor_api: CrossProcessCompositorApi,
212 pub viewport_details: ViewportDetails,
213 pub theme: Theme,
214}
215
216pub struct PropertyRegistration {
217 pub name: String,
218 pub syntax: String,
219 pub initial_value: Option<String>,
220 pub inherits: bool,
221 pub url_data: UrlExtraData,
222}
223
224#[derive(Debug)]
225pub enum RegisterPropertyError {
226 InvalidName,
227 AlreadyRegistered,
228 InvalidSyntax,
229 InvalidInitialValue,
230 InitialValueNotComputationallyIndependent,
231 NoInitialValue,
232}
233
234pub trait LayoutFactory: Send + Sync {
235 fn create(&self, config: LayoutConfig) -> Box<dyn Layout>;
236}
237
238pub trait Layout {
239 fn device(&self) -> &Device;
242
243 fn current_epoch(&self) -> Epoch;
245
246 fn load_web_fonts_from_stylesheet(&self, stylesheet: ServoArc<Stylesheet>);
249
250 fn add_stylesheet(
254 &mut self,
255 stylesheet: ServoArc<Stylesheet>,
256 before_stylsheet: Option<ServoArc<Stylesheet>>,
257 );
258
259 fn exit_now(&mut self);
261
262 fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps);
265
266 fn set_quirks_mode(&mut self, quirks_mode: QuirksMode);
268
269 fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>);
271
272 fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult>;
274
275 fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails);
278
279 fn register_paint_worklet_modules(
281 &mut self,
282 name: Atom,
283 properties: Vec<Atom>,
284 painter: Box<dyn Painter>,
285 );
286
287 fn set_scroll_offsets_from_renderer(
289 &mut self,
290 scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
291 );
292
293 fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D>;
296
297 fn needs_new_display_list(&self) -> bool;
299
300 fn set_needs_new_display_list(&self);
302
303 fn query_box_area(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Option<Rect<Au>>;
304 fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> Vec<Rect<Au>>;
305 fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32>;
306 fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String;
307 fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse;
308 fn query_scroll_container(
309 &self,
310 node: TrustedNodeAddress,
311 query_type: ScrollContainerQueryType,
312 ) -> Option<ScrollContainerResponse>;
313 fn query_resolved_style(
314 &self,
315 node: TrustedNodeAddress,
316 pseudo: Option<PseudoElement>,
317 property_id: PropertyId,
318 animations: DocumentAnimationSet,
319 animation_timeline_value: f64,
320 ) -> String;
321 fn query_resolved_font_style(
322 &self,
323 node: TrustedNodeAddress,
324 value: &str,
325 animations: DocumentAnimationSet,
326 animation_timeline_value: f64,
327 ) -> Option<ServoArc<Font>>;
328 fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32>;
329 fn query_text_indext(&self, node: OpaqueNode, point: UntypedPoint2D<f32>) -> Option<usize>;
330 fn query_elements_from_point(
331 &self,
332 point: LayoutPoint,
333 flags: ElementsFromPointFlags,
334 ) -> Vec<ElementsFromPointResult>;
335 fn register_custom_property(
336 &mut self,
337 property_registration: PropertyRegistration,
338 ) -> Result<(), RegisterPropertyError>;
339}
340
341pub trait ScriptThreadFactory {
345 fn create(
347 state: InitialScriptState,
348 layout_factory: Arc<dyn LayoutFactory>,
349 system_font_service: Arc<SystemFontServiceProxy>,
350 load_data: LoadData,
351 ) -> JoinHandle<()>;
352}
353
354#[derive(Copy, Clone)]
357pub enum BoxAreaType {
358 Content,
359 Padding,
360 Border,
361}
362
363#[derive(Clone, Default)]
364pub struct OffsetParentResponse {
365 pub node_address: Option<UntrustedNodeAddress>,
366 pub rect: Rect<Au>,
367}
368
369#[derive(PartialEq)]
370pub enum ScrollContainerQueryType {
371 ForScrollParent,
372 ForScrollIntoView,
373}
374
375#[derive(Clone)]
376pub enum ScrollContainerResponse {
377 Viewport,
378 Element(UntrustedNodeAddress),
379}
380
381#[derive(Debug, PartialEq)]
382pub enum QueryMsg {
383 BoxArea,
384 BoxAreas,
385 ClientRectQuery,
386 ElementInnerOuterTextQuery,
387 ElementsFromPoint,
388 InnerWindowDimensionsQuery,
389 NodesFromPointQuery,
390 OffsetParentQuery,
391 ScrollParentQuery,
392 ResolvedFontStyleQuery,
393 ResolvedStyleQuery,
394 ScrollingAreaOrOffsetQuery,
395 StyleQuery,
396 TextIndexQuery,
397}
398
399#[derive(Debug, PartialEq)]
405pub enum ReflowGoal {
406 UpdateTheRendering,
409
410 LayoutQuery(QueryMsg),
413
414 UpdateScrollNode(ExternalScrollId, LayoutVector2D),
418}
419
420#[derive(Clone, Debug, MallocSizeOf)]
421pub struct IFrameSize {
422 pub browsing_context_id: BrowsingContextId,
423 pub pipeline_id: PipelineId,
424 pub viewport_details: ViewportDetails,
425}
426
427pub type IFrameSizes = FxHashMap<BrowsingContextId, IFrameSize>;
428
429bitflags! {
430 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
433 pub struct RestyleReason: u16 {
434 const StylesheetsChanged = 1 << 0;
435 const DOMChanged = 1 << 1;
436 const PendingRestyles = 1 << 2;
437 const HighlightedDOMNodeChanged = 1 << 3;
438 const ThemeChanged = 1 << 4;
439 const ViewportSizeChanged = 1 << 5;
440 const PaintWorkletLoaded = 1 << 6;
441 }
442}
443
444malloc_size_of_is_0!(RestyleReason);
445
446impl RestyleReason {
447 pub fn needs_restyle(&self) -> bool {
448 !self.is_empty()
449 }
450}
451
452#[derive(Debug, Default)]
454pub struct ReflowResult {
455 pub reflow_phases_run: ReflowPhasesRun,
457 pub pending_images: Vec<PendingImage>,
459 pub pending_rasterization_images: Vec<PendingRasterizationImage>,
461 pub pending_svg_elements_for_serialization: Vec<UntrustedNodeAddress>,
465 pub iframe_sizes: Option<IFrameSizes>,
471}
472
473bitflags! {
474 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
476 pub struct ReflowPhasesRun: u8 {
477 const RanLayout = 1 << 0;
478 const CalculatedOverflow = 1 << 1;
479 const BuiltStackingContextTree = 1 << 2;
480 const BuiltDisplayList = 1 << 3;
481 const UpdatedScrollNodeOffset = 1 << 4;
482 const UpdatedCanvasContents = 1 << 5;
483 }
484}
485
486impl ReflowPhasesRun {
487 pub fn needs_frame(&self) -> bool {
488 self.intersects(
489 Self::BuiltDisplayList | Self::UpdatedScrollNodeOffset | Self::UpdatedCanvasContents,
490 )
491 }
492}
493
494#[derive(Debug)]
497pub struct ReflowRequestRestyle {
498 pub reason: RestyleReason,
500 pub dirty_root: Option<TrustedNodeAddress>,
502 pub stylesheets_changed: bool,
504 pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
506}
507
508#[derive(Debug)]
510pub struct ReflowRequest {
511 pub document: TrustedNodeAddress,
513 pub restyle: Option<ReflowRequestRestyle>,
515 pub viewport_details: ViewportDetails,
517 pub reflow_goal: ReflowGoal,
519 pub dom_count: u32,
521 pub origin: ImmutableOrigin,
523 pub animation_timeline_value: f64,
525 pub animations: DocumentAnimationSet,
527 pub node_to_animating_image_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
529 pub theme: Theme,
531 pub highlighted_dom_node: Option<OpaqueNode>,
533}
534
535impl ReflowRequest {
536 pub fn stylesheets_changed(&self) -> bool {
537 self.restyle
538 .as_ref()
539 .is_some_and(|restyle| restyle.stylesheets_changed)
540 }
541}
542
543#[derive(Debug, Default, MallocSizeOf)]
545pub struct PendingRestyle {
546 pub snapshot: Option<Snapshot>,
549
550 pub hint: RestyleHint,
552
553 pub damage: RestyleDamage,
555}
556
557#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
563pub enum FragmentType {
564 FragmentBody,
566 BeforePseudoContent,
568 AfterPseudoContent,
570}
571
572impl From<Option<PseudoElement>> for FragmentType {
573 fn from(value: Option<PseudoElement>) -> Self {
574 match value {
575 Some(PseudoElement::After) => FragmentType::AfterPseudoContent,
576 Some(PseudoElement::Before) => FragmentType::BeforePseudoContent,
577 _ => FragmentType::FragmentBody,
578 }
579 }
580}
581
582static NEXT_SPECIAL_SCROLL_ROOT_ID: AtomicU64 = AtomicU64::new(0);
586
587const SPECIAL_SCROLL_ROOT_ID_MASK: u64 = 0xffff;
590
591fn next_special_id() -> u64 {
593 ((NEXT_SPECIAL_SCROLL_ROOT_ID.fetch_add(1, Ordering::SeqCst) + 1) << 2) &
595 SPECIAL_SCROLL_ROOT_ID_MASK
596}
597
598pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> u64 {
599 debug_assert_eq!(id & (fragment_type as usize), 0);
600 if fragment_type == FragmentType::FragmentBody {
601 id as u64
602 } else {
603 next_special_id() | (fragment_type as u64)
604 }
605}
606
607pub fn node_id_from_scroll_id(id: usize) -> Option<usize> {
608 if (id as u64 & !SPECIAL_SCROLL_ROOT_ID_MASK) != 0 {
609 return Some(id & !3);
610 }
611 None
612}
613
614#[derive(Clone, Debug, MallocSizeOf)]
615pub struct ImageAnimationState {
616 #[ignore_malloc_size_of = "Arc is hard"]
617 pub image: Arc<RasterImage>,
618 pub active_frame: usize,
619 frame_start_time: f64,
620}
621
622impl ImageAnimationState {
623 pub fn new(image: Arc<RasterImage>, last_update_time: f64) -> Self {
624 Self {
625 image,
626 active_frame: 0,
627 frame_start_time: last_update_time,
628 }
629 }
630
631 pub fn image_key(&self) -> Option<ImageKey> {
632 self.image.id
633 }
634
635 pub fn duration_to_next_frame(&self, now: f64) -> Duration {
636 let frame_delay = self
637 .image
638 .frames
639 .get(self.active_frame)
640 .expect("Image frame should always be valid")
641 .delay
642 .unwrap_or_default();
643
644 let time_since_frame_start = (now - self.frame_start_time).max(0.0) * 1000.0;
645 let time_since_frame_start = Duration::from_secs_f64(time_since_frame_start);
646 frame_delay - time_since_frame_start.min(frame_delay)
647 }
648
649 pub fn update_frame_for_animation_timeline_value(&mut self, now: f64) -> bool {
653 if self.image.frames.len() <= 1 {
654 return false;
655 }
656 let image = &self.image;
657 let time_interval_since_last_update = now - self.frame_start_time;
658 let mut remain_time_interval = time_interval_since_last_update -
659 image
660 .frames
661 .get(self.active_frame)
662 .unwrap()
663 .delay()
664 .unwrap()
665 .as_secs_f64();
666 let mut next_active_frame_id = self.active_frame;
667 while remain_time_interval > 0.0 {
668 next_active_frame_id = (next_active_frame_id + 1) % image.frames.len();
669 remain_time_interval -= image
670 .frames
671 .get(next_active_frame_id)
672 .unwrap()
673 .delay()
674 .unwrap()
675 .as_secs_f64();
676 }
677 if self.active_frame == next_active_frame_id {
678 return false;
679 }
680 self.active_frame = next_active_frame_id;
681 self.frame_start_time = now;
682 true
683 }
684}
685
686#[derive(Debug)]
688pub struct ElementsFromPointResult {
689 pub node: OpaqueNode,
692 pub point_in_target: Point2D<f32, CSSPixel>,
695 pub cursor: Cursor,
698}
699
700bitflags! {
701 pub struct ElementsFromPointFlags: u8 {
702 const FindAll = 0b00000001;
705 }
706}
707
708#[cfg(test)]
709mod test {
710 use std::sync::Arc;
711 use std::time::Duration;
712
713 use ipc_channel::ipc::IpcSharedMemory;
714 use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
715
716 use crate::ImageAnimationState;
717
718 #[test]
719 fn test() {
720 let image_frames: Vec<ImageFrame> = std::iter::repeat_with(|| ImageFrame {
721 delay: Some(Duration::from_millis(100)),
722 byte_range: 0..1,
723 width: 100,
724 height: 100,
725 })
726 .take(10)
727 .collect();
728 let image = RasterImage {
729 metadata: ImageMetadata {
730 width: 100,
731 height: 100,
732 },
733 format: PixelFormat::BGRA8,
734 id: None,
735 bytes: IpcSharedMemory::from_byte(1, 1),
736 frames: image_frames,
737 cors_status: CorsStatus::Unsafe,
738 };
739 let mut image_animation_state = ImageAnimationState::new(Arc::new(image), 0.0);
740
741 assert_eq!(image_animation_state.active_frame, 0);
742 assert_eq!(image_animation_state.frame_start_time, 0.0);
743 assert_eq!(
744 image_animation_state.update_frame_for_animation_timeline_value(0.101),
745 true
746 );
747 assert_eq!(image_animation_state.active_frame, 1);
748 assert_eq!(image_animation_state.frame_start_time, 0.101);
749 assert_eq!(
750 image_animation_state.update_frame_for_animation_timeline_value(0.116),
751 false
752 );
753 assert_eq!(image_animation_state.active_frame, 1);
754 assert_eq!(image_animation_state.frame_start_time, 0.101);
755 }
756}