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