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