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