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