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