1#![expect(unsafe_code)]
6
7use std::cell::{Cell, RefCell};
8use std::collections::HashMap;
9use std::fmt::Debug;
10use std::process;
11use std::rc::Rc;
12use std::sync::{Arc, LazyLock};
13
14use app_units::Au;
15use base::generic_channel::GenericSender;
16use base::id::{PipelineId, WebViewId};
17use bitflags::bitflags;
18use cssparser::ParserInput;
19use embedder_traits::{Theme, ViewportDetails};
20use euclid::{Point2D, Rect, Scale, Size2D};
21use fonts::{FontContext, FontContextWebFontMethods, WebFontDocumentContext};
22use fonts_traits::StylesheetWebFontLoadFinishedCallback;
23use layout_api::wrapper_traits::LayoutNode;
24use layout_api::{
25 BoxAreaType, CSSPixelRectIterator, IFrameSizes, Layout, LayoutConfig, LayoutDamage,
26 LayoutFactory, OffsetParentResponse, PhysicalSides, PropertyRegistration, QueryMsg, ReflowGoal,
27 ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle, ReflowResult, RegisterPropertyError,
28 ScrollContainerQueryFlags, ScrollContainerResponse, TrustedNodeAddress,
29};
30use log::{debug, error, warn};
31use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
32use net_traits::image_cache::ImageCache;
33use paint_api::CrossProcessPaintApi;
34use paint_api::display_list::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::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode};
43use script_traits::{DrawAPaintImageResult, PaintWorkletError, Painter, ScriptThreadMessage};
44use servo_arc::Arc as ServoArc;
45use servo_config::opts::{self, DiagnosticsLogging};
46use servo_config::pref;
47use servo_url::ServoUrl;
48use style::animation::DocumentAnimationSet;
49use style::context::{
50 QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
51};
52use style::custom_properties::{SpecifiedValue, parse_name};
53use style::dom::{OpaqueNode, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
54use style::font_metrics::FontMetrics;
55use style::global_style_data::GLOBAL_STYLE_DATA;
56use style::invalidation::element::restyle_hints::RestyleHint;
57use style::invalidation::stylesheets::StylesheetInvalidationSet;
58use style::media_queries::{Device, MediaList, MediaType};
59use style::properties::style_structs::Font;
60use style::properties::{ComputedValues, PropertyId};
61use style::properties_and_values::registry::{
62 PropertyRegistration as StyloPropertyRegistration, PropertyRegistrationData,
63};
64use style::properties_and_values::rule::{Inherits, PropertyRegistrationError, PropertyRuleName};
65use style::properties_and_values::syntax::Descriptor;
66use style::queries::values::PrefersColorScheme;
67use style::selector_parser::{PseudoElement, RestyleDamage, SnapshotMap};
68use style::servo::media_queries::FontMetricsProvider;
69use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
70use style::stylesheets::{
71 CustomMediaMap, DocumentStyleSheet, Origin, Stylesheet, StylesheetInDocument,
72};
73use style::stylist::Stylist;
74use style::traversal::DomTraversal;
75use style::traversal_flags::TraversalFlags;
76use style::values::computed::font::GenericFontFamily;
77use style::values::computed::{CSSPixelLength, FontSize, Length, NonNegativeLength, XLang};
78use style::values::specified::font::{KeywordInfo, QueryFontMetricsFlags};
79use style::values::{Parser, SourceLocation};
80use style::{Zero, driver};
81use style_traits::{CSSPixel, SpeculativePainter};
82use stylo_atoms::Atom;
83use url::Url;
84use webrender_api::ExternalScrollId;
85use webrender_api::units::{DevicePixel, LayoutVector2D};
86
87use crate::context::{CachedImageOrError, ImageResolver, LayoutContext};
88use crate::display_list::{DisplayListBuilder, HitTest, PaintTimingHandler, StackingContextTree};
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, process_current_css_zoom_query,
92 process_node_scroll_area_request, process_offset_parent_query, process_padding_request,
93 process_resolved_font_style_query, process_resolved_style_request,
94 process_scroll_container_query,
95};
96use crate::traversal::{RecalcStyle, compute_damage_and_repair_style};
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 SERVO_CSS: &[u8] = include_bytes!("./stylesheets/servo.css");
112
113static PRESENTATIONAL_HINTS_CSS: &[u8] = include_bytes!("./stylesheets/presentational-hints.css");
115
116static QUIRKS_MODE_CSS: &[u8] = include_bytes!("./stylesheets/quirks-mode.css");
118
119pub struct LayoutThread {
121 id: PipelineId,
123
124 webview_id: WebViewId,
126
127 url: ServoUrl,
129
130 stylist: Stylist,
132
133 is_iframe: bool,
135
136 script_chan: GenericSender<ScriptThreadMessage>,
138
139 time_profiler_chan: profile_time::ProfilerChan,
141
142 image_cache: Arc<dyn ImageCache>,
144
145 font_context: Arc<FontContext>,
147
148 have_added_user_agent_stylesheets: bool,
150
151 user_stylesheets: Rc<Vec<DocumentStyleSheet>>,
155
156 device_has_changed: bool,
159
160 have_ever_generated_display_list: Cell<bool>,
162
163 need_overflow_calculation: Cell<bool>,
166
167 need_new_display_list: Cell<bool>,
173
174 need_new_stacking_context_tree: Cell<bool>,
179
180 box_tree: RefCell<Option<Arc<BoxTree>>>,
182
183 fragment_tree: RefCell<Option<Rc<FragmentTree>>>,
185
186 stacking_context_tree: RefCell<Option<StackingContextTree>>,
188
189 resolved_images_cache: Arc<RwLock<HashMap<ServoUrl, CachedImageOrError>>>,
193
194 registered_painters: RegisteredPaintersImpl,
196
197 paint_api: CrossProcessPaintApi,
199
200 debug: DiagnosticsLogging,
203
204 previously_highlighted_dom_node: Cell<Option<OpaqueNode>>,
208
209 paint_timing_handler: RefCell<Option<PaintTimingHandler>>,
211}
212
213pub struct LayoutFactoryImpl();
214
215impl LayoutFactory for LayoutFactoryImpl {
216 fn create(&self, config: LayoutConfig) -> Box<dyn Layout> {
217 Box::new(LayoutThread::new(config))
218 }
219}
220
221impl Drop for LayoutThread {
222 fn drop(&mut self) {
223 let (keys, instance_keys) = self
224 .font_context
225 .collect_unused_webrender_resources(true );
226 self.paint_api
227 .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys)
228 }
229}
230
231impl Layout for LayoutThread {
232 fn device(&self) -> &Device {
233 self.stylist.device()
234 }
235
236 fn set_theme(&mut self, theme: Theme) -> bool {
237 let theme: PrefersColorScheme = theme.into();
238 let device = self.stylist.device_mut();
239 if theme == device.color_scheme() {
240 return false;
241 }
242
243 device.set_color_scheme(theme);
244 self.device_has_changed = true;
245 true
246 }
247
248 fn set_viewport_details(&mut self, viewport_details: ViewportDetails) -> bool {
249 let device = self.stylist.device_mut();
250 let device_pixel_ratio = Scale::new(viewport_details.hidpi_scale_factor.get());
251 if device.viewport_size() == viewport_details.size &&
252 device.device_pixel_ratio() == device_pixel_ratio
253 {
254 return false;
255 }
256
257 device.set_viewport_size(viewport_details.size);
258 device.set_device_pixel_ratio(device_pixel_ratio);
259 self.device_has_changed = true;
260 true
261 }
262
263 fn load_web_fonts_from_stylesheet(
264 &self,
265 stylesheet: &ServoArc<Stylesheet>,
266 document_context: &WebFontDocumentContext,
267 ) {
268 let guard = stylesheet.shared_lock.read();
269 self.load_all_web_fonts_from_stylesheet_with_guard(
270 &DocumentStyleSheet(stylesheet.clone()),
271 &guard,
272 document_context,
273 );
274 }
275
276 #[servo_tracing::instrument(skip_all)]
277 fn add_stylesheet(
278 &mut self,
279 stylesheet: ServoArc<Stylesheet>,
280 before_stylesheet: Option<ServoArc<Stylesheet>>,
281 document_context: &WebFontDocumentContext,
282 ) {
283 let guard = stylesheet.shared_lock.read();
284 let stylesheet = DocumentStyleSheet(stylesheet.clone());
285 self.load_all_web_fonts_from_stylesheet_with_guard(&stylesheet, &guard, document_context);
286
287 match before_stylesheet {
288 Some(insertion_point) => self.stylist.insert_stylesheet_before(
289 stylesheet,
290 DocumentStyleSheet(insertion_point),
291 &guard,
292 ),
293 None => self.stylist.append_stylesheet(stylesheet, &guard),
294 }
295 }
296
297 #[servo_tracing::instrument(skip_all)]
298 fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>) {
299 let guard = stylesheet.shared_lock.read();
300 let stylesheet = DocumentStyleSheet(stylesheet.clone());
301 self.stylist.remove_stylesheet(stylesheet.clone(), &guard);
302 self.font_context
303 .remove_all_web_fonts_from_stylesheet(&stylesheet);
304 }
305
306 #[servo_tracing::instrument(skip_all)]
307 fn remove_cached_image(&mut self, url: &ServoUrl) {
308 let mut resolved_images_cache = self.resolved_images_cache.write();
309 resolved_images_cache.remove(url);
310 }
311
312 #[servo_tracing::instrument(skip_all)]
314 fn query_padding(&self, node: TrustedNodeAddress) -> Option<PhysicalSides> {
315 if self.fragment_tree.borrow().is_none() {
318 return None;
319 }
320
321 let node = unsafe { ServoLayoutNode::new(&node) };
322 process_padding_request(node.to_threadsafe())
323 }
324
325 #[servo_tracing::instrument(skip_all)]
331 fn query_box_area(
332 &self,
333 node: TrustedNodeAddress,
334 area: BoxAreaType,
335 exclude_transform_and_inline: bool,
336 ) -> Option<Rect<Au, CSSPixel>> {
337 if self.fragment_tree.borrow().is_none() {
340 return None;
341 }
342
343 let node = unsafe { ServoLayoutNode::new(&node) };
344 let stacking_context_tree = self.stacking_context_tree.borrow();
345 let stacking_context_tree = stacking_context_tree
346 .as_ref()
347 .expect("Should always have a StackingContextTree for box area queries");
348 process_box_area_request(
349 stacking_context_tree,
350 node.to_threadsafe(),
351 area,
352 exclude_transform_and_inline,
353 )
354 }
355
356 #[servo_tracing::instrument(skip_all)]
361 fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> CSSPixelRectIterator {
362 if self.fragment_tree.borrow().is_none() {
365 return Box::new(std::iter::empty());
366 }
367
368 let node = unsafe { ServoLayoutNode::new(&node) };
369 let stacking_context_tree = self.stacking_context_tree.borrow();
370 let stacking_context_tree = stacking_context_tree
371 .as_ref()
372 .expect("Should always have a StackingContextTree for box area queries");
373 process_box_areas_request(stacking_context_tree, node.to_threadsafe(), area)
374 }
375
376 #[servo_tracing::instrument(skip_all)]
377 fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32, CSSPixel> {
378 let node = unsafe { ServoLayoutNode::new(&node) };
379 process_client_rect_request(node.to_threadsafe())
380 }
381
382 #[servo_tracing::instrument(skip_all)]
383 fn query_current_css_zoom(&self, node: TrustedNodeAddress) -> f32 {
384 let node = unsafe { ServoLayoutNode::new(&node) };
385 process_current_css_zoom_query(node)
386 }
387
388 #[servo_tracing::instrument(skip_all)]
389 fn query_element_inner_outer_text(&self, node: layout_api::TrustedNodeAddress) -> String {
390 let node = unsafe { ServoLayoutNode::new(&node) };
391 get_the_text_steps(node)
392 }
393 #[servo_tracing::instrument(skip_all)]
394 fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse {
395 let node = unsafe { ServoLayoutNode::new(&node) };
396 let stacking_context_tree = self.stacking_context_tree.borrow();
397 let stacking_context_tree = stacking_context_tree
398 .as_ref()
399 .expect("Should always have a StackingContextTree for offset parent queries");
400 process_offset_parent_query(&stacking_context_tree.paint_info.scroll_tree, node)
401 .unwrap_or_default()
402 }
403
404 #[servo_tracing::instrument(skip_all)]
405 fn query_scroll_container(
406 &self,
407 node: Option<TrustedNodeAddress>,
408 flags: ScrollContainerQueryFlags,
409 ) -> Option<ScrollContainerResponse> {
410 let node = unsafe { node.as_ref().map(|node| ServoLayoutNode::new(node)) };
411 let viewport_overflow = self
412 .box_tree
413 .borrow()
414 .as_ref()
415 .expect("Should have a BoxTree for all scroll container queries.")
416 .viewport_overflow;
417 process_scroll_container_query(node, flags, viewport_overflow)
418 }
419
420 #[servo_tracing::instrument(skip_all)]
421 fn query_resolved_style(
422 &self,
423 node: TrustedNodeAddress,
424 pseudo: Option<PseudoElement>,
425 property_id: PropertyId,
426 animations: DocumentAnimationSet,
427 animation_timeline_value: f64,
428 ) -> String {
429 let node = unsafe { ServoLayoutNode::new(&node) };
430 let document = node.owner_doc();
431 let document_shared_lock = document.style_shared_lock();
432 let guards = StylesheetGuards {
433 author: &document_shared_lock.read(),
434 ua_or_user: &GLOBAL_STYLE_DATA.shared_lock.read(),
435 };
436 let snapshot_map = SnapshotMap::new();
437
438 let shared_style_context = self.build_shared_style_context(
439 guards,
440 &snapshot_map,
441 animation_timeline_value,
442 &animations,
443 TraversalFlags::empty(),
444 );
445
446 process_resolved_style_request(&shared_style_context, node, &pseudo, &property_id)
447 }
448
449 #[servo_tracing::instrument(skip_all)]
450 fn query_resolved_font_style(
451 &self,
452 node: TrustedNodeAddress,
453 value: &str,
454 animations: DocumentAnimationSet,
455 animation_timeline_value: f64,
456 ) -> Option<ServoArc<Font>> {
457 let node = unsafe { ServoLayoutNode::new(&node) };
458 let document = node.owner_doc();
459 let document_shared_lock = document.style_shared_lock();
460 let guards = StylesheetGuards {
461 author: &document_shared_lock.read(),
462 ua_or_user: &GLOBAL_STYLE_DATA.shared_lock.read(),
463 };
464 let snapshot_map = SnapshotMap::new();
465 let shared_style_context = self.build_shared_style_context(
466 guards,
467 &snapshot_map,
468 animation_timeline_value,
469 &animations,
470 TraversalFlags::empty(),
471 );
472
473 process_resolved_font_style_query(
474 &shared_style_context,
475 node,
476 value,
477 self.url.clone(),
478 document_shared_lock,
479 )
480 }
481
482 #[servo_tracing::instrument(skip_all)]
483 fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32, CSSPixel> {
484 let node = node.map(|node| unsafe { ServoLayoutNode::new(&node).to_threadsafe() });
485 process_node_scroll_area_request(node, self.fragment_tree.borrow().clone())
486 }
487
488 #[servo_tracing::instrument(skip_all)]
489 fn query_text_index(
490 &self,
491 node: TrustedNodeAddress,
492 point_in_node: Point2D<Au, CSSPixel>,
493 ) -> Option<usize> {
494 let node = unsafe { ServoLayoutNode::new(&node).to_threadsafe() };
495 let stacking_context_tree = self.stacking_context_tree.borrow_mut();
496 let stacking_context_tree = stacking_context_tree.as_ref()?;
497 find_character_offset_in_fragment_descendants(&node, stacking_context_tree, point_in_node)
498 }
499
500 #[servo_tracing::instrument(skip_all)]
501 fn query_elements_from_point(
502 &self,
503 point: webrender_api::units::LayoutPoint,
504 flags: layout_api::ElementsFromPointFlags,
505 ) -> Vec<layout_api::ElementsFromPointResult> {
506 self.stacking_context_tree
507 .borrow_mut()
508 .as_mut()
509 .map(|tree| HitTest::run(tree, point, flags))
510 .unwrap_or_default()
511 }
512
513 fn exit_now(&mut self) {}
514
515 fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps) {
516 let formatted_url = &format!("url({})", self.url);
518 reports.push(Report {
519 path: path![formatted_url, "layout-thread", "display-list"],
520 kind: ReportKind::ExplicitJemallocHeapSize,
521 size: 0,
522 });
523
524 reports.push(Report {
525 path: path![formatted_url, "layout-thread", "stylist"],
526 kind: ReportKind::ExplicitJemallocHeapSize,
527 size: self.stylist.size_of(ops),
528 });
529
530 reports.push(Report {
531 path: path![formatted_url, "layout-thread", "font-context"],
532 kind: ReportKind::ExplicitJemallocHeapSize,
533 size: self.font_context.conditional_size_of(ops),
534 });
535
536 reports.push(Report {
537 path: path![formatted_url, "layout-thread", "box-tree"],
538 kind: ReportKind::ExplicitJemallocHeapSize,
539 size: self
540 .box_tree
541 .borrow()
542 .as_ref()
543 .map_or(0, |tree| tree.conditional_size_of(ops)),
544 });
545
546 reports.push(Report {
547 path: path![formatted_url, "layout-thread", "fragment-tree"],
548 kind: ReportKind::ExplicitJemallocHeapSize,
549 size: self
550 .fragment_tree
551 .borrow()
552 .as_ref()
553 .map(|tree| tree.conditional_size_of(ops))
554 .unwrap_or_default(),
555 });
556
557 reports.push(Report {
558 path: path![formatted_url, "layout-thread", "stacking-context-tree"],
559 kind: ReportKind::ExplicitJemallocHeapSize,
560 size: self.stacking_context_tree.size_of(ops),
561 });
562
563 reports.extend(self.image_cache.memory_reports(formatted_url, ops));
564 }
565
566 fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
567 self.stylist.set_quirks_mode(quirks_mode);
568 }
569
570 fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult> {
571 time_profile!(
572 profile_time::ProfilerCategory::Layout,
573 self.profiler_metadata(),
574 self.time_profiler_chan.clone(),
575 || self.handle_reflow(reflow_request),
576 )
577 }
578
579 fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails) {
580 if self.stacking_context_tree.borrow().is_some() &&
581 !self.need_new_stacking_context_tree.get()
582 {
583 return;
584 }
585 self.build_stacking_context_tree(viewport_details);
586 }
587
588 fn register_paint_worklet_modules(
589 &mut self,
590 _name: Atom,
591 _properties: Vec<Atom>,
592 _painter: Box<dyn Painter>,
593 ) {
594 }
595
596 fn set_scroll_offsets_from_renderer(
597 &mut self,
598 scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
599 ) {
600 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
601 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
602 warn!("Received scroll offsets before finishing layout.");
603 return;
604 };
605
606 stacking_context_tree
607 .paint_info
608 .scroll_tree
609 .set_all_scroll_offsets(scroll_states);
610 }
611
612 fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D> {
613 self.stacking_context_tree
614 .borrow_mut()
615 .as_mut()
616 .and_then(|tree| tree.paint_info.scroll_tree.scroll_offset(id))
617 }
618
619 fn needs_new_display_list(&self) -> bool {
620 self.need_new_display_list.get()
621 }
622
623 fn set_needs_new_display_list(&self) {
624 self.need_new_display_list.set(true);
625 }
626
627 fn register_custom_property(
629 &mut self,
630 property_registration: PropertyRegistration,
631 ) -> Result<(), RegisterPropertyError> {
632 let Ok(name) = parse_name(&property_registration.name) else {
636 return Err(RegisterPropertyError::InvalidName);
637 };
638 let name = Atom::from(name);
639
640 if self
641 .stylist
642 .custom_property_script_registry()
643 .get(&name)
644 .is_some()
645 {
646 return Err(RegisterPropertyError::AlreadyRegistered);
647 }
648
649 let syntax = Descriptor::from_str(&property_registration.syntax, false)
652 .map_err(|_| RegisterPropertyError::InvalidSyntax)?;
653
654 let initial_value = match property_registration.initial_value {
656 Some(value) => {
657 let mut input = ParserInput::new(&value);
658 let parsed = Parser::new(&mut input)
659 .parse_entirely(|input| {
660 input.skip_whitespace();
661 SpecifiedValue::parse(input, &property_registration.url_data)
662 .map(servo_arc::Arc::new)
663 })
664 .ok();
665 if parsed.is_none() {
666 return Err(RegisterPropertyError::InvalidInitialValue);
667 }
668 parsed
669 },
670 None => None,
671 };
672
673 StyloPropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref())
674 .map_err(|error| match error {
675 PropertyRegistrationError::InitialValueNotComputationallyIndependent => {
676 RegisterPropertyError::InitialValueNotComputationallyIndependent
677 },
678 PropertyRegistrationError::InvalidInitialValue => {
679 RegisterPropertyError::InvalidInitialValue
680 },
681 PropertyRegistrationError::NoInitialValue => RegisterPropertyError::NoInitialValue,
682 })?;
683
684 let inherits = if property_registration.inherits {
686 Inherits::True
687 } else {
688 Inherits::False
689 };
690
691 let property_registration = StyloPropertyRegistration {
695 name: PropertyRuleName(name),
696 data: PropertyRegistrationData {
697 syntax,
698 initial_value,
699 inherits,
700 },
701 url_data: property_registration.url_data,
702 source_location: SourceLocation { line: 0, column: 0 },
703 };
704
705 self.stylist
706 .custom_property_script_registry_mut()
707 .register(property_registration);
708 self.stylist.rebuild_initial_values_for_custom_properties();
709
710 Ok(())
711 }
712}
713
714impl LayoutThread {
715 fn new(config: LayoutConfig) -> LayoutThread {
716 config
718 .paint_api
719 .send_initial_transaction(config.webview_id, config.id.into());
720
721 let mut font = Font::initial_values();
722 let default_font_size = pref!(fonts_default_size);
723 font.font_size = FontSize {
724 computed_size: NonNegativeLength::new(default_font_size as f32),
725 used_size: NonNegativeLength::new(default_font_size as f32),
726 keyword_info: KeywordInfo::medium(),
727 };
728
729 let device = Device::new(
732 MediaType::screen(),
733 QuirksMode::NoQuirks,
734 config.viewport_details.size,
735 Scale::new(config.viewport_details.hidpi_scale_factor.get()),
736 Box::new(LayoutFontMetricsProvider(config.font_context.clone())),
737 ComputedValues::initial_values_with_font_override(font),
738 config.theme.into(),
739 );
740
741 LayoutThread {
742 id: config.id,
743 webview_id: config.webview_id,
744 url: config.url,
745 is_iframe: config.is_iframe,
746 script_chan: config.script_chan.clone(),
747 time_profiler_chan: config.time_profiler_chan,
748 registered_painters: RegisteredPaintersImpl(Default::default()),
749 image_cache: config.image_cache,
750 font_context: config.font_context,
751 have_added_user_agent_stylesheets: false,
752 have_ever_generated_display_list: Cell::new(false),
753 device_has_changed: false,
754 need_overflow_calculation: Cell::new(false),
755 need_new_display_list: Cell::new(false),
756 need_new_stacking_context_tree: Cell::new(false),
757 box_tree: Default::default(),
758 fragment_tree: Default::default(),
759 stacking_context_tree: Default::default(),
760 paint_api: config.paint_api,
761 stylist: Stylist::new(device, QuirksMode::NoQuirks),
762 resolved_images_cache: Default::default(),
763 debug: opts::get().debug.clone(),
764 previously_highlighted_dom_node: Cell::new(None),
765 paint_timing_handler: Default::default(),
766 user_stylesheets: config.user_stylesheets,
767 }
768 }
769
770 fn build_shared_style_context<'a>(
771 &'a self,
772 guards: StylesheetGuards<'a>,
773 snapshot_map: &'a SnapshotMap,
774 animation_timeline_value: f64,
775 animations: &DocumentAnimationSet,
776 traversal_flags: TraversalFlags,
777 ) -> SharedStyleContext<'a> {
778 SharedStyleContext {
779 stylist: &self.stylist,
780 options: GLOBAL_STYLE_DATA.options.clone(),
781 guards,
782 visited_styles_enabled: false,
783 animations: animations.clone(),
784 registered_speculative_painters: &self.registered_painters,
785 current_time_for_animations: animation_timeline_value,
786 traversal_flags,
787 snapshot_map,
788 }
789 }
790
791 fn load_all_web_fonts_from_stylesheet_with_guard(
792 &self,
793 stylesheet: &DocumentStyleSheet,
794 guard: &SharedRwLockReadGuard,
795 document_context: &WebFontDocumentContext,
796 ) {
797 let custom_media = &CustomMediaMap::default();
798 if !stylesheet.is_effective_for_device(self.stylist.device(), custom_media, guard) {
799 return;
800 }
801
802 let locked_script_channel = Mutex::new(self.script_chan.clone());
803 let pipeline_id = self.id;
804 let web_font_finished_loading_callback = move |succeeded: bool| {
805 let _ = locked_script_channel
806 .lock()
807 .send(ScriptThreadMessage::WebFontLoaded(pipeline_id, succeeded));
808 };
809
810 self.font_context.add_all_web_fonts_from_stylesheet(
811 self.webview_id,
812 stylesheet,
813 guard,
814 self.stylist.device(),
815 Arc::new(web_font_finished_loading_callback) as StylesheetWebFontLoadFinishedCallback,
816 document_context,
817 );
818 }
819
820 fn can_skip_reflow_request_entirely(&self, reflow_request: &ReflowRequest) -> bool {
824 if reflow_request.restyle.is_some() {
826 return false;
827 }
828 if self.fragment_tree.borrow().is_none() {
830 return false;
831 }
832
833 let necessary_phases = ReflowPhases::necessary(&reflow_request.reflow_goal);
836 if necessary_phases.is_empty() {
837 return true;
838 }
839
840 if necessary_phases == ReflowPhases::StackingContextTreeConstruction {
843 return self.stacking_context_tree.borrow().is_some() &&
844 !self.need_new_stacking_context_tree.get();
845 }
846
847 assert_eq!(
850 necessary_phases,
851 ReflowPhases::StackingContextTreeConstruction | ReflowPhases::DisplayListConstruction
852 );
853 !self.need_new_display_list.get()
854 }
855
856 fn maybe_print_reflow_event(&self, reflow_request: &ReflowRequest) {
857 if !self.debug.relayout_event {
858 return;
859 }
860
861 println!(
862 "**** Reflow({}) => {:?}, {:?}",
863 self.id,
864 reflow_request.reflow_goal,
865 reflow_request
866 .restyle
867 .as_ref()
868 .map(|restyle| restyle.reason)
869 .unwrap_or_default()
870 );
871 }
872
873 fn handle_update_scroll_node_request(&self, reflow_request: &ReflowRequest) -> bool {
876 if let ReflowGoal::UpdateScrollNode(external_scroll_id, offset) = reflow_request.reflow_goal
877 {
878 self.set_scroll_offset_from_script(external_scroll_id, offset)
879 } else {
880 false
881 }
882 }
883
884 #[servo_tracing::instrument(skip_all)]
886 fn handle_reflow(&mut self, mut reflow_request: ReflowRequest) -> Option<ReflowResult> {
887 self.maybe_print_reflow_event(&reflow_request);
888
889 if self.can_skip_reflow_request_entirely(&reflow_request) {
890 return self
892 .handle_update_scroll_node_request(&reflow_request)
893 .then(|| ReflowResult {
894 reflow_phases_run: ReflowPhasesRun::UpdatedScrollNodeOffset,
895 ..Default::default()
896 });
897 }
898
899 let document = unsafe { ServoLayoutNode::new(&reflow_request.document) };
900 let document = document.as_document().unwrap();
901 let Some(root_element) = document.root_element() else {
902 debug!("layout: No root node: bailing");
903 return None;
904 };
905
906 let image_resolver = Arc::new(ImageResolver {
907 origin: reflow_request.origin.clone(),
908 image_cache: self.image_cache.clone(),
909 resolved_images_cache: self.resolved_images_cache.clone(),
910 pending_images: Mutex::default(),
911 pending_rasterization_images: Mutex::default(),
912 pending_svg_elements_for_serialization: Mutex::default(),
913 animating_images: reflow_request.animating_images.clone(),
914 animation_timeline_value: reflow_request.animation_timeline_value,
915 });
916
917 let (mut reflow_phases_run, iframe_sizes) = self.restyle_and_build_trees(
918 &mut reflow_request,
919 document,
920 root_element,
921 &image_resolver,
922 );
923 if self.calculate_overflow() {
924 reflow_phases_run.insert(ReflowPhasesRun::CalculatedOverflow);
925 }
926 if self.build_stacking_context_tree_for_reflow(&reflow_request) {
927 reflow_phases_run.insert(ReflowPhasesRun::BuiltStackingContextTree);
928 }
929 if self.build_display_list(&reflow_request, &image_resolver) {
930 reflow_phases_run.insert(ReflowPhasesRun::BuiltDisplayList);
931 }
932 if self.handle_update_scroll_node_request(&reflow_request) {
933 reflow_phases_run.insert(ReflowPhasesRun::UpdatedScrollNodeOffset);
934 }
935
936 let pending_images = std::mem::take(&mut *image_resolver.pending_images.lock());
937 let pending_rasterization_images =
938 std::mem::take(&mut *image_resolver.pending_rasterization_images.lock());
939 let pending_svg_elements_for_serialization =
940 std::mem::take(&mut *image_resolver.pending_svg_elements_for_serialization.lock());
941
942 Some(ReflowResult {
943 reflow_phases_run,
944 pending_images,
945 pending_rasterization_images,
946 pending_svg_elements_for_serialization,
947 iframe_sizes: Some(iframe_sizes),
948 })
949 }
950
951 #[servo_tracing::instrument(skip_all)]
952 fn prepare_stylist_for_reflow<'dom>(
953 &mut self,
954 reflow_request: &ReflowRequest,
955 document: ServoLayoutDocument<'dom>,
956 guards: &StylesheetGuards,
957 ua_stylesheets: &UserAgentStylesheets,
958 ) -> StylesheetInvalidationSet {
959 if !self.have_added_user_agent_stylesheets {
960 for stylesheet in &ua_stylesheets.user_agent_stylesheets {
961 self.stylist
962 .append_stylesheet(stylesheet.clone(), guards.ua_or_user);
963 self.load_all_web_fonts_from_stylesheet_with_guard(
964 stylesheet,
965 guards.ua_or_user,
966 &reflow_request.document_context,
967 );
968 }
969
970 for user_stylesheet in self.user_stylesheets.iter() {
971 self.stylist
972 .append_stylesheet(user_stylesheet.clone(), guards.ua_or_user);
973 self.load_all_web_fonts_from_stylesheet_with_guard(
974 user_stylesheet,
975 guards.ua_or_user,
976 &reflow_request.document_context,
977 );
978 }
979
980 if self.stylist.quirks_mode() == QuirksMode::Quirks {
981 self.stylist.append_stylesheet(
982 ua_stylesheets.quirks_mode_stylesheet.clone(),
983 guards.ua_or_user,
984 );
985 self.load_all_web_fonts_from_stylesheet_with_guard(
986 &ua_stylesheets.quirks_mode_stylesheet,
987 guards.ua_or_user,
988 &reflow_request.document_context,
989 );
990 }
991 self.have_added_user_agent_stylesheets = true;
992 }
993
994 if reflow_request.stylesheets_changed() {
995 self.stylist
996 .force_stylesheet_origins_dirty(Origin::Author.into());
997 }
998
999 document.flush_shadow_roots_stylesheets(&mut self.stylist, guards.author);
1001
1002 self.stylist.flush(guards)
1003 }
1004
1005 #[servo_tracing::instrument(skip_all)]
1006 fn restyle_and_build_trees(
1007 &mut self,
1008 reflow_request: &mut ReflowRequest,
1009 document: ServoLayoutDocument<'_>,
1010 root_element: ServoLayoutElement<'_>,
1011 image_resolver: &Arc<ImageResolver>,
1012 ) -> (ReflowPhasesRun, IFrameSizes) {
1013 let mut snapshot_map = SnapshotMap::new();
1014 let _snapshot_setter = match reflow_request.restyle.as_mut() {
1015 Some(restyle) => SnapshotSetter::new(restyle, &mut snapshot_map),
1016 None => return Default::default(),
1017 };
1018
1019 let document_shared_lock = document.style_shared_lock();
1020 let author_guard = document_shared_lock.read();
1021 let ua_stylesheets = &*UA_STYLESHEETS;
1022 let guards = StylesheetGuards {
1023 author: &author_guard,
1024 ua_or_user: &GLOBAL_STYLE_DATA.shared_lock.read(),
1025 };
1026
1027 let rayon_pool = STYLE_THREAD_POOL.lock();
1028 let rayon_pool = rayon_pool.pool();
1029 let rayon_pool = rayon_pool.as_ref();
1030
1031 let device_has_changed = std::mem::replace(&mut self.device_has_changed, false);
1032 if device_has_changed {
1033 let sheet_origins_affected_by_device_change = self
1034 .stylist
1035 .media_features_change_changed_style(&guards, self.device());
1036 self.stylist
1037 .force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
1038
1039 if let Some(mut data) = root_element.mutate_data() {
1040 data.hint.insert(RestyleHint::recascade_subtree());
1041 }
1042 }
1043
1044 self.prepare_stylist_for_reflow(reflow_request, document, &guards, ua_stylesheets)
1045 .process_style(root_element, Some(&snapshot_map));
1046
1047 if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
1048 self.need_new_display_list.set(true);
1051 }
1052
1053 let layout_context = LayoutContext {
1054 style_context: self.build_shared_style_context(
1055 guards,
1056 &snapshot_map,
1057 reflow_request.animation_timeline_value,
1058 &reflow_request.animations,
1059 match reflow_request.stylesheets_changed() {
1060 true => TraversalFlags::ForCSSRuleChanges,
1061 false => TraversalFlags::empty(),
1062 },
1063 ),
1064 font_context: self.font_context.clone(),
1065 iframe_sizes: Mutex::default(),
1066 use_rayon: rayon_pool.is_some(),
1067 image_resolver: image_resolver.clone(),
1068 painter_id: self.webview_id.into(),
1069 };
1070
1071 let restyle = reflow_request
1072 .restyle
1073 .as_ref()
1074 .expect("Should not get here if there is not restyle.");
1075
1076 let recalc_style_traversal;
1077 let dirty_root;
1078 {
1079 let _span = profile_traits::trace_span!("Styling").entered();
1080
1081 let original_dirty_root = unsafe {
1082 ServoLayoutNode::new(&restyle.dirty_root.unwrap())
1083 .as_element()
1084 .unwrap()
1085 };
1086
1087 recalc_style_traversal = RecalcStyle::new(&layout_context);
1088 let token = {
1089 let shared =
1090 DomTraversal::<ServoLayoutElement>::shared_context(&recalc_style_traversal);
1091 RecalcStyle::pre_traverse(original_dirty_root, shared)
1092 };
1093
1094 if !token.should_traverse() {
1095 layout_context.style_context.stylist.rule_tree().maybe_gc();
1096 return Default::default();
1097 }
1098
1099 dirty_root = driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
1100 }
1101
1102 let root_node = root_element.as_node();
1103 let damage_from_environment = if device_has_changed {
1104 RestyleDamage::RELAYOUT
1105 } else {
1106 Default::default()
1107 };
1108
1109 let damage = compute_damage_and_repair_style(
1110 &layout_context.style_context,
1111 root_node.to_threadsafe(),
1112 damage_from_environment,
1113 );
1114 if damage.contains(RestyleDamage::RECALCULATE_OVERFLOW) {
1115 self.need_overflow_calculation.set(true);
1116 }
1117 if damage.contains(RestyleDamage::REBUILD_STACKING_CONTEXT) {
1118 self.need_new_stacking_context_tree.set(true);
1119 }
1120 if damage.contains(RestyleDamage::REPAINT) {
1121 self.need_new_display_list.set(true);
1122 }
1123
1124 if !damage.contains(RestyleDamage::RELAYOUT) {
1125 layout_context.style_context.stylist.rule_tree().maybe_gc();
1126 return (ReflowPhasesRun::empty(), IFrameSizes::default());
1127 }
1128
1129 let mut box_tree = self.box_tree.borrow_mut();
1130 let box_tree = &mut *box_tree;
1131 let layout_damage: LayoutDamage = damage.into();
1132 if box_tree.is_none() || layout_damage.has_box_damage() {
1133 let mut build_box_tree = || {
1134 if !BoxTree::update(recalc_style_traversal.context(), dirty_root) {
1135 *box_tree = Some(Arc::new(BoxTree::construct(
1136 recalc_style_traversal.context(),
1137 root_node,
1138 )));
1139 }
1140 };
1141 if let Some(pool) = rayon_pool {
1142 pool.install(build_box_tree)
1143 } else {
1144 build_box_tree()
1145 };
1146 }
1147
1148 let viewport_size = self.stylist.device().au_viewport_size();
1149 let run_layout = || {
1150 box_tree
1151 .as_ref()
1152 .unwrap()
1153 .layout(recalc_style_traversal.context(), viewport_size)
1154 };
1155 let fragment_tree = Rc::new(if let Some(pool) = rayon_pool {
1156 pool.install(run_layout)
1157 } else {
1158 run_layout()
1159 });
1160
1161 *self.fragment_tree.borrow_mut() = Some(fragment_tree);
1162
1163 if self.debug.style_tree {
1164 println!(
1165 "{:?}",
1166 ShowSubtreeDataAndPrimaryValues(root_element.as_node())
1167 );
1168 }
1169 if self.debug.rule_tree {
1170 recalc_style_traversal
1171 .context()
1172 .style_context
1173 .stylist
1174 .rule_tree()
1175 .dump_stdout(&layout_context.style_context.guards);
1176 }
1177
1178 layout_context.style_context.stylist.rule_tree().maybe_gc();
1180
1181 let mut iframe_sizes = layout_context.iframe_sizes.lock();
1182 (
1183 ReflowPhasesRun::RanLayout,
1184 std::mem::take(&mut *iframe_sizes),
1185 )
1186 }
1187
1188 #[servo_tracing::instrument(name = "Overflow Calculation", skip_all)]
1189 fn calculate_overflow(&self) -> bool {
1190 if !self.need_overflow_calculation.get() {
1191 return false;
1192 }
1193
1194 if let Some(fragment_tree) = &*self.fragment_tree.borrow() {
1195 fragment_tree.calculate_scrollable_overflow();
1196 if self.debug.flow_tree {
1197 fragment_tree.print();
1198 }
1199 }
1200
1201 self.need_overflow_calculation.set(false);
1202 assert!(self.need_new_display_list.get());
1203 assert!(self.need_new_stacking_context_tree.get());
1204
1205 true
1206 }
1207
1208 fn build_stacking_context_tree_for_reflow(&self, reflow_request: &ReflowRequest) -> bool {
1209 if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1210 .contains(ReflowPhases::StackingContextTreeConstruction)
1211 {
1212 return false;
1213 }
1214 if !self.need_new_stacking_context_tree.get() {
1215 return false;
1216 }
1217
1218 self.build_stacking_context_tree(reflow_request.viewport_details)
1219 }
1220
1221 #[servo_tracing::instrument(name = "Stacking Context Tree Construction", skip_all)]
1222 fn build_stacking_context_tree(&self, viewport_details: ViewportDetails) -> bool {
1223 let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1224 return false;
1225 };
1226
1227 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1228 let old_scroll_offsets = stacking_context_tree
1229 .as_ref()
1230 .map(|tree| tree.paint_info.scroll_tree.scroll_offsets());
1231
1232 let mut new_stacking_context_tree = StackingContextTree::new(
1236 fragment_tree,
1237 viewport_details,
1238 self.id.into(),
1239 !self.have_ever_generated_display_list.get(),
1240 &self.debug,
1241 );
1242
1243 if let Some(old_scroll_offsets) = old_scroll_offsets {
1247 new_stacking_context_tree
1248 .paint_info
1249 .scroll_tree
1250 .set_all_scroll_offsets(&old_scroll_offsets);
1251 }
1252
1253 if self.debug.scroll_tree {
1254 new_stacking_context_tree
1255 .paint_info
1256 .scroll_tree
1257 .debug_print();
1258 }
1259
1260 *stacking_context_tree = Some(new_stacking_context_tree);
1261
1262 self.need_new_stacking_context_tree.set(false);
1264 assert!(self.need_new_display_list.get());
1265
1266 true
1267 }
1268
1269 #[servo_tracing::instrument(name = "Display List Construction", skip_all)]
1272 fn build_display_list(
1273 &self,
1274 reflow_request: &ReflowRequest,
1275 image_resolver: &Arc<ImageResolver>,
1276 ) -> bool {
1277 if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1278 .contains(ReflowPhases::DisplayListConstruction)
1279 {
1280 return false;
1281 }
1282 let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1283 return false;
1284 };
1285 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1286 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1287 return false;
1288 };
1289
1290 if !self.need_new_display_list.get() {
1294 return false;
1295 }
1296
1297 stacking_context_tree.paint_info.epoch = reflow_request.epoch;
1300
1301 let mut paint_timing_handler = self.paint_timing_handler.borrow_mut();
1302 let paint_timing_handler = match paint_timing_handler.as_mut() {
1304 Some(paint_timing_handler) => paint_timing_handler,
1305 None => {
1306 *paint_timing_handler = Some(PaintTimingHandler::new(
1307 stacking_context_tree
1308 .paint_info
1309 .viewport_details
1310 .layout_size(),
1311 ));
1312 paint_timing_handler.as_mut().unwrap()
1313 },
1314 };
1315
1316 let built_display_list = DisplayListBuilder::build(
1317 stacking_context_tree,
1318 fragment_tree,
1319 image_resolver.clone(),
1320 self.device().device_pixel_ratio(),
1321 reflow_request.highlighted_dom_node,
1322 &self.debug,
1323 paint_timing_handler,
1324 );
1325 self.paint_api.send_display_list(
1326 self.webview_id,
1327 &stacking_context_tree.paint_info,
1328 built_display_list,
1329 );
1330
1331 if paint_timing_handler.did_lcp_candidate_update() {
1332 if let Some(lcp_candidate) = paint_timing_handler.largest_contentful_paint_candidate() {
1333 self.paint_api.send_lcp_candidate(
1334 lcp_candidate,
1335 self.webview_id,
1336 self.id,
1337 stacking_context_tree.paint_info.epoch,
1338 );
1339 paint_timing_handler.unset_lcp_candidate_updated();
1340 }
1341 }
1342
1343 let (keys, instance_keys) = self
1344 .font_context
1345 .collect_unused_webrender_resources(false );
1346 self.paint_api
1347 .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys);
1348
1349 self.have_ever_generated_display_list.set(true);
1350 self.need_new_display_list.set(false);
1351 self.previously_highlighted_dom_node
1352 .set(reflow_request.highlighted_dom_node);
1353 true
1354 }
1355
1356 fn set_scroll_offset_from_script(
1357 &self,
1358 external_scroll_id: ExternalScrollId,
1359 offset: LayoutVector2D,
1360 ) -> bool {
1361 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1362 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1363 return false;
1364 };
1365
1366 if let Some(offset) = stacking_context_tree
1367 .paint_info
1368 .scroll_tree
1369 .set_scroll_offset_for_node_with_external_scroll_id(
1370 external_scroll_id,
1371 offset,
1372 ScrollType::Script,
1373 )
1374 {
1375 self.paint_api.scroll_node_by_delta(
1376 self.webview_id,
1377 self.id.into(),
1378 offset,
1379 external_scroll_id,
1380 );
1381 true
1382 } else {
1383 false
1384 }
1385 }
1386
1387 fn profiler_metadata(&self) -> Option<TimerMetadata> {
1389 Some(TimerMetadata {
1390 url: self.url.to_string(),
1391 iframe: if self.is_iframe {
1392 TimerMetadataFrameType::IFrame
1393 } else {
1394 TimerMetadataFrameType::RootWindow
1395 },
1396 incremental: if self.have_ever_generated_display_list.get() {
1397 TimerMetadataReflowType::Incremental
1398 } else {
1399 TimerMetadataReflowType::FirstReflow
1400 },
1401 })
1402 }
1403}
1404
1405fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
1406 fn parse_ua_stylesheet(
1407 shared_lock: &SharedRwLock,
1408 filename: &str,
1409 content: &[u8],
1410 ) -> Result<DocumentStyleSheet, &'static str> {
1411 let url = Url::parse(&format!("chrome://resources/{:?}", filename))
1412 .ok()
1413 .unwrap();
1414 Ok(DocumentStyleSheet(ServoArc::new(Stylesheet::from_bytes(
1415 content,
1416 url.into(),
1417 None,
1418 None,
1419 Origin::UserAgent,
1420 ServoArc::new(shared_lock.wrap(MediaList::empty())),
1421 shared_lock.clone(),
1422 None,
1423 None,
1424 QuirksMode::NoQuirks,
1425 ))))
1426 }
1427
1428 let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
1429
1430 let user_agent_stylesheets = vec![
1433 parse_ua_stylesheet(shared_lock, "user-agent.css", USER_AGENT_CSS)?,
1434 parse_ua_stylesheet(shared_lock, "servo.css", SERVO_CSS)?,
1435 parse_ua_stylesheet(
1436 shared_lock,
1437 "presentational-hints.css",
1438 PRESENTATIONAL_HINTS_CSS,
1439 )?,
1440 ];
1441
1442 let quirks_mode_stylesheet =
1443 parse_ua_stylesheet(shared_lock, "quirks-mode.css", QUIRKS_MODE_CSS)?;
1444
1445 Ok(UserAgentStylesheets {
1446 user_agent_stylesheets,
1447 quirks_mode_stylesheet,
1448 })
1449}
1450
1451pub struct UserAgentStylesheets {
1453 pub user_agent_stylesheets: Vec<DocumentStyleSheet>,
1455 pub quirks_mode_stylesheet: DocumentStyleSheet,
1457}
1458
1459static UA_STYLESHEETS: LazyLock<UserAgentStylesheets> =
1460 LazyLock::new(|| match get_ua_stylesheets() {
1461 Ok(stylesheets) => stylesheets,
1462 Err(filename) => {
1463 error!("Failed to load UA stylesheet {}!", filename);
1464 process::exit(1);
1465 },
1466 });
1467
1468struct RegisteredPainterImpl {
1469 painter: Box<dyn Painter>,
1470 name: Atom,
1471 properties: FxHashMap<Atom, PropertyId>,
1473}
1474
1475impl SpeculativePainter for RegisteredPainterImpl {
1476 fn speculatively_draw_a_paint_image(
1477 &self,
1478 properties: Vec<(Atom, String)>,
1479 arguments: Vec<String>,
1480 ) {
1481 self.painter
1482 .speculatively_draw_a_paint_image(properties, arguments);
1483 }
1484}
1485
1486impl RegisteredSpeculativePainter for RegisteredPainterImpl {
1487 fn properties(&self) -> &FxHashMap<Atom, PropertyId> {
1488 &self.properties
1489 }
1490 fn name(&self) -> Atom {
1491 self.name.clone()
1492 }
1493}
1494
1495impl Painter for RegisteredPainterImpl {
1496 fn draw_a_paint_image(
1497 &self,
1498 size: Size2D<f32, CSSPixel>,
1499 device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
1500 properties: Vec<(Atom, String)>,
1501 arguments: Vec<String>,
1502 ) -> Result<DrawAPaintImageResult, PaintWorkletError> {
1503 self.painter
1504 .draw_a_paint_image(size, device_pixel_ratio, properties, arguments)
1505 }
1506}
1507
1508struct RegisteredPaintersImpl(HashMap<Atom, RegisteredPainterImpl>);
1509
1510impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1511 fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1512 self.0
1513 .get(name)
1514 .map(|painter| painter as &dyn RegisteredSpeculativePainter)
1515 }
1516}
1517
1518struct LayoutFontMetricsProvider(Arc<FontContext>);
1519
1520impl FontMetricsProvider for LayoutFontMetricsProvider {
1521 fn query_font_metrics(
1522 &self,
1523 _vertical: bool,
1524 font: &Font,
1525 base_size: CSSPixelLength,
1526 _flags: QueryFontMetricsFlags,
1527 ) -> FontMetrics {
1528 let font_context = &self.0;
1529 let font_group = self
1530 .0
1531 .font_group_with_size(ServoArc::new(font.clone()), base_size.into());
1532
1533 let Some(first_font_metrics) = font_group
1534 .first(font_context)
1535 .map(|font| font.metrics.clone())
1536 else {
1537 return Default::default();
1538 };
1539
1540 let x_height = Some(first_font_metrics.x_height)
1543 .filter(|x_height| !x_height.is_zero())
1544 .map(CSSPixelLength::from);
1545
1546 let zero_advance_measure = first_font_metrics
1547 .zero_horizontal_advance
1548 .or_else(|| {
1549 font_group
1550 .find_by_codepoint(font_context, '0', None, XLang::get_initial_value())?
1551 .metrics
1552 .zero_horizontal_advance
1553 })
1554 .map(CSSPixelLength::from);
1555
1556 let ic_width = first_font_metrics
1557 .ic_horizontal_advance
1558 .or_else(|| {
1559 font_group
1560 .find_by_codepoint(font_context, '\u{6C34}', None, XLang::get_initial_value())?
1561 .metrics
1562 .ic_horizontal_advance
1563 })
1564 .map(CSSPixelLength::from);
1565
1566 FontMetrics {
1567 x_height,
1568 zero_advance_measure,
1569 cap_height: None,
1570 ic_width,
1571 ascent: first_font_metrics.ascent.into(),
1572 script_percent_scale_down: None,
1573 script_script_percent_scale_down: None,
1574 }
1575 }
1576
1577 fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
1578 Length::new(match generic {
1579 GenericFontFamily::Monospace => pref!(fonts_default_monospace_size),
1580 _ => pref!(fonts_default_size),
1581 } as f32)
1582 .max(Length::new(0.0))
1583 }
1584}
1585
1586impl Debug for LayoutFontMetricsProvider {
1587 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1588 f.debug_tuple("LayoutFontMetricsProvider").finish()
1589 }
1590}
1591
1592struct SnapshotSetter<'dom> {
1593 elements_with_snapshot: Vec<ServoLayoutElement<'dom>>,
1594}
1595
1596impl SnapshotSetter<'_> {
1597 fn new(restyle: &mut ReflowRequestRestyle, snapshot_map: &mut SnapshotMap) -> Self {
1598 debug!("Draining restyles: {}", restyle.pending_restyles.len());
1599 let restyles = std::mem::take(&mut restyle.pending_restyles);
1600
1601 let elements_with_snapshot: Vec<_> = restyles
1602 .iter()
1603 .filter(|r| r.1.snapshot.is_some())
1604 .map(|r| unsafe { ServoLayoutNode::new(&r.0).as_element().unwrap() })
1605 .collect();
1606
1607 for (element, restyle) in restyles {
1608 let element = unsafe { ServoLayoutNode::new(&element).as_element().unwrap() };
1609
1610 let Some(mut style_data) = element.mutate_data() else {
1613 unsafe { element.unset_snapshot_flags() };
1614 continue;
1615 };
1616
1617 debug!("Noting restyle for {:?}: {:?}", element, style_data);
1618 if let Some(s) = restyle.snapshot {
1619 unsafe { element.set_has_snapshot() };
1620 snapshot_map.insert(element.as_node().opaque(), s);
1621 }
1622
1623 style_data.hint.insert(restyle.hint);
1625 style_data.damage = restyle.damage;
1626 }
1627 Self {
1628 elements_with_snapshot,
1629 }
1630 }
1631}
1632
1633impl Drop for SnapshotSetter<'_> {
1634 fn drop(&mut self) {
1635 for element in &self.elements_with_snapshot {
1636 unsafe { element.unset_snapshot_flags() }
1637 }
1638 }
1639}
1640
1641bitflags! {
1642 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1643 pub struct ReflowPhases: u8 {
1644 const StackingContextTreeConstruction = 1 << 0;
1645 const DisplayListConstruction = 1 << 1;
1646 }
1647}
1648
1649impl ReflowPhases {
1650 fn necessary(reflow_goal: &ReflowGoal) -> Self {
1654 match reflow_goal {
1655 ReflowGoal::LayoutQuery(query) => match query {
1656 QueryMsg::NodesFromPointQuery => {
1657 Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1658 },
1659 QueryMsg::BoxArea |
1660 QueryMsg::BoxAreas |
1661 QueryMsg::ElementsFromPoint |
1662 QueryMsg::OffsetParentQuery |
1663 QueryMsg::ResolvedStyleQuery |
1664 QueryMsg::ScrollingAreaOrOffsetQuery |
1665 QueryMsg::TextIndexQuery => Self::StackingContextTreeConstruction,
1666 QueryMsg::ClientRectQuery |
1667 QueryMsg::CurrentCSSZoomQuery |
1668 QueryMsg::ElementInnerOuterTextQuery |
1669 QueryMsg::InnerWindowDimensionsQuery |
1670 QueryMsg::PaddingQuery |
1671 QueryMsg::ResolvedFontStyleQuery |
1672 QueryMsg::ScrollParentQuery |
1673 QueryMsg::StyleQuery => Self::empty(),
1674 },
1675 ReflowGoal::UpdateScrollNode(..) | ReflowGoal::UpdateTheRendering => {
1676 Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1677 },
1678 }
1679 }
1680}