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