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