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