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