1#![expect(unsafe_code)]
6
7use std::cell::{Cell, OnceCell, RefCell};
8use std::collections::HashMap;
9use std::fmt::Debug;
10use std::rc::Rc;
11use std::sync::{Arc, LazyLock};
12
13use app_units::Au;
14use bitflags::bitflags;
15use embedder_traits::{
16 EmbedderMsg, ScriptToEmbedderChan, Theme, UntrustedNodeAddress, ViewportDetails,
17};
18use euclid::{Point2D, Rect, Scale, Size2D};
19use fonts::{FontContext, FontContextWebFontMethods, WebFontDocumentContext};
20use fonts_traits::StylesheetWebFontLoadFinishedCallback;
21use icu_locid::subtags::Language;
22use layout_api::{
23 AxesOverflow, BoxAreaType, CSSPixelRectVec, DangerousStyleNode, IFrameSizes, Layout,
24 LayoutConfig, LayoutDamage, LayoutElement, LayoutFactory, LayoutNode, NodeRenderingType,
25 OffsetParentResponse, PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest,
26 ReflowRequestRestyle, ReflowResult, ReflowStatistics, ScrollContainerQueryFlags,
27 ScrollContainerResponse, TrustedNodeAddress, with_layout_state,
28};
29use log::{debug, warn};
30use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
31use net_traits::image_cache::ImageCache;
32use paint_api::CrossProcessPaintApi;
33use paint_api::display_list::{AxesScrollSensitivity, PaintDisplayListInfo, ScrollType};
34use parking_lot::{Mutex, RwLock};
35use profile_traits::mem::{Report, ReportKind};
36use profile_traits::time::{
37 self as profile_time, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType,
38};
39use profile_traits::{path, time_profile};
40use rustc_hash::FxHashMap;
41use script::layout_dom::{
42 ServoDangerousStyleDocument, ServoDangerousStyleElement, ServoLayoutElement, ServoLayoutNode,
43};
44use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter, ScriptThreadMessage};
45use servo_arc::Arc as ServoArc;
46use servo_base::Epoch;
47use servo_base::generic_channel::GenericSender;
48use servo_base::id::{PipelineId, WebViewId};
49use servo_config::opts::{self, DiagnosticsLogging, DiagnosticsLoggingOption};
50use servo_config::pref;
51use servo_url::ServoUrl;
52use style::animation::DocumentAnimationSet;
53use style::context::{
54 QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
55};
56use style::device::Device;
57use style::device::servo::FontMetricsProvider;
58use style::dom::{OpaqueNode, ShowSubtreeDataAndPrimaryValues, TDocument, TElement, TNode};
59use style::font_metrics::FontMetrics;
60use style::global_style_data::GLOBAL_STYLE_DATA;
61use style::invalidation::element::restyle_hints::RestyleHint;
62use style::invalidation::stylesheets::StylesheetInvalidationSet;
63use style::media_queries::{MediaList, MediaType};
64use style::properties::style_structs::Font;
65use style::properties::{ComputedValues, LonghandId, NonCustomPropertyId, PropertyId, ShorthandId};
66use style::queries::values::PrefersColorScheme;
67use style::selector_parser::{PseudoElement, SnapshotMap};
68use style::servo::media_features::PointerCapabilities;
69use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
70use style::stylesheets::{
71 CustomMediaMap, DocumentStyleSheet, Origin, Stylesheet, StylesheetInDocument,
72};
73use style::stylist::Stylist;
74use style::traversal::DomTraversal;
75use style::traversal_flags::TraversalFlags;
76use style::values::computed::font::GenericFontFamily;
77use style::values::computed::{CSSPixelLength, FontSize, Length, NonNegativeLength};
78use style::values::specified::font::{KeywordInfo, QueryFontMetricsFlags};
79use style::{Zero, driver};
80use style_traits::{CSSPixel, SpeculativePainter};
81use stylo_atoms::Atom;
82use url::Url;
83use webrender_api::ExternalScrollId;
84use webrender_api::units::{DevicePixel, LayoutVector2D};
85
86use crate::accessibility_tree::AccessibilityTree;
87use crate::context::{CachedImageOrError, ImageResolver, LayoutContext};
88use crate::display_list::{DisplayListBuilder, HitTest, PaintTimingHandler, StackingContextTree};
89use crate::dom::NodeExt;
90use crate::query::{
91 find_character_offset_in_fragment_descendants, get_the_text_steps, process_box_area_request,
92 process_box_areas_request, process_client_rect_request,
93 process_containing_block_descendant_query, process_containing_block_query,
94 process_current_css_zoom_query, process_effective_overflow_query,
95 process_node_scroll_area_request, process_offset_parent_query, process_padding_request,
96 process_resolved_font_style_query, process_resolved_style_request,
97 process_scroll_container_query,
98};
99use crate::traversal::{RecalcStyle, compute_damage_and_rebuild_box_tree};
100use crate::{BoxTree, FragmentTree};
101
102static STYLE_THREAD_POOL: Mutex<&LazyLock<style::global_style_data::StyleThreadPool>> =
108 Mutex::new(&style::global_style_data::STYLE_THREAD_POOL);
109
110static USER_AGENT_CSS: &[u8] = include_bytes!("./stylesheets/user-agent.css");
112
113static HTML_MODE_CSS: &[u8] = include_bytes!("./stylesheets/html-mode.css");
115
116static SERVO_CSS: &[u8] = include_bytes!("./stylesheets/servo.css");
118
119static PRESENTATIONAL_HINTS_CSS: &[u8] = include_bytes!("./stylesheets/presentational-hints.css");
121
122static QUIRKS_MODE_CSS: &[u8] = include_bytes!("./stylesheets/quirks-mode.css");
124
125pub struct LayoutThread {
127 id: PipelineId,
129
130 webview_id: WebViewId,
132
133 url: ServoUrl,
135
136 stylist: Stylist,
138
139 is_iframe: bool,
141
142 script_chan: GenericSender<ScriptThreadMessage>,
144
145 time_profiler_chan: profile_time::ProfilerChan,
147
148 embedder_chan: ScriptToEmbedderChan,
150
151 image_cache: Arc<dyn ImageCache>,
153
154 font_context: Arc<FontContext>,
156
157 have_added_user_agent_stylesheets: bool,
159
160 user_stylesheets: Rc<Vec<DocumentStyleSheet>>,
164
165 device_has_changed: bool,
168
169 have_ever_generated_display_list: Cell<bool>,
171
172 last_display_list_was_empty: Cell<bool>,
174
175 need_new_display_list: Cell<bool>,
181
182 need_containing_block_calculation: Cell<bool>,
187
188 need_new_stacking_context_tree: Cell<bool>,
193
194 box_tree: RefCell<Option<Arc<BoxTree>>>,
196
197 fragment_tree: RefCell<Option<Rc<FragmentTree>>>,
199
200 stacking_context_tree: RefCell<Option<StackingContextTree>>,
202
203 resolved_images_cache: Arc<RwLock<HashMap<ServoUrl, CachedImageOrError>>>,
207
208 registered_painters: RegisteredPaintersImpl,
210
211 paint_api: CrossProcessPaintApi,
213
214 debug: DiagnosticsLogging,
217
218 previously_highlighted_dom_node: Cell<Option<OpaqueNode>>,
222
223 paint_timing_handler: RefCell<Option<PaintTimingHandler>>,
225
226 accessibility_active: Cell<bool>,
228
229 accessibility_tree: RefCell<Option<AccessibilityTree>>,
232
233 needs_accessibility_update: Cell<bool>,
235}
236
237pub struct LayoutFactoryImpl();
238
239impl LayoutFactory for LayoutFactoryImpl {
240 fn create(&self, config: LayoutConfig) -> Box<dyn Layout> {
241 Box::new(LayoutThread::new(config))
242 }
243}
244
245impl Drop for LayoutThread {
246 fn drop(&mut self) {
247 let (keys, instance_keys) = self
248 .font_context
249 .collect_unused_webrender_resources(true );
250 self.paint_api
251 .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys)
252 }
253}
254
255impl Layout for LayoutThread {
256 fn device(&self) -> &Device {
257 self.stylist.device()
258 }
259
260 fn set_theme(&mut self, theme: Theme) -> bool {
261 let theme: PrefersColorScheme = theme.into();
262 let device = self.stylist.device_mut();
263 if theme == device.color_scheme() {
264 return false;
265 }
266
267 device.set_color_scheme(theme);
268 self.device_has_changed = true;
269 true
270 }
271
272 fn set_viewport_details(&mut self, viewport_details: ViewportDetails) -> bool {
273 let device = self.stylist.device_mut();
274 let device_pixel_ratio = Scale::new(viewport_details.hidpi_scale_factor.get());
275 let device_size = viewport_details.device_size.cast_unit();
276 if device.viewport_size() == viewport_details.size &&
277 device.device_pixel_ratio() == device_pixel_ratio &&
278 device.device_size() == device_size
279 {
280 return false;
281 }
282
283 device.set_viewport_size(viewport_details.size);
284 device.set_device_pixel_ratio(device_pixel_ratio);
285 device.set_device_size(device_size);
286 self.device_has_changed = true;
287 true
288 }
289
290 fn load_web_fonts_from_stylesheet(
291 &self,
292 stylesheet: &ServoArc<Stylesheet>,
293 document_context: &WebFontDocumentContext,
294 ) {
295 let guard = stylesheet.shared_lock.read();
296 self.load_all_web_fonts_from_stylesheet_with_guard(
297 &DocumentStyleSheet(stylesheet.clone()),
298 &guard,
299 document_context,
300 );
301 }
302
303 #[servo_tracing::instrument(skip_all)]
304 fn add_stylesheet(
305 &mut self,
306 stylesheet: ServoArc<Stylesheet>,
307 before_stylesheet: Option<ServoArc<Stylesheet>>,
308 document_context: &WebFontDocumentContext,
309 ) {
310 let guard = stylesheet.shared_lock.read();
311 let stylesheet = DocumentStyleSheet(stylesheet.clone());
312 self.load_all_web_fonts_from_stylesheet_with_guard(&stylesheet, &guard, document_context);
313
314 match before_stylesheet {
315 Some(insertion_point) => self.stylist.insert_stylesheet_before(
316 stylesheet,
317 DocumentStyleSheet(insertion_point),
318 &guard,
319 ),
320 None => self.stylist.append_stylesheet(stylesheet, &guard),
321 }
322 }
323
324 #[servo_tracing::instrument(skip_all)]
325 fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>) {
326 let guard = stylesheet.shared_lock.read();
327 let stylesheet = DocumentStyleSheet(stylesheet.clone());
328 self.stylist.remove_stylesheet(stylesheet.clone(), &guard);
329 self.font_context.remove_all_web_fonts_from_stylesheet(
330 &stylesheet,
331 self.stylist.device(),
332 &guard,
333 );
334 }
335
336 #[servo_tracing::instrument(skip_all)]
337 fn remove_cached_image(&mut self, url: &ServoUrl) {
338 let mut resolved_images_cache = self.resolved_images_cache.write();
339 resolved_images_cache.remove(url);
340 }
341
342 fn node_rendering_type(
343 &self,
344 node: TrustedNodeAddress,
345 pseudo: Option<PseudoElement>,
346 ) -> NodeRenderingType {
347 with_layout_state(|| {
348 let node = unsafe { ServoLayoutNode::new(&node) };
349
350 if node
352 .as_element()
353 .is_none_or(|element| element.style_data().is_none())
354 {
355 return NodeRenderingType::NotRendered;
356 }
357
358 let node = match pseudo {
359 Some(pseudo) => node.with_pseudo(pseudo),
360 None => Some(node),
361 };
362 let Some(node) = node else {
363 return NodeRenderingType::NotRendered;
364 };
365 node.rendering_type()
366 })
367 }
368
369 #[servo_tracing::instrument(skip_all)]
371 fn query_containing_block(&self, node: TrustedNodeAddress) -> Option<UntrustedNodeAddress> {
372 with_layout_state(|| {
373 let node = unsafe { ServoLayoutNode::new(&node) };
374 process_containing_block_query(node)
375 })
376 }
377
378 #[servo_tracing::instrument(skip_all)]
380 fn query_containing_block_is_descendant(
381 &self,
382 root: TrustedNodeAddress,
383 possible_descendant: TrustedNodeAddress,
384 ) -> bool {
385 with_layout_state(|| {
386 let (root, possible_descendant) = unsafe {
387 (
388 ServoLayoutNode::new(&root),
389 ServoLayoutNode::new(&possible_descendant),
390 )
391 };
392 process_containing_block_descendant_query(root, possible_descendant)
393 })
394 }
395
396 #[servo_tracing::instrument(skip_all)]
398 fn query_padding(&self, node: TrustedNodeAddress) -> Option<PhysicalSides> {
399 with_layout_state(|| {
400 if self.fragment_tree.borrow().is_none() {
403 return None;
404 }
405
406 let node = unsafe { ServoLayoutNode::new(&node) };
407 process_padding_request(node)
408 })
409 }
410
411 #[servo_tracing::instrument(skip_all)]
417 fn query_box_area(
418 &self,
419 node: TrustedNodeAddress,
420 area: BoxAreaType,
421 exclude_transform_and_inline: bool,
422 ) -> Option<Rect<Au, CSSPixel>> {
423 with_layout_state(|| {
424 if self.fragment_tree.borrow().is_none() {
427 return None;
428 }
429
430 let node = unsafe { ServoLayoutNode::new(&node) };
431 let stacking_context_tree = self.stacking_context_tree.borrow();
432 let stacking_context_tree = stacking_context_tree.as_ref()?;
433 process_box_area_request(
434 self,
435 stacking_context_tree,
436 node,
437 area,
438 exclude_transform_and_inline,
439 )
440 })
441 }
442
443 #[servo_tracing::instrument(skip_all)]
448 fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> CSSPixelRectVec {
449 with_layout_state(|| {
450 if self.fragment_tree.borrow().is_none() {
453 return None;
454 }
455
456 let node = unsafe { ServoLayoutNode::new(&node) };
457 let stacking_context_tree = self.stacking_context_tree.borrow();
458 let stacking_context_tree = stacking_context_tree.as_ref()?;
459 Some(process_box_areas_request(
460 self,
461 stacking_context_tree,
462 node,
463 area,
464 ))
465 })
466 .unwrap_or_default()
467 }
468
469 #[servo_tracing::instrument(skip_all)]
470 fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32, CSSPixel> {
471 with_layout_state(|| {
472 let node = unsafe { ServoLayoutNode::new(&node) };
473 process_client_rect_request(node)
474 })
475 }
476
477 #[servo_tracing::instrument(skip_all)]
478 fn query_current_css_zoom(&self, node: TrustedNodeAddress) -> f32 {
479 with_layout_state(|| {
480 let node = unsafe { ServoLayoutNode::new(&node) };
481 process_current_css_zoom_query(node)
482 })
483 }
484
485 #[servo_tracing::instrument(skip_all)]
486 fn query_element_inner_outer_text(&self, node: layout_api::TrustedNodeAddress) -> String {
487 with_layout_state(|| {
488 let node = unsafe { ServoLayoutNode::new(&node) };
489 get_the_text_steps(node)
490 })
491 }
492 #[servo_tracing::instrument(skip_all)]
493 fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse {
494 with_layout_state(|| {
495 let node = unsafe { ServoLayoutNode::new(&node) };
496 let stacking_context_tree = self.stacking_context_tree.borrow();
497 let stacking_context_tree = stacking_context_tree.as_ref()?;
498 process_offset_parent_query(self, &stacking_context_tree.paint_info.scroll_tree, node)
499 })
500 .unwrap_or_default()
501 }
502
503 #[servo_tracing::instrument(skip_all)]
504 fn query_scroll_container(
505 &self,
506 node: Option<TrustedNodeAddress>,
507 flags: ScrollContainerQueryFlags,
508 ) -> Option<ScrollContainerResponse> {
509 with_layout_state(|| {
510 let node = unsafe { node.as_ref().map(|node| ServoLayoutNode::new(node)) };
511 let viewport_overflow = self.box_tree.borrow().as_ref()?.viewport_overflow;
512 process_scroll_container_query(node, flags, viewport_overflow)
513 })
514 }
515
516 #[servo_tracing::instrument(skip_all)]
517 fn query_resolved_style(
518 &self,
519 node: TrustedNodeAddress,
520 pseudo: Option<PseudoElement>,
521 property_id: PropertyId,
522 animations: DocumentAnimationSet,
523 animation_timeline_value: f64,
524 ) -> String {
525 with_layout_state(|| {
526 let node = unsafe { ServoLayoutNode::new(&node) };
527 let document = unsafe { node.dangerous_style_node() }.owner_doc();
528 let shared_locks = document.shared_style_locks();
529 let guards = StylesheetGuards {
530 author: &shared_locks.author.read(),
531 ua_or_user: &shared_locks.ua_or_user.read(),
532 };
533 let snapshot_map = SnapshotMap::new();
534
535 let shared_style_context = self.build_shared_style_context(
536 guards,
537 &snapshot_map,
538 animation_timeline_value,
539 &animations,
540 TraversalFlags::empty(),
541 );
542
543 process_resolved_style_request(self, &shared_style_context, node, &pseudo, &property_id)
544 })
545 }
546
547 #[servo_tracing::instrument(skip_all)]
548 fn query_resolved_font_style(
549 &self,
550 node: TrustedNodeAddress,
551 value: &str,
552 animations: DocumentAnimationSet,
553 animation_timeline_value: f64,
554 ) -> Option<ServoArc<Font>> {
555 with_layout_state(|| {
556 let node = unsafe { ServoLayoutNode::new(&node) };
557 let document = unsafe { node.dangerous_style_node() }.owner_doc();
558 let shared_locks = document.shared_style_locks();
559 let shared_author_lock = &shared_locks.author;
560 let guards = StylesheetGuards {
561 author: &shared_author_lock.read(),
562 ua_or_user: &shared_locks.ua_or_user.read(),
563 };
564 let snapshot_map = SnapshotMap::new();
565 let shared_style_context = self.build_shared_style_context(
566 guards,
567 &snapshot_map,
568 animation_timeline_value,
569 &animations,
570 TraversalFlags::empty(),
571 );
572
573 process_resolved_font_style_query(
574 &shared_style_context,
575 node,
576 value,
577 self.url.clone(),
578 shared_author_lock,
579 )
580 })
581 }
582
583 #[servo_tracing::instrument(skip_all)]
584 fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32, CSSPixel> {
585 with_layout_state(|| {
586 let node = node.map(|node| unsafe { ServoLayoutNode::new(&node) });
587 process_node_scroll_area_request(self, node, self.fragment_tree.borrow().clone())
588 })
589 }
590
591 #[servo_tracing::instrument(skip_all)]
592 fn query_text_index(
593 &self,
594 node: TrustedNodeAddress,
595 point_in_node: Point2D<Au, CSSPixel>,
596 ) -> Option<usize> {
597 with_layout_state(|| {
598 let node = unsafe { ServoLayoutNode::new(&node) };
599 let stacking_context_tree = self.stacking_context_tree.borrow_mut();
600 let stacking_context_tree = stacking_context_tree.as_ref()?;
601 find_character_offset_in_fragment_descendants(
602 &node,
603 stacking_context_tree,
604 point_in_node,
605 )
606 })
607 }
608
609 #[servo_tracing::instrument(skip_all)]
610 fn query_elements_from_point(
611 &self,
612 point: webrender_api::units::LayoutPoint,
613 ) -> Vec<layout_api::ElementsFromPointResult> {
614 with_layout_state(|| {
615 self.stacking_context_tree
616 .borrow_mut()
617 .as_mut()
618 .map(|tree| HitTest::run(tree, point))
619 .unwrap_or_default()
620 })
621 }
622
623 #[servo_tracing::instrument(skip_all)]
624 fn query_effective_overflow(&self, node: TrustedNodeAddress) -> Option<AxesOverflow> {
625 with_layout_state(|| {
626 let node = unsafe { ServoLayoutNode::new(&node) };
627 process_effective_overflow_query(node)
628 })
629 }
630
631 fn exit_now(&mut self) {}
632
633 fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps) {
634 let formatted_url = &format!("url({})", self.url);
636 reports.push(Report {
637 path: path![formatted_url, "layout-thread", "display-list"],
638 kind: ReportKind::ExplicitJemallocHeapSize,
639 size: 0,
640 });
641
642 reports.push(Report {
643 path: path![formatted_url, "layout-thread", "stylist"],
644 kind: ReportKind::ExplicitJemallocHeapSize,
645 size: self.stylist.size_of(ops),
646 });
647
648 reports.push(Report {
649 path: path![formatted_url, "layout-thread", "font-context"],
650 kind: ReportKind::ExplicitJemallocHeapSize,
651 size: self.font_context.conditional_size_of(ops),
652 });
653
654 reports.push(Report {
655 path: path![formatted_url, "layout-thread", "box-tree"],
656 kind: ReportKind::ExplicitJemallocHeapSize,
657 size: self
658 .box_tree
659 .borrow()
660 .as_ref()
661 .map_or(0, |tree| tree.conditional_size_of(ops)),
662 });
663
664 reports.push(Report {
665 path: path![formatted_url, "layout-thread", "fragment-tree"],
666 kind: ReportKind::ExplicitJemallocHeapSize,
667 size: self
668 .fragment_tree
669 .borrow()
670 .as_ref()
671 .map(|tree| tree.conditional_size_of(ops))
672 .unwrap_or_default(),
673 });
674
675 reports.push(Report {
676 path: path![formatted_url, "layout-thread", "stacking-context-tree"],
677 kind: ReportKind::ExplicitJemallocHeapSize,
678 size: self.stacking_context_tree.size_of(ops),
679 });
680
681 reports.extend(self.image_cache.memory_reports(formatted_url, ops));
682 }
683
684 fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
685 self.stylist.set_quirks_mode(quirks_mode);
686 }
687
688 fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult> {
689 time_profile!(
690 profile_time::ProfilerCategory::Layout,
691 self.profiler_metadata(),
692 self.time_profiler_chan.clone(),
693 || with_layout_state(|| self.handle_reflow(reflow_request)),
694 )
695 }
696
697 fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails) {
698 with_layout_state(|| {
699 if self.stacking_context_tree.borrow().is_some() &&
700 !self.need_new_stacking_context_tree.get()
701 {
702 return;
703 }
704 self.build_stacking_context_tree(viewport_details);
705 })
706 }
707
708 fn register_paint_worklet_modules(
709 &mut self,
710 _name: Atom,
711 _properties: Vec<Atom>,
712 _painter: Box<dyn Painter>,
713 ) {
714 }
715
716 fn set_scroll_offsets_from_renderer(
717 &mut self,
718 scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
719 ) {
720 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
721 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
722 warn!("Received scroll offsets before finishing layout.");
723 return;
724 };
725
726 stacking_context_tree
727 .paint_info
728 .scroll_tree
729 .set_all_scroll_offsets(scroll_states);
730 }
731
732 fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D> {
733 self.stacking_context_tree
734 .borrow_mut()
735 .as_mut()
736 .and_then(|tree| tree.paint_info.scroll_tree.scroll_offset(id))
737 }
738
739 fn needs_new_display_list(&self) -> bool {
740 self.need_new_display_list.get()
741 }
742
743 fn set_needs_new_display_list(&self) {
744 self.need_new_display_list.set(true);
745 }
746
747 fn stylist_mut(&mut self) -> &mut Stylist {
749 &mut self.stylist
750 }
751
752 fn set_accessibility_active(&self, active: bool, epoch: Epoch) {
753 self.accessibility_active.set(active);
754 if !active {
755 self.accessibility_tree.replace(None);
756 return;
757 }
758
759 self.set_needs_accessibility_update();
760 let mut accessibility_tree = self.accessibility_tree.borrow_mut();
761 if accessibility_tree.is_some() {
762 return;
763 }
764 *accessibility_tree = Some(AccessibilityTree::new(self.id.into(), epoch));
765 }
766
767 fn accessibility_active(&self) -> bool {
768 self.accessibility_active.get()
769 }
770
771 fn needs_accessibility_update(&self) -> bool {
772 self.needs_accessibility_update.get()
773 }
774
775 fn set_needs_accessibility_update(&self) {
776 self.needs_accessibility_update.set(true);
777 }
778}
779
780impl LayoutThread {
781 fn new(config: LayoutConfig) -> LayoutThread {
782 config
784 .paint_api
785 .send_initial_transaction(config.webview_id, config.id.into());
786
787 let mut font = Font::initial_values();
788 let default_font_size = pref!(fonts_default_size);
789 font.font_size = FontSize {
790 computed_size: NonNegativeLength::new(default_font_size as f32),
791 used_size: NonNegativeLength::new(default_font_size as f32),
792 keyword_info: KeywordInfo::medium(),
793 };
794
795 let device = Device::new(
798 MediaType::screen(),
799 QuirksMode::NoQuirks,
800 config.viewport_details.size,
801 config.viewport_details.device_size.cast_unit(),
802 Scale::new(config.viewport_details.hidpi_scale_factor.get()),
803 Box::new(LayoutFontMetricsProvider(config.font_context.clone())),
804 ComputedValues::initial_values_with_font_override(font),
805 config.theme.into(),
806 PointerCapabilities::default(),
807 PointerCapabilities::default(),
808 );
809
810 LayoutThread {
811 id: config.id,
812 webview_id: config.webview_id,
813 url: config.url,
814 is_iframe: config.is_iframe,
815 script_chan: config.script_chan.clone(),
816 time_profiler_chan: config.time_profiler_chan,
817 embedder_chan: config.embedder_chan.clone(),
818 registered_painters: RegisteredPaintersImpl(Default::default()),
819 image_cache: config.image_cache,
820 font_context: config.font_context,
821 have_added_user_agent_stylesheets: false,
822 have_ever_generated_display_list: Cell::new(false),
823 last_display_list_was_empty: Cell::new(true),
824 device_has_changed: false,
825 need_containing_block_calculation: Cell::new(false),
826 need_new_display_list: Cell::new(false),
827 need_new_stacking_context_tree: Cell::new(false),
828 box_tree: Default::default(),
829 fragment_tree: Default::default(),
830 stacking_context_tree: Default::default(),
831 paint_api: config.paint_api,
832 stylist: Stylist::new(device, QuirksMode::NoQuirks),
833 resolved_images_cache: Default::default(),
834 debug: opts::get().debug.clone(),
835 previously_highlighted_dom_node: Cell::new(None),
836 paint_timing_handler: Default::default(),
837 user_stylesheets: config.user_stylesheets,
838 accessibility_active: Cell::new(false),
839 accessibility_tree: Default::default(),
840 needs_accessibility_update: Cell::new(false),
841 }
842 }
843
844 fn build_shared_style_context<'a>(
845 &'a self,
846 guards: StylesheetGuards<'a>,
847 snapshot_map: &'a SnapshotMap,
848 animation_timeline_value: f64,
849 animations: &DocumentAnimationSet,
850 traversal_flags: TraversalFlags,
851 ) -> SharedStyleContext<'a> {
852 SharedStyleContext {
853 stylist: &self.stylist,
854 options: GLOBAL_STYLE_DATA.options.clone(),
855 guards,
856 visited_styles_enabled: false,
857 animations: animations.clone(),
858 registered_speculative_painters: &self.registered_painters,
859 current_time_for_animations: animation_timeline_value,
860 traversal_flags,
861 snapshot_map,
862 }
863 }
864
865 fn load_all_web_fonts_from_stylesheet_with_guard(
866 &self,
867 stylesheet: &DocumentStyleSheet,
868 guard: &SharedRwLockReadGuard,
869 document_context: &WebFontDocumentContext,
870 ) {
871 let custom_media = &CustomMediaMap::default();
872 if !stylesheet.is_effective_for_device(self.stylist.device(), custom_media, guard) {
873 return;
874 }
875
876 let locked_script_channel = Mutex::new(self.script_chan.clone());
877 let pipeline_id = self.id;
878 let web_font_finished_loading_callback = move |succeeded: bool| {
879 if succeeded {
880 let _ = locked_script_channel
881 .lock()
882 .send(ScriptThreadMessage::WebFontLoaded(pipeline_id));
883 }
884 };
885
886 self.font_context.add_all_web_fonts_from_stylesheet(
887 self.webview_id,
888 stylesheet,
889 guard,
890 self.stylist.device(),
891 Arc::new(web_font_finished_loading_callback) as StylesheetWebFontLoadFinishedCallback,
892 document_context,
893 );
894 }
895
896 fn can_skip_reflow_request_entirely(&self, reflow_request: &ReflowRequest) -> bool {
900 if reflow_request.restyle.is_some() {
902 return false;
903 }
904 if self.fragment_tree.borrow().is_none() {
906 return false;
907 }
908 if self.needs_accessibility_update() {
910 return false;
911 }
912
913 let necessary_phases = ReflowPhases::necessary(&reflow_request.reflow_goal);
916 if necessary_phases.is_empty() {
917 return true;
918 }
919
920 if necessary_phases == ReflowPhases::StackingContextTreeConstruction {
923 return self.stacking_context_tree.borrow().is_some() &&
924 !self.need_new_stacking_context_tree.get();
925 }
926
927 assert_eq!(
930 necessary_phases,
931 ReflowPhases::StackingContextTreeConstruction | ReflowPhases::DisplayListConstruction
932 );
933 !self.need_new_display_list.get()
934 }
935
936 fn maybe_print_reflow_event(&self, reflow_request: &ReflowRequest) {
937 if !self
938 .debug
939 .is_enabled(DiagnosticsLoggingOption::RelayoutEvent)
940 {
941 return;
942 }
943
944 println!(
945 "**** Reflow({}) => {:?}, {:?}",
946 self.id,
947 reflow_request.reflow_goal,
948 reflow_request
949 .restyle
950 .as_ref()
951 .map(|restyle| restyle.reason)
952 .unwrap_or_default()
953 );
954 }
955
956 fn handle_update_scroll_node_request(&self, reflow_request: &ReflowRequest) -> bool {
959 if let ReflowGoal::UpdateScrollNode(external_scroll_id, offset) = reflow_request.reflow_goal
960 {
961 self.set_scroll_offset_from_script(external_scroll_id, offset)
962 } else {
963 false
964 }
965 }
966
967 fn handle_accessibility_tree_update(
968 &self,
969 root_element: &ServoLayoutNode,
970 reflow_request: &mut ReflowRequest,
971 ) -> bool {
972 if !self.needs_accessibility_update() {
973 return false;
974 }
975 let mut accessibility_tree = self.accessibility_tree.borrow_mut();
976 let Some(accessibility_tree) = accessibility_tree.as_mut() else {
977 return false;
978 };
979
980 let accessibility_tree = &mut *accessibility_tree;
981 let rooted_nodes =
982 std::mem::take(&mut reflow_request.rooted_nodes_for_accessibility_integrity_check);
983
984 if let Some(tree_update) = accessibility_tree.update_tree(root_element, rooted_nodes) {
985 let _ = self
989 .embedder_chan
990 .send(EmbedderMsg::AccessibilityTreeUpdate(
991 self.webview_id,
992 tree_update,
993 accessibility_tree.embedder_epoch(),
994 ));
995 }
996 self.needs_accessibility_update.set(false);
997 true
998 }
999
1000 #[servo_tracing::instrument(skip_all)]
1002 fn handle_reflow(&mut self, mut reflow_request: ReflowRequest) -> Option<ReflowResult> {
1003 self.maybe_print_reflow_event(&reflow_request);
1004
1005 if self.can_skip_reflow_request_entirely(&reflow_request) {
1006 return self
1008 .handle_update_scroll_node_request(&reflow_request)
1009 .then(|| ReflowResult {
1010 reflow_phases_run: ReflowPhasesRun::UpdatedScrollNodeOffset,
1011 ..Default::default()
1012 });
1013 }
1014
1015 let document = unsafe { ServoLayoutNode::new(&reflow_request.document) };
1016 let document = unsafe { document.dangerous_style_node() }
1017 .as_document()
1018 .unwrap();
1019 let Some(root_element) = document.root_element() else {
1020 if !self.last_display_list_was_empty.get() {
1021 return self.clear_layout_trees_and_send_empty_display_list(&reflow_request);
1022 }
1023 debug!("layout: No root node: bailing");
1024 return None;
1025 };
1026
1027 let image_resolver = Arc::new(ImageResolver {
1028 origin: reflow_request.origin.clone(),
1029 image_cache: self.image_cache.clone(),
1030 resolved_images_cache: self.resolved_images_cache.clone(),
1031 pending_images: Mutex::default(),
1032 pending_rasterization_images: Mutex::default(),
1033 pending_svg_elements_for_serialization: Mutex::default(),
1034 animating_images: reflow_request.animating_images.clone(),
1035 animation_timeline_value: reflow_request.animation_timeline_value,
1036 });
1037 let mut reflow_statistics = Default::default();
1038
1039 let (mut reflow_phases_run, iframe_sizes) = self.restyle_and_build_trees(
1040 &mut reflow_request,
1041 document,
1042 root_element,
1043 &image_resolver,
1044 );
1045 if self.build_stacking_context_tree_for_reflow(&reflow_request) {
1046 reflow_phases_run.insert(ReflowPhasesRun::BuiltStackingContextTree);
1047 }
1048 if self.build_display_list(&reflow_request, &image_resolver, &mut reflow_statistics) {
1049 reflow_phases_run.insert(ReflowPhasesRun::BuiltDisplayList);
1050 }
1051 if self.handle_update_scroll_node_request(&reflow_request) {
1052 reflow_phases_run.insert(ReflowPhasesRun::UpdatedScrollNodeOffset);
1053 }
1054 if self.handle_accessibility_tree_update(&root_element.as_node(), &mut reflow_request) {
1055 reflow_phases_run.insert(ReflowPhasesRun::UpdatedAccessibilityTree);
1056 }
1057
1058 if self.debug.is_enabled(DiagnosticsLoggingOption::FlowTree) &&
1059 reflow_phases_run.contains(ReflowPhasesRun::RanLayout) &&
1060 let Some(fragment_tree) = &*self.fragment_tree.borrow()
1061 {
1062 fragment_tree.print();
1063 }
1064
1065 let pending_images = std::mem::take(&mut *image_resolver.pending_images.lock());
1066 let pending_rasterization_images =
1067 std::mem::take(&mut *image_resolver.pending_rasterization_images.lock());
1068 let pending_svg_elements_for_serialization =
1069 std::mem::take(&mut *image_resolver.pending_svg_elements_for_serialization.lock());
1070
1071 Some(ReflowResult {
1072 reflow_phases_run,
1073 pending_images,
1074 pending_rasterization_images,
1075 pending_svg_elements_for_serialization,
1076 iframe_sizes: Some(iframe_sizes),
1077 reflow_statistics,
1078 })
1079 }
1080
1081 #[servo_tracing::instrument(skip_all)]
1082 fn prepare_stylist_for_reflow<'dom>(
1083 &mut self,
1084 reflow_request: &ReflowRequest,
1085 document: ServoDangerousStyleDocument<'dom>,
1086 guards: &StylesheetGuards,
1087 ua_stylesheets: &UserAgentStylesheets,
1088 ) -> StylesheetInvalidationSet {
1089 if !self.have_added_user_agent_stylesheets {
1090 for stylesheet in &ua_stylesheets.user_agent_stylesheets {
1091 self.stylist
1092 .append_stylesheet(stylesheet.clone(), guards.ua_or_user);
1093 self.load_all_web_fonts_from_stylesheet_with_guard(
1094 stylesheet,
1095 guards.ua_or_user,
1096 &reflow_request.document_context,
1097 );
1098 }
1099
1100 if document.is_html_document() {
1101 self.stylist.append_stylesheet(
1102 ua_stylesheets.html_mode_stylesheet.clone(),
1103 guards.ua_or_user,
1104 );
1105 self.load_all_web_fonts_from_stylesheet_with_guard(
1106 &ua_stylesheets.html_mode_stylesheet,
1107 guards.ua_or_user,
1108 &reflow_request.document_context,
1109 );
1110 }
1111
1112 for user_stylesheet in self.user_stylesheets.iter() {
1113 self.stylist
1114 .append_stylesheet(user_stylesheet.clone(), guards.ua_or_user);
1115 self.load_all_web_fonts_from_stylesheet_with_guard(
1116 user_stylesheet,
1117 guards.ua_or_user,
1118 &reflow_request.document_context,
1119 );
1120 }
1121
1122 if self.stylist.quirks_mode() == QuirksMode::Quirks {
1123 self.stylist.append_stylesheet(
1124 ua_stylesheets.quirks_mode_stylesheet.clone(),
1125 guards.ua_or_user,
1126 );
1127 self.load_all_web_fonts_from_stylesheet_with_guard(
1128 &ua_stylesheets.quirks_mode_stylesheet,
1129 guards.ua_or_user,
1130 &reflow_request.document_context,
1131 );
1132 }
1133 self.have_added_user_agent_stylesheets = true;
1134 }
1135
1136 if reflow_request.stylesheets_changed() {
1137 self.stylist
1138 .force_stylesheet_origins_dirty(Origin::Author.into());
1139 }
1140
1141 document.flush_shadow_root_stylesheets_if_necessary(&mut self.stylist, guards.author);
1142
1143 self.stylist.flush(guards)
1144 }
1145
1146 #[servo_tracing::instrument(skip_all)]
1147 fn restyle_and_build_trees(
1148 &mut self,
1149 reflow_request: &mut ReflowRequest,
1150 document: ServoDangerousStyleDocument<'_>,
1151 root_element: ServoLayoutElement<'_>,
1152 image_resolver: &Arc<ImageResolver>,
1153 ) -> (ReflowPhasesRun, IFrameSizes) {
1154 let mut snapshot_map = SnapshotMap::new();
1155 let _snapshot_setter = match reflow_request.restyle.as_mut() {
1156 Some(restyle) => SnapshotSetter::new(restyle, &mut snapshot_map),
1157 None => return Default::default(),
1158 };
1159
1160 let shared_locks = document.shared_style_locks();
1161 let user_agent_stylesheets = get_ua_stylesheets(&shared_locks.ua_or_user);
1162 let guards = StylesheetGuards {
1163 author: &shared_locks.author.read(),
1164 ua_or_user: &shared_locks.ua_or_user.read(),
1165 };
1166
1167 let rayon_pool = STYLE_THREAD_POOL.lock();
1168 let rayon_pool = rayon_pool.pool();
1169 let rayon_pool = rayon_pool.as_ref();
1170
1171 let device_has_changed = std::mem::replace(&mut self.device_has_changed, false);
1172 let dangerous_root_element = unsafe { root_element.dangerous_style_element() };
1173 if device_has_changed {
1174 let sheet_origins_affected_by_device_change = self
1175 .stylist
1176 .media_features_change_changed_style(&guards, self.device());
1177 self.stylist
1178 .force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
1179
1180 if let Some(mut data) = dangerous_root_element.mutate_data() {
1181 data.hint.insert(RestyleHint::recascade_subtree());
1182 }
1183 }
1184
1185 self.prepare_stylist_for_reflow(reflow_request, document, &guards, &user_agent_stylesheets)
1186 .process_style(dangerous_root_element, Some(&snapshot_map));
1187
1188 if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
1189 self.need_new_display_list.set(true);
1192 }
1193
1194 let layout_context = LayoutContext {
1195 style_context: self.build_shared_style_context(
1196 guards,
1197 &snapshot_map,
1198 reflow_request.animation_timeline_value,
1199 &reflow_request.animations,
1200 match reflow_request.stylesheets_changed() {
1201 true => TraversalFlags::ForCSSRuleChanges,
1202 false => TraversalFlags::empty(),
1203 },
1204 ),
1205 font_context: self.font_context.clone(),
1206 iframe_sizes: Mutex::default(),
1207 allow_parallel_layout: rayon_pool.is_some(),
1208 image_resolver: image_resolver.clone(),
1209 painter_id: self.webview_id.into(),
1210 parallelism_job_count_minimum: pref!(layout_parallelism_job_count_minimum) as usize,
1211 parallelism_job_size_minimum: pref!(layout_parallelism_job_size_minimum) as usize,
1212 device_size: reflow_request.viewport_details.device_size.cast_unit(),
1213 };
1214
1215 let restyle = reflow_request
1216 .restyle
1217 .as_ref()
1218 .expect("Should not get here if there is not restyle.");
1219
1220 let recalc_style_traversal;
1221 let dirty_root;
1222 {
1223 let _span = profile_traits::trace_span!("Styling").entered();
1224
1225 let original_dirty_root = unsafe {
1226 ServoLayoutNode::new(&restyle.dirty_root.unwrap())
1227 .as_element()
1228 .unwrap()
1229 .dangerous_style_element()
1230 };
1231
1232 recalc_style_traversal = RecalcStyle::new(&layout_context);
1233 let token = {
1234 let shared = DomTraversal::<ServoDangerousStyleElement>::shared_context(
1235 &recalc_style_traversal,
1236 );
1237 RecalcStyle::pre_traverse(original_dirty_root, shared)
1238 };
1239
1240 if !token.should_traverse() {
1241 layout_context.style_context.stylist.rule_tree().maybe_gc();
1242 return Default::default();
1243 }
1244
1245 dirty_root = driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
1246 }
1247
1248 let root_node = root_element.as_node();
1249 let damage_from_environment = if device_has_changed {
1250 LayoutDamage::Relayout
1251 } else {
1252 LayoutDamage::empty()
1253 };
1254
1255 let mut box_tree = self.box_tree.borrow_mut();
1256 let mut layout_roots = Vec::new();
1257 let damage = {
1258 let box_tree = &mut *box_tree;
1259 let mut compute_damage_and_build_box_tree = || {
1260 compute_damage_and_rebuild_box_tree(
1261 box_tree,
1262 &layout_context,
1263 dirty_root.layout_node(),
1264 root_node,
1265 damage_from_environment,
1266 &mut layout_roots,
1267 )
1268 };
1269
1270 if let Some(pool) = rayon_pool {
1271 pool.install(compute_damage_and_build_box_tree)
1272 } else {
1273 compute_damage_and_build_box_tree()
1274 }
1275 };
1276
1277 if damage.contains(LayoutDamage::RebuildStackingContextTree) {
1278 self.need_new_stacking_context_tree.set(true);
1279 }
1280 if damage.contains(LayoutDamage::Repaint) {
1281 self.need_new_display_list.set(true);
1282 }
1283
1284 if !damage.contains(LayoutDamage::Relayout) {
1285 if damage.contains(LayoutDamage::RecalculateOverflow) {
1286 assert!(self.need_new_display_list.get());
1287 assert!(self.need_new_stacking_context_tree.get());
1288 self.fragment_tree
1289 .borrow()
1290 .as_ref()
1291 .expect("Should always have a FragmentTree when layout unnecessary")
1292 .clear_scrollable_overflow();
1293 }
1294
1295 if !damage.contains(LayoutDamage::DescendantCollectedAsLayoutRoot) {
1296 layout_context.style_context.stylist.rule_tree().maybe_gc();
1297 return (ReflowPhasesRun::empty(), IFrameSizes::default());
1298 }
1299
1300 debug_assert!(!layout_roots.is_empty());
1301 if layout_roots
1302 .into_iter()
1303 .all(|layout_root| layout_root.try_layout(&layout_context))
1304 {
1305 return (
1306 ReflowPhasesRun::RanLayout,
1307 std::mem::take(&mut *layout_context.iframe_sizes.lock()),
1308 );
1309 }
1310 }
1311
1312 let box_tree = &*box_tree;
1313 let viewport_size = self.stylist.device().au_viewport_size();
1314 let run_layout = || {
1315 box_tree
1316 .as_ref()
1317 .unwrap()
1318 .layout(recalc_style_traversal.context(), viewport_size)
1319 };
1320 let fragment_tree = Rc::new(if let Some(pool) = rayon_pool {
1321 pool.install(run_layout)
1322 } else {
1323 run_layout()
1324 });
1325
1326 *self.fragment_tree.borrow_mut() = Some(fragment_tree);
1327
1328 if self.debug.is_enabled(DiagnosticsLoggingOption::StyleTree) {
1329 println!(
1330 "{:?}",
1331 ShowSubtreeDataAndPrimaryValues(dangerous_root_element.as_node())
1332 );
1333 }
1334 if self.debug.is_enabled(DiagnosticsLoggingOption::RuleTree) {
1335 recalc_style_traversal
1336 .context()
1337 .style_context
1338 .stylist
1339 .rule_tree()
1340 .dump_stdout(&layout_context.style_context.guards);
1341 }
1342
1343 layout_context.style_context.stylist.rule_tree().maybe_gc();
1345
1346 let mut iframe_sizes = layout_context.iframe_sizes.lock();
1347 (
1348 ReflowPhasesRun::RanLayout,
1349 std::mem::take(&mut *iframe_sizes),
1350 )
1351 }
1352
1353 fn build_stacking_context_tree_for_reflow(&self, reflow_request: &ReflowRequest) -> bool {
1354 if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1355 .contains(ReflowPhases::StackingContextTreeConstruction)
1356 {
1357 return false;
1358 }
1359 if !self.need_new_stacking_context_tree.get() {
1360 return false;
1361 }
1362
1363 self.build_stacking_context_tree(reflow_request.viewport_details)
1364 }
1365
1366 #[servo_tracing::instrument(name = "Stacking Context Tree Construction", skip_all)]
1367 fn build_stacking_context_tree(&self, viewport_details: ViewportDetails) -> bool {
1368 let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1369 return false;
1370 };
1371
1372 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1373 let old_scroll_offsets = stacking_context_tree
1374 .as_ref()
1375 .map(|tree| tree.paint_info.scroll_tree.scroll_offsets());
1376
1377 self.need_containing_block_calculation.set(false);
1379
1380 let mut new_stacking_context_tree = StackingContextTree::new(
1384 fragment_tree,
1385 viewport_details,
1386 self.id.into(),
1387 !self.have_ever_generated_display_list.get(),
1388 &self.debug,
1389 );
1390
1391 if let Some(old_scroll_offsets) = old_scroll_offsets {
1395 new_stacking_context_tree
1396 .paint_info
1397 .scroll_tree
1398 .set_all_scroll_offsets(&old_scroll_offsets);
1399 }
1400
1401 if self.debug.is_enabled(DiagnosticsLoggingOption::ScrollTree) {
1402 new_stacking_context_tree
1403 .paint_info
1404 .scroll_tree
1405 .debug_print();
1406 }
1407
1408 *stacking_context_tree = Some(new_stacking_context_tree);
1409
1410 self.need_new_stacking_context_tree.set(false);
1412 assert!(self.need_new_display_list.get());
1413
1414 true
1415 }
1416
1417 #[servo_tracing::instrument(name = "Display List Construction", skip_all)]
1420 fn build_display_list(
1421 &self,
1422 reflow_request: &ReflowRequest,
1423 image_resolver: &Arc<ImageResolver>,
1424 reflow_statistics: &mut ReflowStatistics,
1425 ) -> bool {
1426 if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1427 .contains(ReflowPhases::DisplayListConstruction)
1428 {
1429 return false;
1430 }
1431 let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1432 return false;
1433 };
1434 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1435 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1436 return false;
1437 };
1438
1439 if !self.need_new_display_list.get() {
1443 return false;
1444 }
1445
1446 stacking_context_tree.paint_info.epoch = reflow_request.epoch;
1449
1450 let mut paint_timing_handler = self.paint_timing_handler.borrow_mut();
1451 let paint_timing_handler = match paint_timing_handler.as_mut() {
1453 Some(paint_timing_handler) => paint_timing_handler,
1454 None => {
1455 *paint_timing_handler = Some(PaintTimingHandler::new(
1456 stacking_context_tree
1457 .paint_info
1458 .viewport_details
1459 .layout_size(),
1460 ));
1461 paint_timing_handler.as_mut().unwrap()
1462 },
1463 };
1464
1465 let built_display_list = DisplayListBuilder::build(
1466 stacking_context_tree,
1467 fragment_tree,
1468 image_resolver.clone(),
1469 self.device().device_pixel_ratio(),
1470 reflow_request.highlighted_dom_node,
1471 &self.debug,
1472 paint_timing_handler,
1473 reflow_statistics,
1474 );
1475 self.paint_api.send_display_list(
1476 self.webview_id,
1477 &stacking_context_tree.paint_info,
1478 built_display_list,
1479 );
1480
1481 if paint_timing_handler.did_lcp_candidate_update() &&
1482 let Some(lcp_candidate) = paint_timing_handler.largest_contentful_paint_candidate()
1483 {
1484 self.paint_api.send_lcp_candidate(
1485 lcp_candidate,
1486 self.webview_id,
1487 self.id,
1488 stacking_context_tree.paint_info.epoch,
1489 );
1490 paint_timing_handler.unset_lcp_candidate_updated();
1491 }
1492
1493 let (keys, instance_keys) = self
1494 .font_context
1495 .collect_unused_webrender_resources(false );
1496 self.paint_api
1497 .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys);
1498 self.last_display_list_was_empty.set(false);
1499 self.have_ever_generated_display_list.set(true);
1500 self.need_new_display_list.set(false);
1501 self.previously_highlighted_dom_node
1502 .set(reflow_request.highlighted_dom_node);
1503 true
1504 }
1505
1506 fn set_scroll_offset_from_script(
1507 &self,
1508 external_scroll_id: ExternalScrollId,
1509 offset: LayoutVector2D,
1510 ) -> bool {
1511 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1512 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1513 return false;
1514 };
1515
1516 if let Some(offset) = stacking_context_tree
1517 .paint_info
1518 .scroll_tree
1519 .set_scroll_offset_for_node_with_external_scroll_id(
1520 external_scroll_id,
1521 offset,
1522 ScrollType::Script,
1523 )
1524 {
1525 self.paint_api.scroll_node_by_delta(
1526 self.webview_id,
1527 self.id.into(),
1528 offset,
1529 external_scroll_id,
1530 );
1531 true
1532 } else {
1533 false
1534 }
1535 }
1536
1537 fn profiler_metadata(&self) -> Option<TimerMetadata> {
1539 Some(TimerMetadata {
1540 url: self.url.to_string(),
1541 iframe: if self.is_iframe {
1542 TimerMetadataFrameType::IFrame
1543 } else {
1544 TimerMetadataFrameType::RootWindow
1545 },
1546 incremental: if self.have_ever_generated_display_list.get() {
1547 TimerMetadataReflowType::Incremental
1548 } else {
1549 TimerMetadataReflowType::FirstReflow
1550 },
1551 })
1552 }
1553
1554 fn clear_layout_trees_and_send_empty_display_list(
1556 &self,
1557 reflow_request: &ReflowRequest,
1558 ) -> Option<ReflowResult> {
1559 self.box_tree.borrow_mut().take();
1561 self.fragment_tree.borrow_mut().take();
1562 self.stacking_context_tree.borrow_mut().take();
1563
1564 let paint_info = PaintDisplayListInfo::new(
1566 reflow_request.viewport_details,
1567 Size2D::zero(),
1568 self.id.into(),
1569 reflow_request.epoch,
1570 AxesScrollSensitivity {
1571 x: ScrollType::InputEvents | ScrollType::Script,
1572 y: ScrollType::InputEvents | ScrollType::Script,
1573 },
1574 !self.have_ever_generated_display_list.get(),
1575 );
1576 let mut builder = webrender_api::DisplayListBuilder::new(paint_info.pipeline_id);
1577 builder.begin();
1578 let (_, empty_display_list) = builder.end();
1579
1580 self.paint_api
1581 .send_display_list(self.webview_id, &paint_info, empty_display_list);
1582 self.last_display_list_was_empty.set(true);
1583 self.have_ever_generated_display_list.set(true);
1584
1585 Some(ReflowResult {
1586 reflow_phases_run: ReflowPhasesRun::BuiltDisplayList,
1587 ..Default::default()
1588 })
1589 }
1590
1591 pub(crate) fn ensure_containing_block_calculation(&self) {
1592 if !self.need_containing_block_calculation.get() {
1593 return;
1594 }
1595 let fragment_tree = self.fragment_tree.borrow();
1596 fragment_tree.as_ref().expect("missing fragment tree").find(
1597 |fragment, _level, containing_block| {
1598 fragment.set_containing_block(containing_block);
1599 None::<()>
1600 },
1601 );
1602 self.need_containing_block_calculation.set(false)
1603 }
1604}
1605
1606fn get_ua_stylesheets(shared_lock: &SharedRwLock) -> Rc<UserAgentStylesheets> {
1607 thread_local! {
1611 static USER_AGENT_STYLESHEETS: OnceCell<Rc<UserAgentStylesheets>> = const { OnceCell::new() };
1612 }
1613
1614 fn parse_ua_stylesheet(
1615 shared_lock: &SharedRwLock,
1616 filename: &str,
1617 content: &[u8],
1618 ) -> DocumentStyleSheet {
1619 let url = Url::parse(&format!("chrome://resources/{filename}")).unwrap_or_else(|_| {
1620 panic!("Could not parse user stylesheet URL: {filename}");
1621 });
1622 DocumentStyleSheet(ServoArc::new(Stylesheet::from_bytes(
1623 content,
1624 url.into(),
1625 None,
1626 None,
1627 Origin::UserAgent,
1628 ServoArc::new(shared_lock.wrap(MediaList::empty())),
1629 shared_lock.clone(),
1630 None,
1631 None,
1632 QuirksMode::NoQuirks,
1633 )))
1634 }
1635
1636 USER_AGENT_STYLESHEETS.with(|user_stylesheets| {
1637 user_stylesheets
1638 .get_or_init(|| {
1639 let user_agent_stylesheets = vec![
1642 parse_ua_stylesheet(shared_lock, "user-agent.css", USER_AGENT_CSS),
1643 parse_ua_stylesheet(shared_lock, "servo.css", SERVO_CSS),
1644 parse_ua_stylesheet(
1645 shared_lock,
1646 "presentational-hints.css",
1647 PRESENTATIONAL_HINTS_CSS,
1648 ),
1649 ];
1650
1651 let html_mode_stylesheet =
1652 parse_ua_stylesheet(shared_lock, "html-mode.css", HTML_MODE_CSS);
1653
1654 let quirks_mode_stylesheet =
1655 parse_ua_stylesheet(shared_lock, "quirks-mode.css", QUIRKS_MODE_CSS);
1656
1657 Rc::new(UserAgentStylesheets {
1658 user_agent_stylesheets,
1659 html_mode_stylesheet,
1660 quirks_mode_stylesheet,
1661 })
1662 })
1663 .clone()
1664 })
1665}
1666
1667pub struct UserAgentStylesheets {
1669 pub user_agent_stylesheets: Vec<DocumentStyleSheet>,
1671 pub html_mode_stylesheet: DocumentStyleSheet,
1673 pub quirks_mode_stylesheet: DocumentStyleSheet,
1675}
1676
1677struct RegisteredPainterImpl {
1678 painter: Box<dyn Painter>,
1679 name: Atom,
1680 properties: FxHashMap<Atom, PropertyId>,
1682}
1683
1684impl SpeculativePainter for RegisteredPainterImpl {
1685 fn speculatively_draw_a_paint_image(
1686 &self,
1687 properties: Vec<(Atom, String)>,
1688 arguments: Vec<String>,
1689 ) {
1690 self.painter
1691 .speculatively_draw_a_paint_image(properties, arguments);
1692 }
1693}
1694
1695impl RegisteredSpeculativePainter for RegisteredPainterImpl {
1696 fn properties(&self) -> &FxHashMap<Atom, PropertyId> {
1697 &self.properties
1698 }
1699 fn name(&self) -> Atom {
1700 self.name.clone()
1701 }
1702}
1703
1704impl Painter for RegisteredPainterImpl {
1705 fn draw_a_paint_image(
1706 &self,
1707 size: Size2D<f32, CSSPixel>,
1708 device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
1709 properties: Vec<(Atom, String)>,
1710 arguments: Vec<String>,
1711 ) -> Result<DrawAPaintImageResult, PaintWorkletError> {
1712 self.painter
1713 .draw_a_paint_image(size, device_pixel_ratio, properties, arguments)
1714 }
1715}
1716
1717struct RegisteredPaintersImpl(HashMap<Atom, RegisteredPainterImpl>);
1718
1719impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1720 fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1721 self.0
1722 .get(name)
1723 .map(|painter| painter as &dyn RegisteredSpeculativePainter)
1724 }
1725}
1726
1727struct LayoutFontMetricsProvider(Arc<FontContext>);
1728
1729impl FontMetricsProvider for LayoutFontMetricsProvider {
1730 fn query_font_metrics(
1731 &self,
1732 _vertical: bool,
1733 font: &Font,
1734 base_size: CSSPixelLength,
1735 _flags: QueryFontMetricsFlags,
1736 ) -> FontMetrics {
1737 let font_context = &self.0;
1738 let font_group = self
1739 .0
1740 .font_group_with_size(ServoArc::new(font.clone()), base_size.into());
1741
1742 let Some(first_font_metrics) = font_group
1743 .first(font_context)
1744 .map(|font| font.metrics.clone())
1745 else {
1746 return Default::default();
1747 };
1748
1749 let x_height = Some(first_font_metrics.x_height)
1752 .filter(|x_height| !x_height.is_zero())
1753 .map(CSSPixelLength::from);
1754
1755 let zero_advance_measure = first_font_metrics
1756 .zero_horizontal_advance
1757 .or_else(|| {
1758 font_group
1759 .find_by_codepoint(font_context, '0', None, Language::UND)?
1760 .metrics
1761 .zero_horizontal_advance
1762 })
1763 .map(CSSPixelLength::from);
1764
1765 let ic_width = first_font_metrics
1766 .ic_horizontal_advance
1767 .or_else(|| {
1768 font_group
1769 .find_by_codepoint(font_context, '\u{6C34}', None, Language::UND)?
1770 .metrics
1771 .ic_horizontal_advance
1772 })
1773 .map(CSSPixelLength::from);
1774
1775 FontMetrics {
1776 x_height,
1777 zero_advance_measure,
1778 cap_height: None,
1779 ic_width,
1780 ascent: first_font_metrics.ascent.into(),
1781 script_percent_scale_down: None,
1782 script_script_percent_scale_down: None,
1783 }
1784 }
1785
1786 fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
1787 Length::new(match generic {
1788 GenericFontFamily::Monospace => pref!(fonts_default_monospace_size),
1789 _ => pref!(fonts_default_size),
1790 } as f32)
1791 .max(Length::new(0.0))
1792 }
1793}
1794
1795impl Debug for LayoutFontMetricsProvider {
1796 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1797 f.debug_tuple("LayoutFontMetricsProvider").finish()
1798 }
1799}
1800
1801struct SnapshotSetter<'dom> {
1802 elements_with_snapshot: Vec<ServoLayoutElement<'dom>>,
1803}
1804
1805impl SnapshotSetter<'_> {
1806 fn new(restyle: &mut ReflowRequestRestyle, snapshot_map: &mut SnapshotMap) -> Self {
1807 debug!("Draining restyles: {}", restyle.pending_restyles.len());
1808 let restyles = std::mem::take(&mut restyle.pending_restyles);
1809
1810 let elements_with_snapshot: Vec<_> = restyles
1811 .iter()
1812 .filter(|r| r.1.snapshot.is_some())
1813 .map(|r| unsafe { ServoLayoutNode::new(&r.0).as_element().unwrap() })
1814 .collect();
1815
1816 for (element, restyle) in restyles {
1817 let element = unsafe { ServoLayoutNode::new(&element).as_element().unwrap() };
1818
1819 let Some(mut style_data) = element
1822 .style_data()
1823 .map(|data| data.element_data.borrow_mut())
1824 else {
1825 element.unset_snapshot_flags();
1826 continue;
1827 };
1828
1829 debug!("Noting restyle for {:?}: {:?}", element, style_data);
1830 if let Some(s) = restyle.snapshot {
1831 element.set_has_snapshot();
1832 snapshot_map.insert(element.as_node().opaque(), s);
1833 }
1834
1835 style_data.hint.insert(restyle.hint);
1837 style_data.damage = restyle.damage;
1838 }
1839 Self {
1840 elements_with_snapshot,
1841 }
1842 }
1843}
1844
1845impl Drop for SnapshotSetter<'_> {
1846 fn drop(&mut self) {
1847 for element in &self.elements_with_snapshot {
1848 element.unset_snapshot_flags();
1849 }
1850 }
1851}
1852
1853bitflags! {
1854 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1855 pub struct ReflowPhases: u8 {
1856 const StackingContextTreeConstruction = 1 << 0;
1857 const DisplayListConstruction = 1 << 1;
1858 }
1859}
1860
1861impl ReflowPhases {
1862 fn necessary(reflow_goal: &ReflowGoal) -> Self {
1866 let is_inset_longhand = |longhand: LonghandId| {
1867 matches!(
1868 longhand,
1869 LonghandId::Top |
1870 LonghandId::Right |
1871 LonghandId::Bottom |
1872 LonghandId::Left |
1873 LonghandId::InsetInlineStart |
1874 LonghandId::InsetInlineEnd |
1875 LonghandId::InsetBlockStart |
1876 LonghandId::InsetBlockEnd
1877 )
1878 };
1879
1880 let is_inset_property =
1881 |property: NonCustomPropertyId| match property.longhand_or_shorthand() {
1882 Ok(longhand) => is_inset_longhand(longhand),
1883 Err(ShorthandId::All) => true,
1885 Err(shorthand) => shorthand.longhands().any(is_inset_longhand),
1886 };
1887
1888 match reflow_goal {
1889 ReflowGoal::LayoutQuery(query) => match query {
1890 QueryMsg::ResolvedStyleQuery(PropertyId::NonCustom(non_custom_property_id))
1893 if is_inset_property(*non_custom_property_id) =>
1894 {
1895 Self::StackingContextTreeConstruction
1896 },
1897 QueryMsg::ResolvedStyleQuery(_) => Self::empty(),
1898 QueryMsg::NodesFromPointQuery => {
1899 Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1900 },
1901 QueryMsg::BoxArea |
1902 QueryMsg::BoxAreas |
1903 QueryMsg::ElementsFromPoint |
1904 QueryMsg::FlushForUpdateTheRenderingQuery |
1905 QueryMsg::OffsetParentQuery |
1906 QueryMsg::ScrollingAreaOrOffsetQuery |
1907 QueryMsg::TextIndexQuery => Self::StackingContextTreeConstruction,
1908 QueryMsg::ClientRectQuery |
1909 QueryMsg::CurrentCSSZoomQuery |
1910 QueryMsg::EffectiveOverflow |
1911 QueryMsg::ElementInnerOuterTextQuery |
1912 QueryMsg::InnerWindowDimensionsQuery |
1913 QueryMsg::PaddingQuery |
1914 QueryMsg::ResolvedFontStyleQuery |
1915 QueryMsg::ScrollParentQuery |
1916 QueryMsg::StyleQuery => Self::empty(),
1917 },
1918 ReflowGoal::UpdateScrollNode(..) | ReflowGoal::UpdateTheRendering => {
1919 Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1920 },
1921 }
1922 }
1923}