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};
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
400 .as_ref()
401 .expect("Should always have a StackingContextTree for box area queries");
402 process_box_area_request(
403 stacking_context_tree,
404 node,
405 area,
406 exclude_transform_and_inline,
407 )
408 })
409 }
410
411 #[servo_tracing::instrument(skip_all)]
416 fn query_box_areas(&self, node: TrustedNodeAddress, area: BoxAreaType) -> CSSPixelRectIterator {
417 with_layout_state(|| {
418 if self.fragment_tree.borrow().is_none() {
421 return Box::new(std::iter::empty()) as CSSPixelRectIterator;
422 }
423
424 let node = unsafe { ServoLayoutNode::new(&node) };
425 let stacking_context_tree = self.stacking_context_tree.borrow();
426 let stacking_context_tree = stacking_context_tree
427 .as_ref()
428 .expect("Should always have a StackingContextTree for box area queries");
429 process_box_areas_request(stacking_context_tree, node, area)
430 })
431 }
432
433 #[servo_tracing::instrument(skip_all)]
434 fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32, CSSPixel> {
435 with_layout_state(|| {
436 let node = unsafe { ServoLayoutNode::new(&node) };
437 process_client_rect_request(node)
438 })
439 }
440
441 #[servo_tracing::instrument(skip_all)]
442 fn query_current_css_zoom(&self, node: TrustedNodeAddress) -> f32 {
443 with_layout_state(|| {
444 let node = unsafe { ServoLayoutNode::new(&node) };
445 process_current_css_zoom_query(node)
446 })
447 }
448
449 #[servo_tracing::instrument(skip_all)]
450 fn query_element_inner_outer_text(&self, node: layout_api::TrustedNodeAddress) -> String {
451 with_layout_state(|| {
452 let node = unsafe { ServoLayoutNode::new(&node) };
453 get_the_text_steps(node)
454 })
455 }
456 #[servo_tracing::instrument(skip_all)]
457 fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse {
458 with_layout_state(|| {
459 let node = unsafe { ServoLayoutNode::new(&node) };
460 let stacking_context_tree = self.stacking_context_tree.borrow();
461 let stacking_context_tree = stacking_context_tree
462 .as_ref()
463 .expect("Should always have a StackingContextTree for offset parent queries");
464 process_offset_parent_query(&stacking_context_tree.paint_info.scroll_tree, node)
465 .unwrap_or_default()
466 })
467 }
468
469 #[servo_tracing::instrument(skip_all)]
470 fn query_scroll_container(
471 &self,
472 node: Option<TrustedNodeAddress>,
473 flags: ScrollContainerQueryFlags,
474 ) -> Option<ScrollContainerResponse> {
475 with_layout_state(|| {
476 let node = unsafe { node.as_ref().map(|node| ServoLayoutNode::new(node)) };
477 let viewport_overflow = self
478 .box_tree
479 .borrow()
480 .as_ref()
481 .expect("Should have a BoxTree for all scroll container queries.")
482 .viewport_overflow;
483 process_scroll_container_query(node, flags, viewport_overflow)
484 })
485 }
486
487 #[servo_tracing::instrument(skip_all)]
488 fn query_resolved_style(
489 &self,
490 node: TrustedNodeAddress,
491 pseudo: Option<PseudoElement>,
492 property_id: PropertyId,
493 animations: DocumentAnimationSet,
494 animation_timeline_value: f64,
495 ) -> String {
496 with_layout_state(|| {
497 let node = unsafe { ServoLayoutNode::new(&node) };
498 let document = unsafe { node.dangerous_style_node() }.owner_doc();
499 let document_shared_lock = document.style_shared_lock();
500 let guards = StylesheetGuards {
501 author: &document_shared_lock.read(),
502 ua_or_user: &GLOBAL_STYLE_DATA.shared_lock.read(),
503 };
504 let snapshot_map = SnapshotMap::new();
505
506 let shared_style_context = self.build_shared_style_context(
507 guards,
508 &snapshot_map,
509 animation_timeline_value,
510 &animations,
511 TraversalFlags::empty(),
512 );
513
514 process_resolved_style_request(&shared_style_context, node, &pseudo, &property_id)
515 })
516 }
517
518 #[servo_tracing::instrument(skip_all)]
519 fn query_resolved_font_style(
520 &self,
521 node: TrustedNodeAddress,
522 value: &str,
523 animations: DocumentAnimationSet,
524 animation_timeline_value: f64,
525 ) -> Option<ServoArc<Font>> {
526 with_layout_state(|| {
527 let node = unsafe { ServoLayoutNode::new(&node) };
528 let document = unsafe { node.dangerous_style_node() }.owner_doc();
529 let document_shared_lock = document.style_shared_lock();
530 let guards = StylesheetGuards {
531 author: &document_shared_lock.read(),
532 ua_or_user: &GLOBAL_STYLE_DATA.shared_lock.read(),
533 };
534 let snapshot_map = SnapshotMap::new();
535 let shared_style_context = self.build_shared_style_context(
536 guards,
537 &snapshot_map,
538 animation_timeline_value,
539 &animations,
540 TraversalFlags::empty(),
541 );
542
543 process_resolved_font_style_query(
544 &shared_style_context,
545 node,
546 value,
547 self.url.clone(),
548 document_shared_lock,
549 )
550 })
551 }
552
553 #[servo_tracing::instrument(skip_all)]
554 fn query_scrolling_area(&self, node: Option<TrustedNodeAddress>) -> Rect<i32, CSSPixel> {
555 with_layout_state(|| {
556 let node = node.map(|node| unsafe { ServoLayoutNode::new(&node) });
557 process_node_scroll_area_request(node, self.fragment_tree.borrow().clone())
558 })
559 }
560
561 #[servo_tracing::instrument(skip_all)]
562 fn query_text_index(
563 &self,
564 node: TrustedNodeAddress,
565 point_in_node: Point2D<Au, CSSPixel>,
566 ) -> Option<usize> {
567 with_layout_state(|| {
568 let node = unsafe { ServoLayoutNode::new(&node) };
569 let stacking_context_tree = self.stacking_context_tree.borrow_mut();
570 let stacking_context_tree = stacking_context_tree.as_ref()?;
571 find_character_offset_in_fragment_descendants(
572 &node,
573 stacking_context_tree,
574 point_in_node,
575 )
576 })
577 }
578
579 #[servo_tracing::instrument(skip_all)]
580 fn query_elements_from_point(
581 &self,
582 point: webrender_api::units::LayoutPoint,
583 flags: layout_api::ElementsFromPointFlags,
584 ) -> Vec<layout_api::ElementsFromPointResult> {
585 with_layout_state(|| {
586 self.stacking_context_tree
587 .borrow_mut()
588 .as_mut()
589 .map(|tree| HitTest::run(tree, point, flags))
590 .unwrap_or_default()
591 })
592 }
593
594 #[servo_tracing::instrument(skip_all)]
595 fn query_effective_overflow(&self, node: TrustedNodeAddress) -> Option<AxesOverflow> {
596 with_layout_state(|| {
597 let node = unsafe { ServoLayoutNode::new(&node) };
598 process_effective_overflow_query(node)
599 })
600 }
601
602 fn exit_now(&mut self) {}
603
604 fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps) {
605 let formatted_url = &format!("url({})", self.url);
607 reports.push(Report {
608 path: path![formatted_url, "layout-thread", "display-list"],
609 kind: ReportKind::ExplicitJemallocHeapSize,
610 size: 0,
611 });
612
613 reports.push(Report {
614 path: path![formatted_url, "layout-thread", "stylist"],
615 kind: ReportKind::ExplicitJemallocHeapSize,
616 size: self.stylist.size_of(ops),
617 });
618
619 reports.push(Report {
620 path: path![formatted_url, "layout-thread", "font-context"],
621 kind: ReportKind::ExplicitJemallocHeapSize,
622 size: self.font_context.conditional_size_of(ops),
623 });
624
625 reports.push(Report {
626 path: path![formatted_url, "layout-thread", "box-tree"],
627 kind: ReportKind::ExplicitJemallocHeapSize,
628 size: self
629 .box_tree
630 .borrow()
631 .as_ref()
632 .map_or(0, |tree| tree.conditional_size_of(ops)),
633 });
634
635 reports.push(Report {
636 path: path![formatted_url, "layout-thread", "fragment-tree"],
637 kind: ReportKind::ExplicitJemallocHeapSize,
638 size: self
639 .fragment_tree
640 .borrow()
641 .as_ref()
642 .map(|tree| tree.conditional_size_of(ops))
643 .unwrap_or_default(),
644 });
645
646 reports.push(Report {
647 path: path![formatted_url, "layout-thread", "stacking-context-tree"],
648 kind: ReportKind::ExplicitJemallocHeapSize,
649 size: self.stacking_context_tree.size_of(ops),
650 });
651
652 reports.extend(self.image_cache.memory_reports(formatted_url, ops));
653 }
654
655 fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
656 self.stylist.set_quirks_mode(quirks_mode);
657 }
658
659 fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult> {
660 time_profile!(
661 profile_time::ProfilerCategory::Layout,
662 self.profiler_metadata(),
663 self.time_profiler_chan.clone(),
664 || with_layout_state(|| self.handle_reflow(reflow_request)),
665 )
666 }
667
668 fn ensure_stacking_context_tree(&self, viewport_details: ViewportDetails) {
669 with_layout_state(|| {
670 if self.stacking_context_tree.borrow().is_some() &&
671 !self.need_new_stacking_context_tree.get()
672 {
673 return;
674 }
675 self.build_stacking_context_tree(viewport_details);
676 })
677 }
678
679 fn register_paint_worklet_modules(
680 &mut self,
681 _name: Atom,
682 _properties: Vec<Atom>,
683 _painter: Box<dyn Painter>,
684 ) {
685 }
686
687 fn set_scroll_offsets_from_renderer(
688 &mut self,
689 scroll_states: &FxHashMap<ExternalScrollId, LayoutVector2D>,
690 ) {
691 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
692 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
693 warn!("Received scroll offsets before finishing layout.");
694 return;
695 };
696
697 stacking_context_tree
698 .paint_info
699 .scroll_tree
700 .set_all_scroll_offsets(scroll_states);
701 }
702
703 fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D> {
704 self.stacking_context_tree
705 .borrow_mut()
706 .as_mut()
707 .and_then(|tree| tree.paint_info.scroll_tree.scroll_offset(id))
708 }
709
710 fn needs_new_display_list(&self) -> bool {
711 self.need_new_display_list.get()
712 }
713
714 fn set_needs_new_display_list(&self) {
715 self.need_new_display_list.set(true);
716 }
717
718 fn stylist_mut(&mut self) -> &mut Stylist {
720 &mut self.stylist
721 }
722
723 fn set_accessibility_active(&self, active: bool, epoch: Epoch) {
724 if !active {
725 self.accessibility_tree.replace(None);
726 return;
727 }
728 self.set_needs_accessibility_update();
729 let mut accessibility_tree = self.accessibility_tree.borrow_mut();
730 if accessibility_tree.is_some() {
731 return;
732 }
733 *accessibility_tree = Some(AccessibilityTree::new(self.id.into(), epoch));
734 }
735
736 fn needs_accessibility_update(&self) -> bool {
737 self.needs_accessibility_update.get()
738 }
739
740 fn set_needs_accessibility_update(&self) {
741 self.needs_accessibility_update.set(true);
742 }
743}
744
745impl LayoutThread {
746 fn new(config: LayoutConfig) -> LayoutThread {
747 config
749 .paint_api
750 .send_initial_transaction(config.webview_id, config.id.into());
751
752 let mut font = Font::initial_values();
753 let default_font_size = pref!(fonts_default_size);
754 font.font_size = FontSize {
755 computed_size: NonNegativeLength::new(default_font_size as f32),
756 used_size: NonNegativeLength::new(default_font_size as f32),
757 keyword_info: KeywordInfo::medium(),
758 };
759
760 let device = Device::new(
763 MediaType::screen(),
764 QuirksMode::NoQuirks,
765 config.viewport_details.size,
766 Scale::new(config.viewport_details.hidpi_scale_factor.get()),
767 Box::new(LayoutFontMetricsProvider(config.font_context.clone())),
768 ComputedValues::initial_values_with_font_override(font),
769 config.theme.into(),
770 );
771
772 LayoutThread {
773 id: config.id,
774 webview_id: config.webview_id,
775 url: config.url,
776 is_iframe: config.is_iframe,
777 script_chan: config.script_chan.clone(),
778 time_profiler_chan: config.time_profiler_chan,
779 embedder_chan: config.embedder_chan.clone(),
780 registered_painters: RegisteredPaintersImpl(Default::default()),
781 image_cache: config.image_cache,
782 font_context: config.font_context,
783 have_added_user_agent_stylesheets: false,
784 have_ever_generated_display_list: Cell::new(false),
785 last_display_list_was_empty: Cell::new(true),
786 device_has_changed: false,
787 need_overflow_calculation: Cell::new(false),
788 need_new_display_list: Cell::new(false),
789 need_new_stacking_context_tree: Cell::new(false),
790 box_tree: Default::default(),
791 fragment_tree: Default::default(),
792 stacking_context_tree: Default::default(),
793 paint_api: config.paint_api,
794 stylist: Stylist::new(device, QuirksMode::NoQuirks),
795 resolved_images_cache: Default::default(),
796 debug: opts::get().debug.clone(),
797 previously_highlighted_dom_node: Cell::new(None),
798 paint_timing_handler: Default::default(),
799 user_stylesheets: config.user_stylesheets,
800 accessibility_tree: Default::default(),
801 needs_accessibility_update: Cell::new(false),
802 }
803 }
804
805 fn build_shared_style_context<'a>(
806 &'a self,
807 guards: StylesheetGuards<'a>,
808 snapshot_map: &'a SnapshotMap,
809 animation_timeline_value: f64,
810 animations: &DocumentAnimationSet,
811 traversal_flags: TraversalFlags,
812 ) -> SharedStyleContext<'a> {
813 SharedStyleContext {
814 stylist: &self.stylist,
815 options: GLOBAL_STYLE_DATA.options.clone(),
816 guards,
817 visited_styles_enabled: false,
818 animations: animations.clone(),
819 registered_speculative_painters: &self.registered_painters,
820 current_time_for_animations: animation_timeline_value,
821 traversal_flags,
822 snapshot_map,
823 }
824 }
825
826 fn load_all_web_fonts_from_stylesheet_with_guard(
827 &self,
828 stylesheet: &DocumentStyleSheet,
829 guard: &SharedRwLockReadGuard,
830 document_context: &WebFontDocumentContext,
831 ) {
832 let custom_media = &CustomMediaMap::default();
833 if !stylesheet.is_effective_for_device(self.stylist.device(), custom_media, guard) {
834 return;
835 }
836
837 let locked_script_channel = Mutex::new(self.script_chan.clone());
838 let pipeline_id = self.id;
839 let web_font_finished_loading_callback = move |succeeded: bool| {
840 if succeeded {
841 let _ = locked_script_channel
842 .lock()
843 .send(ScriptThreadMessage::WebFontLoaded(pipeline_id));
844 }
845 };
846
847 self.font_context.add_all_web_fonts_from_stylesheet(
848 self.webview_id,
849 stylesheet,
850 guard,
851 self.stylist.device(),
852 Arc::new(web_font_finished_loading_callback) as StylesheetWebFontLoadFinishedCallback,
853 document_context,
854 );
855 }
856
857 fn can_skip_reflow_request_entirely(&self, reflow_request: &ReflowRequest) -> bool {
861 if reflow_request.restyle.is_some() {
863 return false;
864 }
865 if self.fragment_tree.borrow().is_none() {
867 return false;
868 }
869 if self.needs_accessibility_update() {
871 return false;
872 }
873
874 let necessary_phases = ReflowPhases::necessary(&reflow_request.reflow_goal);
877 if necessary_phases.is_empty() {
878 return true;
879 }
880
881 if necessary_phases == ReflowPhases::StackingContextTreeConstruction {
884 return self.stacking_context_tree.borrow().is_some() &&
885 !self.need_new_stacking_context_tree.get();
886 }
887
888 assert_eq!(
891 necessary_phases,
892 ReflowPhases::StackingContextTreeConstruction | ReflowPhases::DisplayListConstruction
893 );
894 !self.need_new_display_list.get()
895 }
896
897 fn maybe_print_reflow_event(&self, reflow_request: &ReflowRequest) {
898 if !self.debug.relayout_event {
899 return;
900 }
901
902 println!(
903 "**** Reflow({}) => {:?}, {:?}",
904 self.id,
905 reflow_request.reflow_goal,
906 reflow_request
907 .restyle
908 .as_ref()
909 .map(|restyle| restyle.reason)
910 .unwrap_or_default()
911 );
912 }
913
914 fn handle_update_scroll_node_request(&self, reflow_request: &ReflowRequest) -> bool {
917 if let ReflowGoal::UpdateScrollNode(external_scroll_id, offset) = reflow_request.reflow_goal
918 {
919 self.set_scroll_offset_from_script(external_scroll_id, offset)
920 } else {
921 false
922 }
923 }
924
925 fn handle_accessibility_tree_update(&self, root_element: &ServoLayoutNode) -> bool {
926 if !self.needs_accessibility_update() {
927 return false;
928 }
929 let mut accessibility_tree = self.accessibility_tree.borrow_mut();
930 let Some(accessibility_tree) = accessibility_tree.as_mut() else {
931 return false;
932 };
933
934 let accessibility_tree = &mut *accessibility_tree;
935 if let Some(tree_update) = accessibility_tree.update_tree(root_element) {
936 let _ = self
941 .embedder_chan
942 .send(EmbedderMsg::AccessibilityTreeUpdate(
943 self.webview_id,
944 tree_update,
945 accessibility_tree.epoch(),
946 ));
947 }
948 self.needs_accessibility_update.set(false);
949 true
950 }
951
952 #[servo_tracing::instrument(skip_all)]
954 fn handle_reflow(&mut self, mut reflow_request: ReflowRequest) -> Option<ReflowResult> {
955 self.maybe_print_reflow_event(&reflow_request);
956
957 if self.can_skip_reflow_request_entirely(&reflow_request) {
958 return self
960 .handle_update_scroll_node_request(&reflow_request)
961 .then(|| ReflowResult {
962 reflow_phases_run: ReflowPhasesRun::UpdatedScrollNodeOffset,
963 ..Default::default()
964 });
965 }
966
967 let document = unsafe { ServoLayoutNode::new(&reflow_request.document) };
968 let document = unsafe { document.dangerous_style_node() }
969 .as_document()
970 .unwrap();
971 let Some(root_element) = document.root_element() else {
972 if !self.last_display_list_was_empty.get() {
973 return self.clear_layout_trees_and_send_empty_display_list(&reflow_request);
974 }
975 debug!("layout: No root node: bailing");
976 return None;
977 };
978
979 let image_resolver = Arc::new(ImageResolver {
980 origin: reflow_request.origin.clone(),
981 image_cache: self.image_cache.clone(),
982 resolved_images_cache: self.resolved_images_cache.clone(),
983 pending_images: Mutex::default(),
984 pending_rasterization_images: Mutex::default(),
985 pending_svg_elements_for_serialization: Mutex::default(),
986 animating_images: reflow_request.animating_images.clone(),
987 animation_timeline_value: reflow_request.animation_timeline_value,
988 });
989 let mut reflow_statistics = Default::default();
990
991 let (mut reflow_phases_run, iframe_sizes) = self.restyle_and_build_trees(
992 &mut reflow_request,
993 document,
994 root_element,
995 &image_resolver,
996 );
997 if self.calculate_overflow() {
998 reflow_phases_run.insert(ReflowPhasesRun::CalculatedOverflow);
999 }
1000 if self.build_stacking_context_tree_for_reflow(&reflow_request) {
1001 reflow_phases_run.insert(ReflowPhasesRun::BuiltStackingContextTree);
1002 }
1003 if self.build_display_list(&reflow_request, &image_resolver, &mut reflow_statistics) {
1004 reflow_phases_run.insert(ReflowPhasesRun::BuiltDisplayList);
1005 }
1006 if self.handle_update_scroll_node_request(&reflow_request) {
1007 reflow_phases_run.insert(ReflowPhasesRun::UpdatedScrollNodeOffset);
1008 }
1009 if self.handle_accessibility_tree_update(&root_element.as_node()) {
1010 reflow_phases_run.insert(ReflowPhasesRun::UpdatedAccessibilityTree);
1011 }
1012
1013 let pending_images = std::mem::take(&mut *image_resolver.pending_images.lock());
1014 let pending_rasterization_images =
1015 std::mem::take(&mut *image_resolver.pending_rasterization_images.lock());
1016 let pending_svg_elements_for_serialization =
1017 std::mem::take(&mut *image_resolver.pending_svg_elements_for_serialization.lock());
1018
1019 Some(ReflowResult {
1020 reflow_phases_run,
1021 pending_images,
1022 pending_rasterization_images,
1023 pending_svg_elements_for_serialization,
1024 iframe_sizes: Some(iframe_sizes),
1025 reflow_statistics,
1026 })
1027 }
1028
1029 #[servo_tracing::instrument(skip_all)]
1030 fn prepare_stylist_for_reflow<'dom>(
1031 &mut self,
1032 reflow_request: &ReflowRequest,
1033 document: ServoDangerousStyleDocument<'dom>,
1034 guards: &StylesheetGuards,
1035 ua_stylesheets: &UserAgentStylesheets,
1036 ) -> StylesheetInvalidationSet {
1037 if !self.have_added_user_agent_stylesheets {
1038 for stylesheet in &ua_stylesheets.user_agent_stylesheets {
1039 self.stylist
1040 .append_stylesheet(stylesheet.clone(), guards.ua_or_user);
1041 self.load_all_web_fonts_from_stylesheet_with_guard(
1042 stylesheet,
1043 guards.ua_or_user,
1044 &reflow_request.document_context,
1045 );
1046 }
1047
1048 for user_stylesheet in self.user_stylesheets.iter() {
1049 self.stylist
1050 .append_stylesheet(user_stylesheet.clone(), guards.ua_or_user);
1051 self.load_all_web_fonts_from_stylesheet_with_guard(
1052 user_stylesheet,
1053 guards.ua_or_user,
1054 &reflow_request.document_context,
1055 );
1056 }
1057
1058 if self.stylist.quirks_mode() == QuirksMode::Quirks {
1059 self.stylist.append_stylesheet(
1060 ua_stylesheets.quirks_mode_stylesheet.clone(),
1061 guards.ua_or_user,
1062 );
1063 self.load_all_web_fonts_from_stylesheet_with_guard(
1064 &ua_stylesheets.quirks_mode_stylesheet,
1065 guards.ua_or_user,
1066 &reflow_request.document_context,
1067 );
1068 }
1069 self.have_added_user_agent_stylesheets = true;
1070 }
1071
1072 if reflow_request.stylesheets_changed() {
1073 self.stylist
1074 .force_stylesheet_origins_dirty(Origin::Author.into());
1075 }
1076
1077 document.flush_shadow_root_stylesheets_if_necessary(&mut self.stylist, guards.author);
1078
1079 self.stylist.flush(guards)
1080 }
1081
1082 #[servo_tracing::instrument(skip_all)]
1083 fn restyle_and_build_trees(
1084 &mut self,
1085 reflow_request: &mut ReflowRequest,
1086 document: ServoDangerousStyleDocument<'_>,
1087 root_element: ServoLayoutElement<'_>,
1088 image_resolver: &Arc<ImageResolver>,
1089 ) -> (ReflowPhasesRun, IFrameSizes) {
1090 let mut snapshot_map = SnapshotMap::new();
1091 let _snapshot_setter = match reflow_request.restyle.as_mut() {
1092 Some(restyle) => SnapshotSetter::new(restyle, &mut snapshot_map),
1093 None => return Default::default(),
1094 };
1095
1096 let document_shared_lock = document.style_shared_lock();
1097 let author_guard = document_shared_lock.read();
1098 let ua_stylesheets = &*UA_STYLESHEETS;
1099 let guards = StylesheetGuards {
1100 author: &author_guard,
1101 ua_or_user: &GLOBAL_STYLE_DATA.shared_lock.read(),
1102 };
1103
1104 let rayon_pool = STYLE_THREAD_POOL.lock();
1105 let rayon_pool = rayon_pool.pool();
1106 let rayon_pool = rayon_pool.as_ref();
1107
1108 let device_has_changed = std::mem::replace(&mut self.device_has_changed, false);
1109 let dangerous_root_element = unsafe { root_element.dangerous_style_element() };
1110 if device_has_changed {
1111 let sheet_origins_affected_by_device_change = self
1112 .stylist
1113 .media_features_change_changed_style(&guards, self.device());
1114 self.stylist
1115 .force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
1116
1117 if let Some(mut data) = dangerous_root_element.mutate_data() {
1118 data.hint.insert(RestyleHint::recascade_subtree());
1119 }
1120 }
1121
1122 self.prepare_stylist_for_reflow(reflow_request, document, &guards, ua_stylesheets)
1123 .process_style(dangerous_root_element, Some(&snapshot_map));
1124
1125 if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
1126 self.need_new_display_list.set(true);
1129 }
1130
1131 let layout_context = LayoutContext {
1132 style_context: self.build_shared_style_context(
1133 guards,
1134 &snapshot_map,
1135 reflow_request.animation_timeline_value,
1136 &reflow_request.animations,
1137 match reflow_request.stylesheets_changed() {
1138 true => TraversalFlags::ForCSSRuleChanges,
1139 false => TraversalFlags::empty(),
1140 },
1141 ),
1142 font_context: self.font_context.clone(),
1143 iframe_sizes: Mutex::default(),
1144 use_rayon: rayon_pool.is_some(),
1145 image_resolver: image_resolver.clone(),
1146 painter_id: self.webview_id.into(),
1147 };
1148
1149 let restyle = reflow_request
1150 .restyle
1151 .as_ref()
1152 .expect("Should not get here if there is not restyle.");
1153
1154 let recalc_style_traversal;
1155 let dirty_root;
1156 {
1157 let _span = profile_traits::trace_span!("Styling").entered();
1158
1159 let original_dirty_root = unsafe {
1160 ServoLayoutNode::new(&restyle.dirty_root.unwrap())
1161 .as_element()
1162 .unwrap()
1163 .dangerous_style_element()
1164 };
1165
1166 recalc_style_traversal = RecalcStyle::new(&layout_context);
1167 let token = {
1168 let shared = DomTraversal::<ServoDangerousStyleElement>::shared_context(
1169 &recalc_style_traversal,
1170 );
1171 RecalcStyle::pre_traverse(original_dirty_root, shared)
1172 };
1173
1174 if !token.should_traverse() {
1175 layout_context.style_context.stylist.rule_tree().maybe_gc();
1176 return Default::default();
1177 }
1178
1179 dirty_root = driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
1180 }
1181
1182 let root_node = root_element.as_node();
1183 let damage_from_environment = if device_has_changed {
1184 RestyleDamage::RELAYOUT
1185 } else {
1186 Default::default()
1187 };
1188
1189 let mut box_tree = self.box_tree.borrow_mut();
1190 let damage = {
1191 let box_tree = &mut *box_tree;
1192 let mut compute_damage_and_build_box_tree = || {
1193 compute_damage_and_rebuild_box_tree(
1194 box_tree,
1195 &layout_context,
1196 dirty_root.layout_node(),
1197 root_node,
1198 damage_from_environment,
1199 )
1200 };
1201
1202 if let Some(pool) = rayon_pool {
1203 pool.install(compute_damage_and_build_box_tree)
1204 } else {
1205 compute_damage_and_build_box_tree()
1206 }
1207 };
1208
1209 if damage.contains(RestyleDamage::RECALCULATE_OVERFLOW) {
1210 self.need_overflow_calculation.set(true);
1211 }
1212 if damage.contains(RestyleDamage::REBUILD_STACKING_CONTEXT) {
1213 self.need_new_stacking_context_tree.set(true);
1214 }
1215 if damage.contains(RestyleDamage::REPAINT) {
1216 self.need_new_display_list.set(true);
1217 }
1218 if !damage.contains(RestyleDamage::RELAYOUT) {
1219 layout_context.style_context.stylist.rule_tree().maybe_gc();
1220 return (ReflowPhasesRun::empty(), IFrameSizes::default());
1221 }
1222
1223 let box_tree = &*box_tree;
1224 let viewport_size = self.stylist.device().au_viewport_size();
1225 let run_layout = || {
1226 box_tree
1227 .as_ref()
1228 .unwrap()
1229 .layout(recalc_style_traversal.context(), viewport_size)
1230 };
1231 let fragment_tree = Rc::new(if let Some(pool) = rayon_pool {
1232 pool.install(run_layout)
1233 } else {
1234 run_layout()
1235 });
1236
1237 *self.fragment_tree.borrow_mut() = Some(fragment_tree);
1238
1239 if self.debug.style_tree {
1240 println!(
1241 "{:?}",
1242 ShowSubtreeDataAndPrimaryValues(dangerous_root_element.as_node())
1243 );
1244 }
1245 if self.debug.rule_tree {
1246 recalc_style_traversal
1247 .context()
1248 .style_context
1249 .stylist
1250 .rule_tree()
1251 .dump_stdout(&layout_context.style_context.guards);
1252 }
1253
1254 layout_context.style_context.stylist.rule_tree().maybe_gc();
1256
1257 let mut iframe_sizes = layout_context.iframe_sizes.lock();
1258 (
1259 ReflowPhasesRun::RanLayout,
1260 std::mem::take(&mut *iframe_sizes),
1261 )
1262 }
1263
1264 #[servo_tracing::instrument(name = "Overflow Calculation", skip_all)]
1265 fn calculate_overflow(&self) -> bool {
1266 if !self.need_overflow_calculation.get() {
1267 return false;
1268 }
1269
1270 if let Some(fragment_tree) = &*self.fragment_tree.borrow() {
1271 fragment_tree.calculate_scrollable_overflow();
1272 if self.debug.flow_tree {
1273 fragment_tree.print();
1274 }
1275 }
1276
1277 self.need_overflow_calculation.set(false);
1278 assert!(self.need_new_display_list.get());
1279 assert!(self.need_new_stacking_context_tree.get());
1280
1281 true
1282 }
1283
1284 fn build_stacking_context_tree_for_reflow(&self, reflow_request: &ReflowRequest) -> bool {
1285 if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1286 .contains(ReflowPhases::StackingContextTreeConstruction)
1287 {
1288 return false;
1289 }
1290 if !self.need_new_stacking_context_tree.get() {
1291 return false;
1292 }
1293
1294 self.build_stacking_context_tree(reflow_request.viewport_details)
1295 }
1296
1297 #[servo_tracing::instrument(name = "Stacking Context Tree Construction", skip_all)]
1298 fn build_stacking_context_tree(&self, viewport_details: ViewportDetails) -> bool {
1299 let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1300 return false;
1301 };
1302
1303 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1304 let old_scroll_offsets = stacking_context_tree
1305 .as_ref()
1306 .map(|tree| tree.paint_info.scroll_tree.scroll_offsets());
1307
1308 let mut new_stacking_context_tree = StackingContextTree::new(
1312 fragment_tree,
1313 viewport_details,
1314 self.id.into(),
1315 !self.have_ever_generated_display_list.get(),
1316 &self.debug,
1317 );
1318
1319 if let Some(old_scroll_offsets) = old_scroll_offsets {
1323 new_stacking_context_tree
1324 .paint_info
1325 .scroll_tree
1326 .set_all_scroll_offsets(&old_scroll_offsets);
1327 }
1328
1329 if self.debug.scroll_tree {
1330 new_stacking_context_tree
1331 .paint_info
1332 .scroll_tree
1333 .debug_print();
1334 }
1335
1336 *stacking_context_tree = Some(new_stacking_context_tree);
1337
1338 self.need_new_stacking_context_tree.set(false);
1340 assert!(self.need_new_display_list.get());
1341
1342 true
1343 }
1344
1345 #[servo_tracing::instrument(name = "Display List Construction", skip_all)]
1348 fn build_display_list(
1349 &self,
1350 reflow_request: &ReflowRequest,
1351 image_resolver: &Arc<ImageResolver>,
1352 reflow_statistics: &mut ReflowStatistics,
1353 ) -> bool {
1354 if !ReflowPhases::necessary(&reflow_request.reflow_goal)
1355 .contains(ReflowPhases::DisplayListConstruction)
1356 {
1357 return false;
1358 }
1359 let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
1360 return false;
1361 };
1362 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1363 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1364 return false;
1365 };
1366
1367 if !self.need_new_display_list.get() {
1371 return false;
1372 }
1373
1374 stacking_context_tree.paint_info.epoch = reflow_request.epoch;
1377
1378 let mut paint_timing_handler = self.paint_timing_handler.borrow_mut();
1379 let paint_timing_handler = match paint_timing_handler.as_mut() {
1381 Some(paint_timing_handler) => paint_timing_handler,
1382 None => {
1383 *paint_timing_handler = Some(PaintTimingHandler::new(
1384 stacking_context_tree
1385 .paint_info
1386 .viewport_details
1387 .layout_size(),
1388 ));
1389 paint_timing_handler.as_mut().unwrap()
1390 },
1391 };
1392
1393 let built_display_list = DisplayListBuilder::build(
1394 stacking_context_tree,
1395 fragment_tree,
1396 image_resolver.clone(),
1397 self.device().device_pixel_ratio(),
1398 reflow_request.highlighted_dom_node,
1399 &self.debug,
1400 paint_timing_handler,
1401 reflow_statistics,
1402 );
1403 self.paint_api.send_display_list(
1404 self.webview_id,
1405 &stacking_context_tree.paint_info,
1406 built_display_list,
1407 );
1408
1409 if paint_timing_handler.did_lcp_candidate_update() {
1410 if let Some(lcp_candidate) = paint_timing_handler.largest_contentful_paint_candidate() {
1411 self.paint_api.send_lcp_candidate(
1412 lcp_candidate,
1413 self.webview_id,
1414 self.id,
1415 stacking_context_tree.paint_info.epoch,
1416 );
1417 paint_timing_handler.unset_lcp_candidate_updated();
1418 }
1419 }
1420
1421 let (keys, instance_keys) = self
1422 .font_context
1423 .collect_unused_webrender_resources(false );
1424 self.paint_api
1425 .remove_unused_font_resources(self.webview_id.into(), keys, instance_keys);
1426 self.last_display_list_was_empty.set(false);
1427 self.have_ever_generated_display_list.set(true);
1428 self.need_new_display_list.set(false);
1429 self.previously_highlighted_dom_node
1430 .set(reflow_request.highlighted_dom_node);
1431 true
1432 }
1433
1434 fn set_scroll_offset_from_script(
1435 &self,
1436 external_scroll_id: ExternalScrollId,
1437 offset: LayoutVector2D,
1438 ) -> bool {
1439 let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
1440 let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
1441 return false;
1442 };
1443
1444 if let Some(offset) = stacking_context_tree
1445 .paint_info
1446 .scroll_tree
1447 .set_scroll_offset_for_node_with_external_scroll_id(
1448 external_scroll_id,
1449 offset,
1450 ScrollType::Script,
1451 )
1452 {
1453 self.paint_api.scroll_node_by_delta(
1454 self.webview_id,
1455 self.id.into(),
1456 offset,
1457 external_scroll_id,
1458 );
1459 true
1460 } else {
1461 false
1462 }
1463 }
1464
1465 fn profiler_metadata(&self) -> Option<TimerMetadata> {
1467 Some(TimerMetadata {
1468 url: self.url.to_string(),
1469 iframe: if self.is_iframe {
1470 TimerMetadataFrameType::IFrame
1471 } else {
1472 TimerMetadataFrameType::RootWindow
1473 },
1474 incremental: if self.have_ever_generated_display_list.get() {
1475 TimerMetadataReflowType::Incremental
1476 } else {
1477 TimerMetadataReflowType::FirstReflow
1478 },
1479 })
1480 }
1481
1482 fn clear_layout_trees_and_send_empty_display_list(
1484 &self,
1485 reflow_request: &ReflowRequest,
1486 ) -> Option<ReflowResult> {
1487 self.box_tree.borrow_mut().take();
1489 self.fragment_tree.borrow_mut().take();
1490 self.stacking_context_tree.borrow_mut().take();
1491
1492 let paint_info = PaintDisplayListInfo::new(
1494 reflow_request.viewport_details,
1495 Size2D::zero(),
1496 self.id.into(),
1497 reflow_request.epoch,
1498 AxesScrollSensitivity {
1499 x: ScrollType::InputEvents | ScrollType::Script,
1500 y: ScrollType::InputEvents | ScrollType::Script,
1501 },
1502 !self.have_ever_generated_display_list.get(),
1503 );
1504 let mut builder = webrender_api::DisplayListBuilder::new(paint_info.pipeline_id);
1505 builder.begin();
1506 let (_, empty_display_list) = builder.end();
1507
1508 self.paint_api
1509 .send_display_list(self.webview_id, &paint_info, empty_display_list);
1510 self.last_display_list_was_empty.set(true);
1511 self.have_ever_generated_display_list.set(true);
1512
1513 Some(ReflowResult {
1514 reflow_phases_run: ReflowPhasesRun::BuiltDisplayList,
1515 ..Default::default()
1516 })
1517 }
1518}
1519
1520fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
1521 fn parse_ua_stylesheet(
1522 shared_lock: &SharedRwLock,
1523 filename: &str,
1524 content: &[u8],
1525 ) -> Result<DocumentStyleSheet, &'static str> {
1526 let url = Url::parse(&format!("chrome://resources/{:?}", filename))
1527 .ok()
1528 .unwrap();
1529 Ok(DocumentStyleSheet(ServoArc::new(Stylesheet::from_bytes(
1530 content,
1531 url.into(),
1532 None,
1533 None,
1534 Origin::UserAgent,
1535 ServoArc::new(shared_lock.wrap(MediaList::empty())),
1536 shared_lock.clone(),
1537 None,
1538 None,
1539 QuirksMode::NoQuirks,
1540 ))))
1541 }
1542
1543 let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
1544
1545 let user_agent_stylesheets = vec![
1548 parse_ua_stylesheet(shared_lock, "user-agent.css", USER_AGENT_CSS)?,
1549 parse_ua_stylesheet(shared_lock, "servo.css", SERVO_CSS)?,
1550 parse_ua_stylesheet(
1551 shared_lock,
1552 "presentational-hints.css",
1553 PRESENTATIONAL_HINTS_CSS,
1554 )?,
1555 ];
1556
1557 let quirks_mode_stylesheet =
1558 parse_ua_stylesheet(shared_lock, "quirks-mode.css", QUIRKS_MODE_CSS)?;
1559
1560 Ok(UserAgentStylesheets {
1561 user_agent_stylesheets,
1562 quirks_mode_stylesheet,
1563 })
1564}
1565
1566pub struct UserAgentStylesheets {
1568 pub user_agent_stylesheets: Vec<DocumentStyleSheet>,
1570 pub quirks_mode_stylesheet: DocumentStyleSheet,
1572}
1573
1574static UA_STYLESHEETS: LazyLock<UserAgentStylesheets> =
1575 LazyLock::new(|| match get_ua_stylesheets() {
1576 Ok(stylesheets) => stylesheets,
1577 Err(filename) => {
1578 error!("Failed to load UA stylesheet {}!", filename);
1579 process::exit(1);
1580 },
1581 });
1582
1583struct RegisteredPainterImpl {
1584 painter: Box<dyn Painter>,
1585 name: Atom,
1586 properties: FxHashMap<Atom, PropertyId>,
1588}
1589
1590impl SpeculativePainter for RegisteredPainterImpl {
1591 fn speculatively_draw_a_paint_image(
1592 &self,
1593 properties: Vec<(Atom, String)>,
1594 arguments: Vec<String>,
1595 ) {
1596 self.painter
1597 .speculatively_draw_a_paint_image(properties, arguments);
1598 }
1599}
1600
1601impl RegisteredSpeculativePainter for RegisteredPainterImpl {
1602 fn properties(&self) -> &FxHashMap<Atom, PropertyId> {
1603 &self.properties
1604 }
1605 fn name(&self) -> Atom {
1606 self.name.clone()
1607 }
1608}
1609
1610impl Painter for RegisteredPainterImpl {
1611 fn draw_a_paint_image(
1612 &self,
1613 size: Size2D<f32, CSSPixel>,
1614 device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
1615 properties: Vec<(Atom, String)>,
1616 arguments: Vec<String>,
1617 ) -> Result<DrawAPaintImageResult, PaintWorkletError> {
1618 self.painter
1619 .draw_a_paint_image(size, device_pixel_ratio, properties, arguments)
1620 }
1621}
1622
1623struct RegisteredPaintersImpl(HashMap<Atom, RegisteredPainterImpl>);
1624
1625impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
1626 fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> {
1627 self.0
1628 .get(name)
1629 .map(|painter| painter as &dyn RegisteredSpeculativePainter)
1630 }
1631}
1632
1633struct LayoutFontMetricsProvider(Arc<FontContext>);
1634
1635impl FontMetricsProvider for LayoutFontMetricsProvider {
1636 fn query_font_metrics(
1637 &self,
1638 _vertical: bool,
1639 font: &Font,
1640 base_size: CSSPixelLength,
1641 _flags: QueryFontMetricsFlags,
1642 ) -> FontMetrics {
1643 let font_context = &self.0;
1644 let font_group = self
1645 .0
1646 .font_group_with_size(ServoArc::new(font.clone()), base_size.into());
1647
1648 let Some(first_font_metrics) = font_group
1649 .first(font_context)
1650 .map(|font| font.metrics.clone())
1651 else {
1652 return Default::default();
1653 };
1654
1655 let x_height = Some(first_font_metrics.x_height)
1658 .filter(|x_height| !x_height.is_zero())
1659 .map(CSSPixelLength::from);
1660
1661 let zero_advance_measure = first_font_metrics
1662 .zero_horizontal_advance
1663 .or_else(|| {
1664 font_group
1665 .find_by_codepoint(font_context, '0', None, Language::UND)?
1666 .metrics
1667 .zero_horizontal_advance
1668 })
1669 .map(CSSPixelLength::from);
1670
1671 let ic_width = first_font_metrics
1672 .ic_horizontal_advance
1673 .or_else(|| {
1674 font_group
1675 .find_by_codepoint(font_context, '\u{6C34}', None, Language::UND)?
1676 .metrics
1677 .ic_horizontal_advance
1678 })
1679 .map(CSSPixelLength::from);
1680
1681 FontMetrics {
1682 x_height,
1683 zero_advance_measure,
1684 cap_height: None,
1685 ic_width,
1686 ascent: first_font_metrics.ascent.into(),
1687 script_percent_scale_down: None,
1688 script_script_percent_scale_down: None,
1689 }
1690 }
1691
1692 fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {
1693 Length::new(match generic {
1694 GenericFontFamily::Monospace => pref!(fonts_default_monospace_size),
1695 _ => pref!(fonts_default_size),
1696 } as f32)
1697 .max(Length::new(0.0))
1698 }
1699}
1700
1701impl Debug for LayoutFontMetricsProvider {
1702 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1703 f.debug_tuple("LayoutFontMetricsProvider").finish()
1704 }
1705}
1706
1707struct SnapshotSetter<'dom> {
1708 elements_with_snapshot: Vec<ServoLayoutElement<'dom>>,
1709}
1710
1711impl SnapshotSetter<'_> {
1712 fn new(restyle: &mut ReflowRequestRestyle, snapshot_map: &mut SnapshotMap) -> Self {
1713 debug!("Draining restyles: {}", restyle.pending_restyles.len());
1714 let restyles = std::mem::take(&mut restyle.pending_restyles);
1715
1716 let elements_with_snapshot: Vec<_> = restyles
1717 .iter()
1718 .filter(|r| r.1.snapshot.is_some())
1719 .map(|r| unsafe { ServoLayoutNode::new(&r.0).as_element().unwrap() })
1720 .collect();
1721
1722 for (element, restyle) in restyles {
1723 let element = unsafe { ServoLayoutNode::new(&element).as_element().unwrap() };
1724
1725 let Some(mut style_data) = element
1728 .style_data()
1729 .map(|data| data.element_data.borrow_mut())
1730 else {
1731 element.unset_snapshot_flags();
1732 continue;
1733 };
1734
1735 debug!("Noting restyle for {:?}: {:?}", element, style_data);
1736 if let Some(s) = restyle.snapshot {
1737 element.set_has_snapshot();
1738 snapshot_map.insert(element.as_node().opaque(), s);
1739 }
1740
1741 style_data.hint.insert(restyle.hint);
1743 style_data.damage = restyle.damage;
1744 }
1745 Self {
1746 elements_with_snapshot,
1747 }
1748 }
1749}
1750
1751impl Drop for SnapshotSetter<'_> {
1752 fn drop(&mut self) {
1753 for element in &self.elements_with_snapshot {
1754 element.unset_snapshot_flags();
1755 }
1756 }
1757}
1758
1759bitflags! {
1760 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
1761 pub struct ReflowPhases: u8 {
1762 const StackingContextTreeConstruction = 1 << 0;
1763 const DisplayListConstruction = 1 << 1;
1764 }
1765}
1766
1767impl ReflowPhases {
1768 fn necessary(reflow_goal: &ReflowGoal) -> Self {
1772 match reflow_goal {
1773 ReflowGoal::LayoutQuery(query) => match query {
1774 QueryMsg::NodesFromPointQuery => {
1775 Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1776 },
1777 QueryMsg::BoxArea |
1778 QueryMsg::BoxAreas |
1779 QueryMsg::ElementsFromPoint |
1780 QueryMsg::OffsetParentQuery |
1781 QueryMsg::ResolvedStyleQuery |
1782 QueryMsg::ScrollingAreaOrOffsetQuery |
1783 QueryMsg::TextIndexQuery => Self::StackingContextTreeConstruction,
1784 QueryMsg::ClientRectQuery |
1785 QueryMsg::CurrentCSSZoomQuery |
1786 QueryMsg::EffectiveOverflow |
1787 QueryMsg::ElementInnerOuterTextQuery |
1788 QueryMsg::InnerWindowDimensionsQuery |
1789 QueryMsg::PaddingQuery |
1790 QueryMsg::ResolvedFontStyleQuery |
1791 QueryMsg::ScrollParentQuery |
1792 QueryMsg::StyleQuery => Self::empty(),
1793 },
1794 ReflowGoal::UpdateScrollNode(..) | ReflowGoal::UpdateTheRendering => {
1795 Self::StackingContextTreeConstruction | Self::DisplayListConstruction
1796 },
1797 }
1798 }
1799}