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