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