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