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