1#![expect(unsafe_code)]
6
7use std::cell::{Cell, OnceCell, RefCell};
8use std::collections::{HashMap, VecDeque};
9use std::ffi::c_void;
10use std::fmt::Debug;
11use std::rc::Rc;
12use std::sync::{Arc, LazyLock};
13
14use app_units::Au;
15use bitflags::bitflags;
16use embedder_traits::{
17 EmbedderMsg, ScriptToEmbedderChan, Theme, UntrustedNodeAddress, ViewportDetails,
18};
19use euclid::{Point2D, Rect, Scale, Size2D};
20use fonts::{FontContext, FontContextWebFontMethods};
21use fonts_traits::{StylesheetWebFontLoadFinishedCallback, WebFontSetDifference};
22use icu_locid::subtags::Language;
23use layout_api::{
24 AxesOverflow, BoxAreaType, CSSPixelRectVec, DangerousStyleNode, IFrameSizes, Layout,
25 LayoutConfig, LayoutDamage, LayoutElement, LayoutFactory, LayoutNode, NodeRenderingType,
26 OffsetParentResponse, PhysicalSides, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest,
27 ReflowRequestRestyle, ReflowResult, ReflowStatistics, ScrollContainerQueryFlags,
28 ScrollContainerResponse, TrustedNodeAddress, with_layout_state,
29};
30use log::{debug, warn};
31use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
32use net_traits::image_cache::ImageCache;
33use paint_api::CrossProcessPaintApi;
34use paint_api::display_list::{AxesScrollSensitivity, PaintDisplayListInfo, ScrollType};
35use parking_lot::{Mutex, RwLock};
36use profile_traits::mem::{Report, ReportKind};
37use profile_traits::time::{
38 self as profile_time, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType,
39};
40use profile_traits::{path, time_profile};
41use rustc_hash::FxHashMap;
42use script::layout_dom::{
43 ServoDangerousStyleDocument, ServoDangerousStyleElement, ServoLayoutElement, ServoLayoutNode,
44};
45use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter, ScriptThreadMessage};
46use servo_arc::Arc as ServoArc;
47use servo_base::Epoch;
48use servo_base::id::{PipelineId, WebViewId};
49use servo_config::opts::{self, DiagnosticsLogging, DiagnosticsLoggingOption};
50use servo_config::pref;
51use servo_url::ServoUrl;
52use style::animation::DocumentAnimationSet;
53use style::context::{
54 QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
55};
56use style::device::Device;
57use style::device::servo::FontMetricsProvider;
58use style::dom::{OpaqueNode, ShowSubtreeDataAndPrimaryValues, TDocument, TElement, TNode};
59use style::font_metrics::FontMetrics;
60use style::global_style_data::GLOBAL_STYLE_DATA;
61use style::invalidation::element::restyle_hints::RestyleHint;
62use style::invalidation::stylesheets::StylesheetInvalidationSet;
63use style::media_queries::{MediaList, MediaType};
64use style::properties::style_structs::Font;
65use style::properties::{ComputedValues, LonghandId, NonCustomPropertyId, PropertyId, ShorthandId};
66use style::queries::values::PrefersColorScheme;
67use style::selector_parser::{PseudoElement, SnapshotMap};
68use style::servo::media_features::PointerCapabilities;
69use style::shared_lock::{SharedRwLock, 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::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 needs_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_node: Point2D<Au, CSSPixel>,
574 ) -> Option<usize> {
575 with_layout_state(|| {
576 let node = unsafe { ServoLayoutNode::new(&node) };
577 let stacking_context_tree = self.stacking_context_tree.borrow_mut();
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_node,
583 )
584 })
585 }
586
587 #[servo_tracing::instrument(skip_all)]
588 fn query_elements_from_point(
589 &self,
590 point: webrender_api::units::LayoutPoint,
591 ) -> Vec<layout_api::ElementsFromPointResult> {
592 with_layout_state(|| {
593 self.stacking_context_tree
594 .borrow_mut()
595 .as_mut()
596 .map(|tree| HitTest::run(tree, point))
597 .unwrap_or_default()
598 })
599 }
600
601 #[servo_tracing::instrument(skip_all)]
602 fn query_effective_overflow(&self, node: TrustedNodeAddress) -> Option<AxesOverflow> {
603 with_layout_state(|| {
604 let node = unsafe { ServoLayoutNode::new(&node) };
605 process_effective_overflow_query(node)
606 })
607 }
608
609 fn exit_now(&mut self) {}
610
611 fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps) {
612 let formatted_url = &format!("url({})", self.url);
614 reports.push(Report {
615 path: path![formatted_url, "layout-thread", "display-list"],
616 kind: ReportKind::ExplicitJemallocHeapSize,
617 size: 0,
618 });
619
620 reports.push(Report {
621 path: path![formatted_url, "layout-thread", "stylist"],
622 kind: ReportKind::ExplicitJemallocHeapSize,
623 size: self.stylist.size_of(ops),
624 });
625
626 reports.push(Report {
627 path: path![formatted_url, "layout-thread", "font-context"],
628 kind: ReportKind::ExplicitJemallocHeapSize,
629 size: self.font_context.conditional_size_of(ops),
630 });
631
632 reports.push(Report {
633 path: path![formatted_url, "layout-thread", "box-tree"],
634 kind: ReportKind::ExplicitJemallocHeapSize,
635 size: self
636 .box_tree
637 .borrow()
638 .as_ref()
639 .map_or(0, |tree| tree.conditional_size_of(ops)),
640 });
641
642 reports.push(Report {
643 path: path![formatted_url, "layout-thread", "fragment-tree"],
644 kind: ReportKind::ExplicitJemallocHeapSize,
645 size: self
646 .fragment_tree
647 .borrow()
648 .as_ref()
649 .map(|tree| tree.conditional_size_of(ops))
650 .unwrap_or_default(),
651 });
652
653 reports.push(Report {
654 path: path![formatted_url, "layout-thread", "stacking-context-tree"],
655 kind: ReportKind::ExplicitJemallocHeapSize,
656 size: self.stacking_context_tree.size_of(ops),
657 });
658
659 reports.extend(self.image_cache.memory_reports(formatted_url, ops));
660 }
661
662 fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
663 self.stylist.set_quirks_mode(quirks_mode);
664 }
665
666 fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult> {
667 time_profile!(
668 profile_time::ProfilerCategory::Layout,
669 self.profiler_metadata(),
670 self.time_profiler_chan.clone(),
671 || with_layout_state(|| self.handle_reflow(reflow_request)),
672 )
673 }
674
675 fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails) {
676 with_layout_state(|| {
677 if self.stacking_context_tree.borrow().is_some() &&
678 !self.need_new_stacking_context_tree.get()
679 {
680 return;
681 }
682 self.build_stacking_context_tree(viewport_details);
683 })
684 }
685
686 fn register_paint_worklet_modules(
687 &mut self,
688 _name: Atom,
689 _properties: Vec<Atom>,
690 _painter: Box<dyn Painter>,
691 ) {
692 }
693
694 fn set_scroll_offsets_from_renderer(
695 &mut self,
696 scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
697 ) {
698 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
699 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
700 warn!("Received scroll offsets before finishing layout.");
701 return;
702 };
703
704 stacking_context_tree
705 .paint_info
706 .scroll_tree
707 .set_all_scroll_offsets(scroll_states);
708 }
709
710 fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D> {
711 self.stacking_context_tree
712 .borrow_mut()
713 .as_mut()
714 .and_then(|tree| tree.paint_info.scroll_tree.scroll_offset(id))
715 }
716
717 fn needs_new_display_list(&self) -> bool {
718 self.need_new_display_list.get()
719 }
720
721 fn set_needs_new_display_list(&self) {
722 self.need_new_display_list.set(true);
723 }
724
725 fn stylist_mut(&mut self) -> &mut Stylist {
727 &mut self.stylist
728 }
729
730 fn set_accessibility_active(&self, active: bool, epoch: Epoch) {
731 self.accessibility_active.set(active);
732 if !active {
733 self.accessibility_tree.replace(None);
734 return;
735 }
736
737 self.set_needs_accessibility_update();
738 let mut accessibility_tree = self.accessibility_tree.borrow_mut();
739 if accessibility_tree.is_some() {
740 return;
741 }
742 *accessibility_tree = Some(AccessibilityTree::new(self.id.into(), epoch));
743 }
744
745 fn accessibility_active(&self) -> bool {
746 self.accessibility_active.get()
747 }
748
749 fn needs_accessibility_update(&self) -> bool {
750 self.needs_accessibility_update.get()
751 }
752
753 fn set_needs_accessibility_update(&self) {
754 self.needs_accessibility_update.set(true);
755 }
756}
757
758impl LayoutThread {
759 fn new(config: LayoutConfig) -> LayoutThread {
760 config
762 .paint_api
763 .send_initial_transaction(config.webview_id, config.id.into());
764
765 let mut font = Font::initial_values();
766 let default_font_size = pref!(fonts_default_size);
767 font.font_size = FontSize {
768 computed_size: NonNegativeLength::new(default_font_size as f32),
769 used_size: NonNegativeLength::new(default_font_size as f32),
770 keyword_info: KeywordInfo::medium(),
771 };
772
773 let device = Device::new(
776 MediaType::screen(),
777 QuirksMode::NoQuirks,
778 config.viewport_details.size,
779 config.viewport_details.device_size.cast_unit(),
780 Scale::new(config.viewport_details.hidpi_scale_factor.get()),
781 Box::new(LayoutFontMetricsProvider(config.font_context.clone())),
782 ComputedValues::initial_values_with_font_override(font),
783 config.theme.into(),
784 PointerCapabilities::default(),
785 PointerCapabilities::default(),
786 );
787
788 let locked_script_channel = Mutex::new(config.script_chan.clone());
789 let pipeline_id = config.id;
790 let web_font_finished_loading_callback = move |event| {
791 let _ = locked_script_channel
792 .lock()
793 .send(ScriptThreadMessage::WebFontLoadFinished(pipeline_id, event));
794 };
795
796 LayoutThread {
797 id: config.id,
798 webview_id: config.webview_id,
799 url: config.url,
800 is_iframe: config.is_iframe,
801 time_profiler_chan: config.time_profiler_chan,
802 embedder_chan: config.embedder_chan.clone(),
803 registered_painters: RegisteredPaintersImpl(Default::default()),
804 image_cache: config.image_cache,
805 font_context: config.font_context,
806 have_added_user_agent_stylesheets: false,
807 have_ever_generated_display_list: Cell::new(false),
808 last_display_list_was_empty: Cell::new(true),
809 device_has_changed: false,
810 need_containing_block_calculation: Cell::new(false),
811 need_new_display_list: Cell::new(false),
812 need_new_stacking_context_tree: Cell::new(false),
813 box_tree: Default::default(),
814 fragment_tree: Default::default(),
815 stacking_context_tree: Default::default(),
816 paint_api: config.paint_api,
817 stylist: Stylist::new(device, QuirksMode::NoQuirks),
818 resolved_images_cache: Default::default(),
819 debug: opts::get().debug.clone(),
820 previously_highlighted_dom_node: Cell::new(None),
821 paint_timing_handler: Default::default(),
822 user_stylesheets: config.user_stylesheets,
823 accessibility_active: Cell::new(false),
824 accessibility_tree: Default::default(),
825 needs_accessibility_update: Cell::new(false),
826 web_font_finished_loading_callback: Arc::new(web_font_finished_loading_callback)
827 as StylesheetWebFontLoadFinishedCallback,
828 }
829 }
830
831 fn build_shared_style_context<'a>(
832 &'a self,
833 guards: StylesheetGuards<'a>,
834 snapshot_map: &'a SnapshotMap,
835 animation_timeline_value: f64,
836 animations: &DocumentAnimationSet,
837 traversal_flags: TraversalFlags,
838 ) -> SharedStyleContext<'a> {
839 SharedStyleContext {
840 stylist: &self.stylist,
841 options: GLOBAL_STYLE_DATA.options.clone(),
842 guards,
843 visited_styles_enabled: false,
844 animations: animations.clone(),
845 registered_speculative_painters: &self.registered_painters,
846 current_time_for_animations: animation_timeline_value,
847 traversal_flags,
848 snapshot_map,
849 }
850 }
851
852 fn can_skip_reflow_request_entirely(&self, reflow_request: &ReflowRequest) -> bool {
856 if reflow_request.restyle.is_some() {
858 return false;
859 }
860 if self.fragment_tree.borrow().is_none() {
862 return false;
863 }
864 if self.needs_accessibility_update() {
866 return false;
867 }
868
869 let necessary_phases = ReflowPhases::necessary(&reflow_request.reflow_goal);
872 if necessary_phases.is_empty() {
873 return true;
874 }
875
876 if necessary_phases == ReflowPhases::StackingContextTreeConstruction {
879 return self.stacking_context_tree.borrow().is_some() &&
880 !self.need_new_stacking_context_tree.get();
881 }
882
883 assert_eq!(
886 necessary_phases,
887 ReflowPhases::StackingContextTreeConstruction | ReflowPhases::DisplayListConstruction
888 );
889 !self.need_new_display_list.get()
890 }
891
892 fn maybe_print_reflow_event(&self, reflow_request: &ReflowRequest) {
893 if !self
894 .debug
895 .is_enabled(DiagnosticsLoggingOption::RelayoutEvent)
896 {
897 return;
898 }
899
900 println!(
901 "**** Reflow({}) => {:?}, {:?}",
902 self.id,
903 reflow_request.reflow_goal,
904 reflow_request
905 .restyle
906 .as_ref()
907 .map(|restyle| restyle.reason)
908 .unwrap_or_default()
909 );
910 }
911
912 fn handle_update_scroll_node_request(&self, reflow_request: &ReflowRequest) -> bool {
915 if let ReflowGoal::UpdateScrollNode(external_scroll_id, offset) = reflow_request.reflow_goal
916 {
917 self.set_scroll_offset_from_script(external_scroll_id, offset)
918 } else {
919 false
920 }
921 }
922
923 fn handle_accessibility_tree_update(
924 &self,
925 root_element: &ServoLayoutNode,
926 reflow_request: &mut ReflowRequest,
927 reflow_statistics: &mut ReflowStatistics,
928 ) -> bool {
929 if !self.needs_accessibility_update() {
930 return false;
931 }
932 let mut accessibility_tree = self.accessibility_tree.borrow_mut();
933 let Some(accessibility_tree) = accessibility_tree.as_mut() else {
934 return false;
935 };
936 let Some(damage) = &reflow_request.accessibility_damage else {
937 return false;
938 };
939
940 let accessibility_tree = &mut *accessibility_tree;
941 let rooted_nodes =
942 std::mem::take(&mut reflow_request.rooted_nodes_for_accessibility_integrity_check);
943
944 let damage: VecDeque<_> = damage
945 .iter()
946 .map(|(address, damage)| unsafe { (ServoLayoutNode::new(address), *damage) })
947 .collect();
948
949 let (tree_update, counters) =
950 accessibility_tree.update_tree(root_element, damage, rooted_nodes);
951 if let Some(tree_update) = tree_update {
952 let _ = self
956 .embedder_chan
957 .send(EmbedderMsg::AccessibilityTreeUpdate(
958 self.webview_id,
959 tree_update,
960 accessibility_tree.embedder_epoch(),
961 ));
962 }
963
964 reflow_statistics.nodes_updated_from_dom = counters.nodes_updated_from_dom;
965 reflow_statistics.nodes_updated_from_tree = counters.nodes_updated_from_tree;
966 reflow_statistics.nodes_in_tree_update = counters.nodes_in_tree_update;
967
968 self.needs_accessibility_update.set(false);
969 true
970 }
971
972 #[servo_tracing::instrument(
974 skip_all,
975 fields(goal = tracing::field::debug(&reflow_request.reflow_goal))
976 )]
977 fn handle_reflow(&mut self, mut reflow_request: ReflowRequest) -> Option<ReflowResult> {
978 self.maybe_print_reflow_event(&reflow_request);
979
980 if self.can_skip_reflow_request_entirely(&reflow_request) {
981 return self
983 .handle_update_scroll_node_request(&reflow_request)
984 .then(|| ReflowResult {
985 reflow_phases_run: ReflowPhasesRun::UpdatedScrollNodeOffset,
986 ..Default::default()
987 });
988 }
989
990 let document = unsafe { ServoLayoutNode::new(&reflow_request.document) };
991 let document = unsafe { document.dangerous_style_node() }
992 .as_document()
993 .unwrap();
994 let Some(root_element) = document.root_element() else {
995 if !self.last_display_list_was_empty.get() {
996 return self.clear_layout_trees_and_send_empty_display_list(&reflow_request);
997 }
998 debug!("layout: No root node: bailing");
999 return None;
1000 };
1001
1002 let image_resolver = Arc::new(ImageResolver {
1003 origin: reflow_request.origin.clone(),
1004 image_cache: self.image_cache.clone(),
1005 resolved_images_cache: self.resolved_images_cache.clone(),
1006 pending_images: Mutex::default(),
1007 pending_rasterization_images: Mutex::default(),
1008 pending_svg_elements_for_serialization: Mutex::default(),
1009 animating_images: reflow_request.animating_images.clone(),
1010 animation_timeline_value: reflow_request.animation_timeline_value,
1011 });
1012 let mut reflow_statistics = Default::default();
1013
1014 let (mut reflow_phases_run, iframe_sizes, changed_web_fonts) = self
1015 .restyle_and_build_trees(&mut reflow_request, document, root_element, &image_resolver);
1016 if self.build_stacking_context_tree_for_reflow(&reflow_request) {
1017 reflow_phases_run.insert(ReflowPhasesRun::BuiltStackingContextTree);
1018 }
1019 if self.build_display_list(&reflow_request, &image_resolver, &mut reflow_statistics) {
1020 reflow_phases_run.insert(ReflowPhasesRun::BuiltDisplayList);
1021 }
1022 if self.handle_update_scroll_node_request(&reflow_request) {
1023 reflow_phases_run.insert(ReflowPhasesRun::UpdatedScrollNodeOffset);
1024 }
1025 if self.handle_accessibility_tree_update(
1026 &root_element.as_node(),
1027 &mut reflow_request,
1028 &mut reflow_statistics,
1029 ) {
1030 reflow_phases_run.insert(ReflowPhasesRun::UpdatedAccessibilityTree);
1031 }
1032
1033 if self.debug.is_enabled(DiagnosticsLoggingOption::FlowTree) &&
1034 reflow_phases_run.contains(ReflowPhasesRun::RanLayout) &&
1035 let Some(fragment_tree) = &*self.fragment_tree.borrow()
1036 {
1037 fragment_tree.print();
1038 }
1039
1040 let pending_images = std::mem::take(&mut *image_resolver.pending_images.lock());
1041 let pending_rasterization_images =
1042 std::mem::take(&mut *image_resolver.pending_rasterization_images.lock());
1043 let pending_svg_elements_for_serialization =
1044 std::mem::take(&mut *image_resolver.pending_svg_elements_for_serialization.lock());
1045
1046 let (lcp_candidate, lcp_node_address) = self
1047 .paint_timing_handler
1048 .borrow()
1049 .as_ref()
1050 .map(|handler| {
1051 (
1052 handler.largest_contentful_paint_candidate(),
1053 handler
1054 .lcp_node()
1055 .map(|node| UntrustedNodeAddress(node.id() as *const c_void)),
1056 )
1057 })
1058 .unwrap_or_default();
1059
1060 Some(ReflowResult {
1061 reflow_phases_run,
1062 pending_images,
1063 pending_rasterization_images,
1064 pending_svg_elements_for_serialization,
1065 iframe_sizes: Some(iframe_sizes),
1066 reflow_statistics,
1067 changed_web_fonts,
1068 lcp_candidate,
1069 lcp_node_address,
1070 })
1071 }
1072
1073 #[servo_tracing::instrument(skip_all)]
1074 fn prepare_stylist_for_reflow<'dom>(
1075 &mut self,
1076 reflow_request: &ReflowRequest,
1077 document: ServoDangerousStyleDocument<'dom>,
1078 guards: &StylesheetGuards,
1079 ua_stylesheets: &UserAgentStylesheets,
1080 ) -> StylistStylesheetUpdate {
1081 let need_user_agent_stylesheet_addition = !self.have_added_user_agent_stylesheets;
1082 if need_user_agent_stylesheet_addition {
1083 for stylesheet in &ua_stylesheets.user_agent_stylesheets {
1084 self.stylist
1085 .append_stylesheet(stylesheet.clone(), guards.ua_or_user);
1086 }
1087
1088 if document.is_html_document() {
1089 self.stylist.append_stylesheet(
1090 ua_stylesheets.html_mode_stylesheet.clone(),
1091 guards.ua_or_user,
1092 );
1093 }
1094
1095 for user_stylesheet in self.user_stylesheets.iter() {
1096 self.stylist
1097 .append_stylesheet(user_stylesheet.clone(), guards.ua_or_user);
1098 }
1099
1100 if self.stylist.quirks_mode() == QuirksMode::Quirks {
1101 self.stylist.append_stylesheet(
1102 ua_stylesheets.quirks_mode_stylesheet.clone(),
1103 guards.ua_or_user,
1104 );
1105 }
1106 self.have_added_user_agent_stylesheets = true;
1107 }
1108
1109 if reflow_request.stylesheets_changed() {
1110 self.stylist
1111 .force_stylesheet_origins_dirty(Origin::Author.into());
1112 }
1113
1114 document.flush_shadow_root_stylesheets_if_necessary(&mut self.stylist, guards.author);
1115
1116 let invalidation_set = self.stylist.flush(guards);
1117
1118 let changed_web_fonts =
1119 if need_user_agent_stylesheet_addition || reflow_request.stylesheets_changed() {
1120 self.font_context.invalidate_font_feature_values_map();
1121 self.font_context.rebuild_font_face_set(
1124 self.webview_id,
1125 &self.stylist,
1126 guards,
1127 self.web_font_finished_loading_callback.clone(),
1128 &reflow_request.document_context,
1129 )
1130 } else {
1131 WebFontSetDifference::default()
1132 };
1133
1134 StylistStylesheetUpdate {
1135 invalidation_set,
1136 changed_web_fonts,
1137 }
1138 }
1139
1140 #[servo_tracing::instrument(skip_all)]
1141 fn restyle_and_build_trees(
1142 &mut self,
1143 reflow_request: &mut ReflowRequest,
1144 document: ServoDangerousStyleDocument<'_>,
1145 root_element: ServoLayoutElement<'_>,
1146 image_resolver: &Arc<ImageResolver>,
1147 ) -> (ReflowPhasesRun, IFrameSizes, WebFontSetDifference) {
1148 let mut snapshot_map = SnapshotMap::new();
1149 let _snapshot_setter = match reflow_request.restyle.as_mut() {
1150 Some(restyle) => SnapshotSetter::new(restyle, &mut snapshot_map),
1151 None => return Default::default(),
1152 };
1153
1154 let shared_locks = document.shared_style_locks();
1155 let user_agent_stylesheets = get_ua_stylesheets(&shared_locks.ua_or_user);
1156 let guards = StylesheetGuards {
1157 author: &shared_locks.author.read(),
1158 ua_or_user: &shared_locks.ua_or_user.read(),
1159 };
1160
1161 let rayon_pool = STYLE_THREAD_POOL.lock();
1162 let rayon_pool = rayon_pool.pool();
1163 let rayon_pool = rayon_pool.as_ref();
1164
1165 let device_has_changed = std::mem::replace(&mut self.device_has_changed, false);
1166 let dangerous_root_element = unsafe { root_element.dangerous_style_element() };
1167 if device_has_changed {
1168 let sheet_origins_affected_by_device_change = self
1169 .stylist
1170 .media_features_change_changed_style(&guards, self.device());
1171 self.stylist
1172 .force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
1173
1174 if let Some(mut data) = dangerous_root_element.mutate_data() {
1175 data.hint.insert(RestyleHint::recascade_subtree());
1176 }
1177 }
1178
1179 let stylist_update = self.prepare_stylist_for_reflow(
1180 reflow_request,
1181 document,
1182 &guards,
1183 &user_agent_stylesheets,
1184 );
1185 stylist_update
1186 .invalidation_set
1187 .process_style(dangerous_root_element, Some(&snapshot_map));
1188
1189 if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
1190 self.need_new_display_list.set(true);
1193 }
1194
1195 let layout_context = LayoutContext {
1196 style_context: self.build_shared_style_context(
1197 guards,
1198 &snapshot_map,
1199 reflow_request.animation_timeline_value,
1200 &reflow_request.animations,
1201 match reflow_request.stylesheets_changed() {
1202 true => TraversalFlags::ForCSSRuleChanges,
1203 false => TraversalFlags::empty(),
1204 },
1205 ),
1206 font_context: self.font_context.clone(),
1207 iframe_sizes: Mutex::default(),
1208 allow_parallel_layout: rayon_pool.is_some(),
1209 image_resolver: image_resolver.clone(),
1210 painter_id: self.webview_id.into(),
1211 parallelism_job_count_minimum: pref!(layout_parallelism_job_count_minimum) as usize,
1212 parallelism_job_size_minimum: pref!(layout_parallelism_job_size_minimum) as usize,
1213 device_size: reflow_request.viewport_details.device_size.cast_unit(),
1214 };
1215
1216 let restyle = reflow_request
1217 .restyle
1218 .as_ref()
1219 .expect("Should not get here if there is not restyle.");
1220
1221 let recalc_style_traversal;
1222 let dirty_root;
1223 {
1224 let _span = profile_traits::trace_span!("Styling").entered();
1225
1226 let original_dirty_root = unsafe {
1227 ServoLayoutNode::new(&restyle.dirty_root.unwrap())
1228 .as_element()
1229 .unwrap()
1230 .dangerous_style_element()
1231 };
1232
1233 recalc_style_traversal = RecalcStyle::new(&layout_context);
1234 let token = {
1235 let shared = DomTraversal::<ServoDangerousStyleElement>::shared_context(
1236 &recalc_style_traversal,
1237 );
1238 RecalcStyle::pre_traverse(original_dirty_root, shared)
1239 };
1240
1241 if !token.should_traverse() {
1242 layout_context.style_context.stylist.rule_tree().maybe_gc();
1243 return Default::default();
1244 }
1245
1246 dirty_root = driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
1247 }
1248
1249 let root_node = root_element.as_node();
1250 let damage_from_environment = if device_has_changed {
1251 LayoutDamage::Relayout
1252 } else {
1253 LayoutDamage::empty()
1254 };
1255
1256 let mut box_tree = self.box_tree.borrow_mut();
1257 let mut layout_roots = Vec::new();
1258 let damage = {
1259 let box_tree = &mut *box_tree;
1260 let mut compute_damage_and_build_box_tree = || {
1261 compute_damage_and_rebuild_box_tree(
1262 box_tree,
1263 &layout_context,
1264 dirty_root.layout_node(),
1265 root_node,
1266 damage_from_environment,
1267 &mut layout_roots,
1268 )
1269 };
1270
1271 if let Some(pool) = rayon_pool {
1272 pool.install(compute_damage_and_build_box_tree)
1273 } else {
1274 compute_damage_and_build_box_tree()
1275 }
1276 };
1277
1278 if damage.contains(LayoutDamage::RebuildStackingContextTree) {
1279 self.need_new_stacking_context_tree.set(true);
1280 }
1281 if damage.contains(LayoutDamage::Repaint) {
1282 self.need_new_display_list.set(true);
1283 }
1284
1285 if !damage.contains(LayoutDamage::Relayout) {
1286 if damage.contains(LayoutDamage::RecalculateOverflow) {
1287 assert!(self.need_new_display_list.get());
1288 assert!(self.need_new_stacking_context_tree.get());
1289 self.fragment_tree
1290 .borrow()
1291 .as_ref()
1292 .expect("Should always have a FragmentTree when layout unnecessary")
1293 .clear_scrollable_overflow();
1294 }
1295
1296 if !damage.contains(LayoutDamage::DescendantCollectedAsLayoutRoot) {
1297 layout_context.style_context.stylist.rule_tree().maybe_gc();
1298 return (
1299 ReflowPhasesRun::empty(),
1300 IFrameSizes::default(),
1301 stylist_update.changed_web_fonts,
1302 );
1303 }
1304
1305 debug_assert!(!layout_roots.is_empty());
1306 if layout_roots
1307 .iter()
1308 .all(|layout_root| layout_root.try_layout(&layout_context))
1309 {
1310 return (
1311 ReflowPhasesRun::RanLayout,
1312 std::mem::take(&mut *layout_context.iframe_sizes.lock()),
1313 stylist_update.changed_web_fonts,
1314 );
1315 }
1316
1317 for layout_root in layout_roots {
1322 layout_root.handle_failed_layout_root_layout();
1323 }
1324 }
1325
1326 let box_tree = &*box_tree;
1327 let viewport_size = self.stylist.device().au_viewport_size();
1328 let run_layout = || {
1329 box_tree
1330 .as_ref()
1331 .unwrap()
1332 .layout(recalc_style_traversal.context(), viewport_size)
1333 };
1334 let fragment_tree = Rc::new(if let Some(pool) = rayon_pool {
1335 pool.install(run_layout)
1336 } else {
1337 run_layout()
1338 });
1339
1340 *self.fragment_tree.borrow_mut() = Some(fragment_tree);
1341
1342 if self.debug.is_enabled(DiagnosticsLoggingOption::StyleTree) {
1343 println!(
1344 "{:?}",
1345 ShowSubtreeDataAndPrimaryValues(dangerous_root_element.as_node())
1346 );
1347 }
1348 if self.debug.is_enabled(DiagnosticsLoggingOption::RuleTree) {
1349 recalc_style_traversal
1350 .context()
1351 .style_context
1352 .stylist
1353 .rule_tree()
1354 .dump_stdout(&layout_context.style_context.guards);
1355 }
1356
1357 layout_context.style_context.stylist.rule_tree().maybe_gc();
1359
1360 let mut iframe_sizes = layout_context.iframe_sizes.lock();
1361 (
1362 ReflowPhasesRun::RanLayout,
1363 std::mem::take(&mut *iframe_sizes),
1364 stylist_update.changed_web_fonts,
1365 )
1366 }
1367
1368 fn build_stacking_context_tree_for_reflow(&self, reflow_request: &ReflowRequest) -> bool {
1369 if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1370 .contains(ReflowPhases::StackingContextTreeConstruction)
1371 {
1372 return false;
1373 }
1374 if !self.need_new_stacking_context_tree.get() {
1375 return false;
1376 }
1377
1378 self.build_stacking_context_tree(reflow_request.viewport_details)
1379 }
1380
1381 #[servo_tracing::instrument(name = "Stacking Context Tree Construction", skip_all)]
1382 fn build_stacking_context_tree(&self, viewport_details: ViewportDetails) -> bool {
1383 let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1384 return false;
1385 };
1386
1387 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1388 let old_scroll_offsets = stacking_context_tree
1389 .as_ref()
1390 .map(|tree| tree.paint_info.scroll_tree.scroll_offsets());
1391
1392 self.need_containing_block_calculation.set(false);
1394
1395 let mut new_stacking_context_tree = StackingContextTree::new(
1399 fragment_tree,
1400 viewport_details,
1401 self.id.into(),
1402 !self.have_ever_generated_display_list.get(),
1403 &self.debug,
1404 );
1405
1406 if let Some(old_scroll_offsets) = old_scroll_offsets {
1410 new_stacking_context_tree
1411 .paint_info
1412 .scroll_tree
1413 .set_all_scroll_offsets(&old_scroll_offsets);
1414 }
1415
1416 if self.debug.is_enabled(DiagnosticsLoggingOption::ScrollTree) {
1417 new_stacking_context_tree
1418 .paint_info
1419 .scroll_tree
1420 .debug_print();
1421 }
1422
1423 *stacking_context_tree = Some(new_stacking_context_tree);
1424
1425 self.need_new_stacking_context_tree.set(false);
1427 assert!(self.need_new_display_list.get());
1428
1429 true
1430 }
1431
1432 #[servo_tracing::instrument(name = "Display List Construction", skip_all)]
1435 fn build_display_list(
1436 &self,
1437 reflow_request: &ReflowRequest,
1438 image_resolver: &Arc<ImageResolver>,
1439 reflow_statistics: &mut ReflowStatistics,
1440 ) -> bool {
1441 if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1442 .contains(ReflowPhases::DisplayListConstruction)
1443 {
1444 return false;
1445 }
1446 let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1447 return false;
1448 };
1449 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1450 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1451 return false;
1452 };
1453
1454 if !self.need_new_display_list.get() {
1458 return false;
1459 }
1460
1461 stacking_context_tree.paint_info.epoch = reflow_request.epoch;
1464
1465 let mut paint_timing_handler = self.paint_timing_handler.borrow_mut();
1466 let paint_timing_handler = match paint_timing_handler.as_mut() {
1468 Some(paint_timing_handler) => paint_timing_handler,
1469 None => {
1470 *paint_timing_handler = Some(PaintTimingHandler::new(
1471 stacking_context_tree
1472 .paint_info
1473 .viewport_details
1474 .layout_size(),
1475 ));
1476 paint_timing_handler.as_mut().unwrap()
1477 },
1478 };
1479
1480 let built_display_list = DisplayListBuilder::build(
1481 stacking_context_tree,
1482 fragment_tree,
1483 image_resolver.clone(),
1484 self.device().device_pixel_ratio(),
1485 reflow_request.highlighted_dom_node,
1486 &self.debug,
1487 paint_timing_handler,
1488 reflow_statistics,
1489 );
1490 self.paint_api.send_display_list(
1491 self.webview_id,
1492 &stacking_context_tree.paint_info,
1493 built_display_list,
1494 );
1495
1496 if paint_timing_handler.did_lcp_candidate_update() &&
1497 let Some(lcp_candidate) = paint_timing_handler.largest_contentful_paint_candidate()
1498 {
1499 self.paint_api.send_lcp_candidate(
1500 lcp_candidate,
1501 self.webview_id,
1502 self.id,
1503 stacking_context_tree.paint_info.epoch,
1504 );
1505 paint_timing_handler.unset_lcp_candidate_updated();
1506 }
1507
1508 let (keys, instance_keys) = self
1509 .font_context
1510 .collect_unused_webrender_resources(false );
1511 self.paint_api
1512 .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys);
1513 self.last_display_list_was_empty.set(false);
1514 self.have_ever_generated_display_list.set(true);
1515 self.need_new_display_list.set(false);
1516 self.previously_highlighted_dom_node
1517 .set(reflow_request.highlighted_dom_node);
1518 true
1519 }
1520
1521 fn set_scroll_offset_from_script(
1522 &self,
1523 external_scroll_id: ExternalScrollId,
1524 offset: LayoutVector2D,
1525 ) -> bool {
1526 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1527 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1528 return false;
1529 };
1530
1531 if let Some(offset) = stacking_context_tree
1532 .paint_info
1533 .scroll_tree
1534 .set_scroll_offset_for_node_with_external_scroll_id(
1535 external_scroll_id,
1536 offset,
1537 ScrollType::Script,
1538 )
1539 {
1540 self.paint_api.scroll_node_by_delta(
1541 self.webview_id,
1542 self.id.into(),
1543 offset,
1544 external_scroll_id,
1545 );
1546 true
1547 } else {
1548 false
1549 }
1550 }
1551
1552 fn profiler_metadata(&self) -> Option<TimerMetadata> {
1554 Some(TimerMetadata {
1555 url: self.url.to_string(),
1556 iframe: if self.is_iframe {
1557 TimerMetadataFrameType::IFrame
1558 } else {
1559 TimerMetadataFrameType::RootWindow
1560 },
1561 incremental: if self.have_ever_generated_display_list.get() {
1562 TimerMetadataReflowType::Incremental
1563 } else {
1564 TimerMetadataReflowType::FirstReflow
1565 },
1566 })
1567 }
1568
1569 fn clear_layout_trees_and_send_empty_display_list(
1571 &self,
1572 reflow_request: &ReflowRequest,
1573 ) -> Option<ReflowResult> {
1574 self.box_tree.borrow_mut().take();
1576 self.fragment_tree.borrow_mut().take();
1577 self.stacking_context_tree.borrow_mut().take();
1578
1579 let paint_info = PaintDisplayListInfo::new(
1581 reflow_request.viewport_details,
1582 Size2D::zero(),
1583 self.id.into(),
1584 reflow_request.epoch,
1585 AxesScrollSensitivity {
1586 x: ScrollType::InputEvents | ScrollType::Script,
1587 y: ScrollType::InputEvents | ScrollType::Script,
1588 },
1589 !self.have_ever_generated_display_list.get(),
1590 );
1591 let mut builder = webrender_api::DisplayListBuilder::new(paint_info.pipeline_id);
1592 builder.begin();
1593 let (_, empty_display_list) = builder.end();
1594
1595 self.paint_api
1596 .send_display_list(self.webview_id, &paint_info, empty_display_list);
1597 self.last_display_list_was_empty.set(true);
1598 self.have_ever_generated_display_list.set(true);
1599
1600 Some(ReflowResult {
1601 reflow_phases_run: ReflowPhasesRun::BuiltDisplayList,
1602 ..Default::default()
1603 })
1604 }
1605
1606 pub(crate) fn ensure_containing_block_calculation(&self) {
1607 if !self.need_containing_block_calculation.get() {
1608 return;
1609 }
1610 let fragment_tree = self.fragment_tree.borrow();
1611 fragment_tree.as_ref().expect("missing fragment tree").find(
1612 |fragment, _level, containing_block| {
1613 fragment.set_containing_block(containing_block);
1614 None::<()>
1615 },
1616 );
1617 self.need_containing_block_calculation.set(false)
1618 }
1619}
1620
1621fn get_ua_stylesheets(shared_lock: &SharedRwLock) -> Rc<UserAgentStylesheets> {
1622 thread_local! {
1626 static USER_AGENT_STYLESHEETS: OnceCell<Rc<UserAgentStylesheets>> = const { OnceCell::new() };
1627 }
1628
1629 fn parse_ua_stylesheet(
1630 shared_lock: &SharedRwLock,
1631 filename: &str,
1632 content: &[u8],
1633 ) -> DocumentStyleSheet {
1634 let url = Url::parse(&format!("chrome://resources/{filename}")).unwrap_or_else(|_| {
1635 panic!("Could not parse user stylesheet URL: {filename}");
1636 });
1637 DocumentStyleSheet(ServoArc::new(Stylesheet::from_bytes(
1638 content,
1639 url.into(),
1640 None,
1641 None,
1642 Origin::UserAgent,
1643 ServoArc::new(shared_lock.wrap(MediaList::empty())),
1644 shared_lock.clone(),
1645 None,
1646 None,
1647 QuirksMode::NoQuirks,
1648 )))
1649 }
1650
1651 USER_AGENT_STYLESHEETS.with(|user_stylesheets| {
1652 user_stylesheets
1653 .get_or_init(|| {
1654 let user_agent_stylesheets = vec![
1657 parse_ua_stylesheet(shared_lock, "user-agent.css", USER_AGENT_CSS),
1658 parse_ua_stylesheet(shared_lock, "servo.css", SERVO_CSS),
1659 parse_ua_stylesheet(
1660 shared_lock,
1661 "presentational-hints.css",
1662 PRESENTATIONAL_HINTS_CSS,
1663 ),
1664 ];
1665
1666 let html_mode_stylesheet =
1667 parse_ua_stylesheet(shared_lock, "html-mode.css", HTML_MODE_CSS);
1668
1669 let quirks_mode_stylesheet =
1670 parse_ua_stylesheet(shared_lock, "quirks-mode.css", QUIRKS_MODE_CSS);
1671
1672 Rc::new(UserAgentStylesheets {
1673 user_agent_stylesheets,
1674 html_mode_stylesheet,
1675 quirks_mode_stylesheet,
1676 })
1677 })
1678 .clone()
1679 })
1680}
1681
1682pub struct UserAgentStylesheets {
1684 pub user_agent_stylesheets: Vec<DocumentStyleSheet>,
1686 pub html_mode_stylesheet: DocumentStyleSheet,
1688 pub quirks_mode_stylesheet: DocumentStyleSheet,
1690}
1691
1692struct RegisteredPainterImpl {
1693 painter: Box<dyn Painter>,
1694 name: Atom,
1695 properties: FxHashMap<Atom, PropertyId>,
1697}
1698
1699impl SpeculativePainter for RegisteredPainterImpl {
1700 fn speculatively_draw_a_paint_image(
1701 &self,
1702 properties: Vec<(Atom, String)>,
1703 arguments: Vec<String>,
1704 ) {
1705 self.painter
1706 .speculatively_draw_a_paint_image(properties, arguments);
1707 }
1708}
1709
1710impl RegisteredSpeculativePainter for RegisteredPainterImpl {
1711 fn properties(&self) -> &FxHashMap<Atom, PropertyId> {
1712 &self.properties
1713 }
1714 fn name(&self) -> Atom {
1715 self.name.clone()
1716 }
1717}
1718
1719impl Painter for RegisteredPainterImpl {
1720 fn draw_a_paint_image(
1721 &self,
1722 size: Size2D<f32, CSSPixel>,
1723 device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
1724 properties: Vec<(Atom, String)>,
1725 arguments: Vec<String>,
1726 ) -> Result<DrawAPaintImageResult, PaintWorkletError> {
1727 self.painter
1728 .draw_a_paint_image(size, device_pixel_ratio, properties, arguments)
1729 }
1730}
1731
1732struct RegisteredPaintersImpl(HashMap<Atom, RegisteredPainterImpl>);
1733
1734impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1735 fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1736 self.0
1737 .get(name)
1738 .map(|painter| painter as &dyn RegisteredSpeculativePainter)
1739 }
1740}
1741
1742struct LayoutFontMetricsProvider(Arc<FontContext>);
1743
1744impl FontMetricsProvider for LayoutFontMetricsProvider {
1745 fn query_font_metrics(
1746 &self,
1747 _vertical: bool,
1748 font: &Font,
1749 base_size: CSSPixelLength,
1750 _flags: QueryFontMetricsFlags,
1751 ) -> FontMetrics {
1752 let font_context = &self.0;
1753 let font_group = self
1754 .0
1755 .font_group_with_size(ServoArc::new(font.clone()), base_size.into());
1756
1757 let Some(first_font_metrics) = font_group
1758 .first(font_context)
1759 .map(|font| font.metrics.clone())
1760 else {
1761 return Default::default();
1762 };
1763
1764 let x_height = Some(first_font_metrics.x_height)
1767 .filter(|x_height| !x_height.is_zero())
1768 .map(CSSPixelLength::from);
1769
1770 let zero_advance_measure = first_font_metrics
1771 .zero_horizontal_advance
1772 .or_else(|| {
1773 font_group
1774 .find_by_codepoint(font_context, '0', None, Language::UND)?
1775 .metrics
1776 .zero_horizontal_advance
1777 })
1778 .map(CSSPixelLength::from);
1779
1780 let ic_width = first_font_metrics
1781 .ic_horizontal_advance
1782 .or_else(|| {
1783 font_group
1784 .find_by_codepoint(font_context, '\u{6C34}', None, Language::UND)?
1785 .metrics
1786 .ic_horizontal_advance
1787 })
1788 .map(CSSPixelLength::from);
1789
1790 FontMetrics {
1791 x_height,
1792 zero_advance_measure,
1793 cap_height: None,
1794 ic_width,
1795 ascent: first_font_metrics.ascent.into(),
1796 script_percent_scale_down: None,
1797 script_script_percent_scale_down: None,
1798 }
1799 }
1800
1801 fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
1802 Length::new(match generic {
1803 GenericFontFamily::Monospace => pref!(fonts_default_monospace_size),
1804 _ => pref!(fonts_default_size),
1805 } as f32)
1806 .max(Length::new(0.0))
1807 }
1808}
1809
1810impl Debug for LayoutFontMetricsProvider {
1811 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1812 f.debug_tuple("LayoutFontMetricsProvider").finish()
1813 }
1814}
1815
1816struct SnapshotSetter<'dom> {
1817 elements_with_snapshot: Vec<ServoLayoutElement<'dom>>,
1818}
1819
1820impl SnapshotSetter<'_> {
1821 fn new(restyle: &mut ReflowRequestRestyle, snapshot_map: &mut SnapshotMap) -> Self {
1822 debug!("Draining restyles: {}", restyle.pending_restyles.len());
1823 let restyles = std::mem::take(&mut restyle.pending_restyles);
1824
1825 let elements_with_snapshot: Vec<_> = restyles
1826 .iter()
1827 .filter(|r| r.1.snapshot.is_some())
1828 .map(|r| unsafe { ServoLayoutNode::new(&r.0).as_element().unwrap() })
1829 .collect();
1830
1831 for (element, restyle) in restyles {
1832 let element = unsafe { ServoLayoutNode::new(&element).as_element().unwrap() };
1833
1834 let Some(mut style_data) = element
1837 .style_data()
1838 .map(|data| data.element_data.borrow_mut())
1839 else {
1840 element.unset_snapshot_flags();
1841 continue;
1842 };
1843
1844 debug!("Noting restyle for {:?}: {:?}", element, style_data);
1845 if let Some(s) = restyle.snapshot {
1846 element.set_has_snapshot();
1847 snapshot_map.insert(element.as_node().opaque(), s);
1848 }
1849
1850 style_data.hint.insert(restyle.hint);
1852 style_data.damage = restyle.damage;
1853 }
1854 Self {
1855 elements_with_snapshot,
1856 }
1857 }
1858}
1859
1860impl Drop for SnapshotSetter<'_> {
1861 fn drop(&mut self) {
1862 for element in &self.elements_with_snapshot {
1863 element.unset_snapshot_flags();
1864 }
1865 }
1866}
1867
1868bitflags! {
1869 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1870 pub struct ReflowPhases: u8 {
1871 const StackingContextTreeConstruction = 1 << 0;
1872 const DisplayListConstruction = 1 << 1;
1873 }
1874}
1875
1876impl ReflowPhases {
1877 fn necessary(reflow_goal: &ReflowGoal) -> Self {
1881 let is_inset_longhand = |longhand: LonghandId| {
1882 matches!(
1883 longhand,
1884 LonghandId::Top |
1885 LonghandId::Right |
1886 LonghandId::Bottom |
1887 LonghandId::Left |
1888 LonghandId::InsetInlineStart |
1889 LonghandId::InsetInlineEnd |
1890 LonghandId::InsetBlockStart |
1891 LonghandId::InsetBlockEnd
1892 )
1893 };
1894
1895 let is_inset_property =
1896 |property: NonCustomPropertyId| match property.longhand_or_shorthand() {
1897 Ok(longhand) => is_inset_longhand(longhand),
1898 Err(ShorthandId::All) => true,
1900 Err(shorthand) => shorthand.longhands().any(is_inset_longhand),
1901 };
1902
1903 match reflow_goal {
1904 ReflowGoal::LayoutQuery(query) => match query {
1905 QueryMsg::ResolvedStyleQuery(PropertyId::NonCustom(non_custom_property_id))
1908 if is_inset_property(*non_custom_property_id) =>
1909 {
1910 Self::StackingContextTreeConstruction
1911 },
1912 QueryMsg::ResolvedStyleQuery(_) => Self::empty(),
1913 QueryMsg::NodesFromPointQuery => {
1914 Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1915 },
1916 QueryMsg::BoxArea |
1917 QueryMsg::BoxAreas |
1918 QueryMsg::ElementsFromPoint |
1919 QueryMsg::FlushForUpdateTheRenderingQuery |
1920 QueryMsg::OffsetParentQuery |
1921 QueryMsg::ScrollingAreaOrOffsetQuery |
1922 QueryMsg::TextIndexQuery => Self::StackingContextTreeConstruction,
1923 QueryMsg::ClientRectQuery |
1924 QueryMsg::CurrentCSSZoomQuery |
1925 QueryMsg::EffectiveOverflow |
1926 QueryMsg::ElementInnerOuterTextQuery |
1927 QueryMsg::InnerWindowDimensionsQuery |
1928 QueryMsg::PaddingQuery |
1929 QueryMsg::ResolvedFontStyleQuery |
1930 QueryMsg::ScrollParentQuery |
1931 QueryMsg::StyleQuery => Self::empty(),
1932 },
1933 ReflowGoal::UpdateScrollNode(..) | ReflowGoal::UpdateTheRendering => {
1934 Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1935 },
1936 }
1937 }
1938}
1939
1940struct StylistStylesheetUpdate {
1942 invalidation_set: StylesheetInvalidationSet,
1944 changed_web_fonts: WebFontSetDifference,
1946}