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