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