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