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