1use std::cell::{OnceCell, RefCell};
6use std::sync::Arc;
7
8use app_units::{AU_PER_PX, Au};
9use base::id::ScrollTreeNodeId;
10use clip::{Clip, ClipId};
11use compositing_traits::display_list::{CompositorDisplayListInfo, SpatialTreeNodeInfo};
12use euclid::{Point2D, Scale, SideOffsets2D, Size2D, UnknownUnit, Vector2D};
13use fonts::GlyphStore;
14use gradient::WebRenderGradient;
15use net_traits::image_cache::Image as CachedImage;
16use range::Range as ServoRange;
17use servo_arc::Arc as ServoArc;
18use servo_config::opts::DebugOptions;
19use servo_geometry::MaxRect;
20use style::Zero;
21use style::color::{AbsoluteColor, ColorSpace};
22use style::computed_values::border_image_outset::T as BorderImageOutset;
23use style::computed_values::text_decoration_style::{
24 T as ComputedTextDecorationStyle, T as TextDecorationStyle,
25};
26use style::dom::OpaqueNode;
27use style::properties::ComputedValues;
28use style::properties::longhands::visibility::computed_value::T as Visibility;
29use style::properties::style_structs::Border;
30use style::values::computed::{
31 BorderImageSideWidth, BorderImageWidth, BorderStyle, LengthPercentage,
32 NonNegativeLengthOrNumber, NumberOrPercentage, OutlineStyle,
33};
34use style::values::generics::NonNegative;
35use style::values::generics::rect::Rect;
36use style::values::specified::text::TextDecorationLine;
37use style_traits::{CSSPixel as StyloCSSPixel, DevicePixel as StyloDevicePixel};
38use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutRect, LayoutSize};
39use webrender_api::{
40 self as wr, BorderDetails, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipChainId,
41 ClipMode, CommonItemProperties, ComplexClipRegion, NinePatchBorder, NinePatchBorderSource,
42 PrimitiveFlags, PropertyBinding, SpatialId, SpatialTreeItemKey, units,
43};
44use wr::units::LayoutVector2D;
45
46use crate::cell::ArcRefCell;
47use crate::context::{ImageResolver, ResolvedImage};
48pub(crate) use crate::display_list::conversions::ToWebRender;
49use crate::display_list::stacking_context::StackingContextSection;
50use crate::fragment_tree::{
51 BackgroundMode, BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo, Tag,
52 TextFragment,
53};
54use crate::geom::{
55 LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize,
56};
57use crate::replaced::NaturalSizes;
58use crate::style_ext::{BorderStyleColor, ComputedValuesExt};
59
60mod background;
61mod clip;
62mod conversions;
63mod gradient;
64mod hit_test;
65mod stacking_context;
66
67use background::BackgroundPainter;
68pub(crate) use hit_test::HitTest;
69pub(crate) use stacking_context::*;
70
71const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(AU_PER_PX);
72
73pub(crate) struct DisplayListBuilder<'a> {
74 current_scroll_node_id: ScrollTreeNodeId,
79
80 current_reference_frame_scroll_node_id: ScrollTreeNodeId,
84
85 current_clip_id: ClipId,
90
91 pub webrender_display_list_builder: &'a mut wr::DisplayListBuilder,
93
94 pub compositor_info: &'a mut CompositorDisplayListInfo,
96
97 inspector_highlight: Option<InspectorHighlight>,
102
103 paint_body_background: bool,
107
108 clip_map: Vec<ClipChainId>,
111
112 image_resolver: Arc<ImageResolver>,
114
115 device_pixel_ratio: Scale<f32, StyloCSSPixel, StyloDevicePixel>,
117}
118
119struct InspectorHighlight {
120 tag: Tag,
122
123 state: Option<HighlightTraversalState>,
128}
129
130struct HighlightTraversalState {
131 content_box: euclid::Rect<Au, StyloCSSPixel>,
134
135 spatial_id: SpatialId,
136
137 clip_chain_id: ClipChainId,
138
139 maybe_box_fragment: Option<ArcRefCell<BoxFragment>>,
142}
143
144impl InspectorHighlight {
145 fn for_node(node: OpaqueNode) -> Self {
146 Self {
147 tag: Tag {
148 node,
149 pseudo_element_chain: Default::default(),
151 },
152 state: None,
153 }
154 }
155}
156
157impl DisplayListBuilder<'_> {
158 pub(crate) fn build(
159 stacking_context_tree: &mut StackingContextTree,
160 fragment_tree: &FragmentTree,
161 image_resolver: Arc<ImageResolver>,
162 device_pixel_ratio: Scale<f32, StyloCSSPixel, StyloDevicePixel>,
163 highlighted_dom_node: Option<OpaqueNode>,
164 debug: &DebugOptions,
165 ) -> BuiltDisplayList {
166 let compositor_info = &mut stacking_context_tree.compositor_info;
168 let pipeline_id = compositor_info.pipeline_id;
169 let mut webrender_display_list_builder =
170 webrender_api::DisplayListBuilder::new(pipeline_id);
171 webrender_display_list_builder.begin();
172
173 if debug.dump_display_list {
178 webrender_display_list_builder.dump_serialized_display_list();
179 }
180
181 #[cfg(feature = "tracing")]
182 let _span =
183 tracing::trace_span!("DisplayListBuilder::build", servo_profiling = true).entered();
184 let mut builder = DisplayListBuilder {
185 current_scroll_node_id: compositor_info.root_reference_frame_id,
186 current_reference_frame_scroll_node_id: compositor_info.root_reference_frame_id,
187 current_clip_id: ClipId::INVALID,
188 webrender_display_list_builder: &mut webrender_display_list_builder,
189 compositor_info,
190 inspector_highlight: highlighted_dom_node.map(InspectorHighlight::for_node),
191 paint_body_background: true,
192 clip_map: Default::default(),
193 image_resolver,
194 device_pixel_ratio,
195 };
196
197 builder.add_all_spatial_nodes();
198
199 for clip in stacking_context_tree.clip_store.0.iter() {
200 builder.add_clip_to_display_list(clip);
201 }
202
203 let pipeline_id = builder.compositor_info.pipeline_id;
206 let viewport_size = builder.compositor_info.viewport_details.size;
207 let viewport_rect = LayoutRect::from_size(viewport_size.cast_unit());
208 builder.wr().push_hit_test(
209 viewport_rect,
210 ClipChainId::INVALID,
211 SpatialId::root_reference_frame(pipeline_id),
212 PrimitiveFlags::default(),
213 (0, 0), );
215
216 stacking_context_tree
218 .root_stacking_context
219 .build_canvas_background_display_list(&mut builder, fragment_tree);
220 stacking_context_tree
221 .root_stacking_context
222 .build_display_list(&mut builder);
223 builder.paint_dom_inspector_highlight();
224
225 webrender_display_list_builder.end().1
226 }
227
228 fn wr(&mut self) -> &mut wr::DisplayListBuilder {
229 self.webrender_display_list_builder
230 }
231
232 fn pipeline_id(&mut self) -> wr::PipelineId {
233 self.compositor_info.pipeline_id
234 }
235
236 fn mark_is_contentful(&mut self) {
237 self.compositor_info.is_contentful = true;
238 }
239
240 fn spatial_id(&self, id: ScrollTreeNodeId) -> SpatialId {
241 self.compositor_info.scroll_tree.webrender_id(&id)
242 }
243
244 fn clip_chain_id(&self, id: ClipId) -> ClipChainId {
245 match id {
246 ClipId::INVALID => ClipChainId::INVALID,
247 _ => *self
248 .clip_map
249 .get(id.0)
250 .expect("Should never try to get clip before adding it to WebRender display list"),
251 }
252 }
253
254 pub(crate) fn add_all_spatial_nodes(&mut self) {
255 let mut spatial_tree_count = 0;
259 let mut scroll_tree = std::mem::take(&mut self.compositor_info.scroll_tree);
260 let mut mapping = Vec::with_capacity(scroll_tree.nodes.len());
261
262 mapping.push(SpatialId::root_reference_frame(self.pipeline_id()));
263 mapping.push(SpatialId::root_scroll_node(self.pipeline_id()));
264
265 let pipeline_id = self.pipeline_id();
266 let pipeline_tag = ((pipeline_id.0 as u64) << 32) | pipeline_id.1 as u64;
267
268 for node in scroll_tree.nodes.iter().skip(2) {
269 let parent_scroll_node_id = node
270 .parent
271 .expect("Should have already added root reference frame");
272 let parent_spatial_node_id = mapping
273 .get(parent_scroll_node_id.index)
274 .expect("Should add spatial nodes to display list in order");
275
276 spatial_tree_count += 1;
279 let spatial_tree_item_key = SpatialTreeItemKey::new(pipeline_tag, spatial_tree_count);
280
281 mapping.push(match &node.info {
282 SpatialTreeNodeInfo::ReferenceFrame(info) => {
283 let spatial_id = self.wr().push_reference_frame(
284 info.origin,
285 *parent_spatial_node_id,
286 info.transform_style,
287 PropertyBinding::Value(*info.transform.to_transform()),
288 info.kind,
289 spatial_tree_item_key,
290 );
291 self.wr().pop_reference_frame();
292 spatial_id
293 },
294 SpatialTreeNodeInfo::Scroll(info) => {
295 self.wr().define_scroll_frame(
296 *parent_spatial_node_id,
297 info.external_id,
298 info.content_rect,
299 info.clip_rect,
300 LayoutVector2D::zero(), 0, wr::HasScrollLinkedEffect::No,
303 spatial_tree_item_key,
304 )
305 },
306 SpatialTreeNodeInfo::Sticky(info) => {
307 self.wr().define_sticky_frame(
308 *parent_spatial_node_id,
309 info.frame_rect,
310 info.margins,
311 info.vertical_offset_bounds,
312 info.horizontal_offset_bounds,
313 LayoutVector2D::zero(), spatial_tree_item_key,
315 None, )
317 },
318 });
319 }
320
321 scroll_tree.update_mapping(mapping);
322 self.compositor_info.scroll_tree = scroll_tree;
323 }
324
325 pub(crate) fn add_clip_to_display_list(&mut self, clip: &Clip) -> ClipChainId {
333 assert_eq!(
334 clip.id.0,
335 self.clip_map.len(),
336 "Clips should be added in order"
337 );
338
339 let spatial_id = self.spatial_id(clip.parent_scroll_node_id);
340 let new_clip_id = if clip.radii.is_zero() {
341 self.wr().define_clip_rect(spatial_id, clip.rect)
342 } else {
343 self.wr().define_clip_rounded_rect(
344 spatial_id,
345 ComplexClipRegion {
346 rect: clip.rect,
347 radii: clip.radii,
348 mode: ClipMode::Clip,
349 },
350 )
351 };
352
353 let parent_clip_chain_id = match self.clip_chain_id(clip.parent_clip_id) {
358 ClipChainId::INVALID => None,
359 parent => Some(parent),
360 };
361 let clip_chain_id = self
362 .wr()
363 .define_clip_chain(parent_clip_chain_id, [new_clip_id]);
364 self.clip_map.push(clip_chain_id);
365 clip_chain_id
366 }
367
368 fn maybe_create_clip(
372 &mut self,
373 radii: wr::BorderRadius,
374 rect: units::LayoutRect,
375 force_clip_creation: bool,
376 ) -> Option<ClipChainId> {
377 if radii.is_zero() && !force_clip_creation {
378 return None;
379 }
380
381 Some(self.add_clip_to_display_list(&Clip {
382 id: ClipId(self.clip_map.len()),
383 radii,
384 rect,
385 parent_scroll_node_id: self.current_scroll_node_id,
386 parent_clip_id: self.current_clip_id,
387 }))
388 }
389
390 fn common_properties(
391 &self,
392 clip_rect: units::LayoutRect,
393 style: &ComputedValues,
394 ) -> wr::CommonItemProperties {
395 wr::CommonItemProperties {
399 clip_rect,
400 spatial_id: self.spatial_id(self.current_scroll_node_id),
401 clip_chain_id: self.clip_chain_id(self.current_clip_id),
402 flags: style.get_webrender_primitive_flags(),
403 }
404 }
405
406 fn paint_dom_inspector_highlight(&mut self) {
408 let Some(highlight) = self
409 .inspector_highlight
410 .take()
411 .and_then(|highlight| highlight.state)
412 else {
413 return;
414 };
415
416 const CONTENT_BOX_HIGHLIGHT_COLOR: webrender_api::ColorF = webrender_api::ColorF {
417 r: 0.23,
418 g: 0.7,
419 b: 0.87,
420 a: 0.5,
421 };
422
423 const PADDING_BOX_HIGHLIGHT_COLOR: webrender_api::ColorF = webrender_api::ColorF {
424 r: 0.49,
425 g: 0.3,
426 b: 0.7,
427 a: 0.5,
428 };
429
430 const BORDER_BOX_HIGHLIGHT_COLOR: webrender_api::ColorF = webrender_api::ColorF {
431 r: 0.2,
432 g: 0.2,
433 b: 0.2,
434 a: 0.5,
435 };
436
437 const MARGIN_BOX_HIGHLIGHT_COLOR: webrender_api::ColorF = webrender_api::ColorF {
438 r: 1.,
439 g: 0.93,
440 b: 0.,
441 a: 0.5,
442 };
443
444 let content_box = highlight.content_box.to_webrender();
446 let properties = wr::CommonItemProperties {
447 clip_rect: content_box,
448 spatial_id: highlight.spatial_id,
449 clip_chain_id: highlight.clip_chain_id,
450 flags: wr::PrimitiveFlags::default(),
451 };
452
453 self.wr()
454 .push_rect(&properties, content_box, CONTENT_BOX_HIGHLIGHT_COLOR);
455
456 if let Some(box_fragment) = highlight.maybe_box_fragment {
458 let mut paint_highlight =
459 |color: webrender_api::ColorF,
460 fragment_relative_bounds: PhysicalRect<Au>,
461 widths: webrender_api::units::LayoutSideOffsets| {
462 if widths.is_zero() {
463 return;
464 }
465
466 let bounds = box_fragment
467 .borrow()
468 .offset_by_containing_block(&fragment_relative_bounds)
469 .to_webrender();
470
471 let border_style = wr::BorderSide {
473 color,
474 style: webrender_api::BorderStyle::Solid,
475 };
476
477 let details = wr::BorderDetails::Normal(wr::NormalBorder {
478 top: border_style,
479 right: border_style,
480 bottom: border_style,
481 left: border_style,
482 radius: webrender_api::BorderRadius::default(),
483 do_aa: true,
484 });
485
486 let common = wr::CommonItemProperties {
487 clip_rect: bounds,
488 spatial_id: highlight.spatial_id,
489 clip_chain_id: highlight.clip_chain_id,
490 flags: wr::PrimitiveFlags::default(),
491 };
492 self.wr().push_border(&common, bounds, widths, details)
493 };
494
495 let box_fragment = box_fragment.borrow();
496 paint_highlight(
497 PADDING_BOX_HIGHLIGHT_COLOR,
498 box_fragment.padding_rect(),
499 box_fragment.padding.to_webrender(),
500 );
501 paint_highlight(
502 BORDER_BOX_HIGHLIGHT_COLOR,
503 box_fragment.border_rect(),
504 box_fragment.border.to_webrender(),
505 );
506 paint_highlight(
507 MARGIN_BOX_HIGHLIGHT_COLOR,
508 box_fragment.margin_rect(),
509 box_fragment.margin.to_webrender(),
510 );
511 }
512 }
513}
514
515impl InspectorHighlight {
516 fn register_fragment_of_highlighted_dom_node(
517 &mut self,
518 fragment: &Fragment,
519 spatial_id: SpatialId,
520 clip_chain_id: ClipChainId,
521 containing_block: &PhysicalRect<Au>,
522 ) {
523 let state = self.state.get_or_insert(HighlightTraversalState {
524 content_box: euclid::Rect::zero(),
525 spatial_id,
526 clip_chain_id,
527 maybe_box_fragment: None,
528 });
529
530 debug_assert_eq!(spatial_id, state.spatial_id);
532 if clip_chain_id != ClipChainId::INVALID && state.clip_chain_id != ClipChainId::INVALID {
533 debug_assert_eq!(
534 clip_chain_id, state.clip_chain_id,
535 "Fragments of the same node must either have no clip chain or the same one"
536 );
537 }
538
539 let fragment_relative_rect = match fragment {
540 Fragment::Box(fragment) | Fragment::Float(fragment) => {
541 state.maybe_box_fragment = Some(fragment.clone());
542
543 fragment.borrow().content_rect
544 },
545 Fragment::Positioning(fragment) => fragment.borrow().rect,
546 Fragment::Text(fragment) => fragment.borrow().rect,
547 Fragment::Image(image_fragment) => image_fragment.borrow().rect,
548 Fragment::AbsoluteOrFixedPositioned(_) => return,
549 Fragment::IFrame(iframe_fragment) => iframe_fragment.borrow().rect,
550 };
551
552 state.content_box = state
553 .content_box
554 .union(&fragment_relative_rect.translate(containing_block.origin.to_vector()));
555 }
556}
557
558impl Fragment {
559 pub(crate) fn build_display_list(
560 &self,
561 builder: &mut DisplayListBuilder,
562 containing_block: &PhysicalRect<Au>,
563 section: StackingContextSection,
564 is_hit_test_for_scrollable_overflow: bool,
565 is_collapsed_table_borders: bool,
566 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
567 ) {
568 let spatial_id = builder.spatial_id(builder.current_scroll_node_id);
569 let clip_chain_id = builder.clip_chain_id(builder.current_clip_id);
570 if let Some(inspector_highlight) = &mut builder.inspector_highlight {
571 if self.tag() == Some(inspector_highlight.tag) {
572 inspector_highlight.register_fragment_of_highlighted_dom_node(
573 self,
574 spatial_id,
575 clip_chain_id,
576 containing_block,
577 );
578 }
579 }
580
581 match self {
582 Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
583 let box_fragment = &*box_fragment.borrow();
584 match box_fragment.style.get_inherited_box().visibility {
585 Visibility::Visible => BuilderForBoxFragment::new(
586 box_fragment,
587 containing_block,
588 is_hit_test_for_scrollable_overflow,
589 is_collapsed_table_borders,
590 )
591 .build(builder, section),
592 Visibility::Hidden => (),
593 Visibility::Collapse => (),
594 }
595 },
596 Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Positioning(_) => {},
597 Fragment::Image(image) => {
598 let image = image.borrow();
599 match image.style.get_inherited_box().visibility {
600 Visibility::Visible => {
601 builder.mark_is_contentful();
602
603 let image_rendering = image
604 .style
605 .get_inherited_box()
606 .image_rendering
607 .to_webrender();
608 let rect = image
609 .rect
610 .translate(containing_block.origin.to_vector())
611 .to_webrender();
612 let clip = image
613 .clip
614 .translate(containing_block.origin.to_vector())
615 .to_webrender();
616 let common = builder.common_properties(clip, &image.style);
617
618 if let Some(image_key) = image.image_key {
619 builder.wr().push_image(
620 &common,
621 rect,
622 image_rendering,
623 wr::AlphaType::PremultipliedAlpha,
624 image_key,
625 wr::ColorF::WHITE,
626 );
627 }
628 },
629 Visibility::Hidden => (),
630 Visibility::Collapse => (),
631 }
632 },
633 Fragment::IFrame(iframe) => {
634 let iframe = iframe.borrow();
635 match iframe.style.get_inherited_box().visibility {
636 Visibility::Visible => {
637 builder.mark_is_contentful();
638 let rect = iframe.rect.translate(containing_block.origin.to_vector());
639
640 let common = builder.common_properties(rect.to_webrender(), &iframe.style);
641 builder.wr().push_iframe(
642 rect.to_webrender(),
643 common.clip_rect,
644 &wr::SpaceAndClipInfo {
645 spatial_id: common.spatial_id,
646 clip_chain_id: common.clip_chain_id,
647 },
648 iframe.pipeline_id.into(),
649 true,
650 );
651 },
652 Visibility::Hidden => (),
653 Visibility::Collapse => (),
654 }
655 },
656 Fragment::Text(text) => {
657 let text = &*text.borrow();
658 match text
659 .inline_styles
660 .style
661 .borrow()
662 .get_inherited_box()
663 .visibility
664 {
665 Visibility::Visible => self.build_display_list_for_text_fragment(
666 text,
667 builder,
668 containing_block,
669 text_decorations,
670 ),
671 Visibility::Hidden => (),
672 Visibility::Collapse => (),
673 }
674 },
675 }
676 }
677
678 fn build_display_list_for_text_fragment(
679 &self,
680 fragment: &TextFragment,
681 builder: &mut DisplayListBuilder,
682 containing_block: &PhysicalRect<Au>,
683 text_decorations: &Arc<Vec<FragmentTextDecoration>>,
684 ) {
685 builder.mark_is_contentful();
689
690 let rect = fragment.rect.translate(containing_block.origin.to_vector());
691 let mut baseline_origin = rect.origin;
692 baseline_origin.y += fragment.font_metrics.ascent;
693 let include_whitespace =
694 fragment.has_selection() || text_decorations.iter().any(|item| !item.line.is_empty());
695
696 let glyphs = glyphs(
697 &fragment.glyphs,
698 baseline_origin,
699 fragment.justification_adjustment,
700 include_whitespace,
701 );
702 if glyphs.is_empty() {
703 return;
704 }
705
706 let parent_style = fragment.inline_styles.style.borrow();
707 let color = parent_style.clone_color();
708 let font_metrics = &fragment.font_metrics;
709 let dppx = builder.device_pixel_ratio.get();
710 let common = builder.common_properties(rect.to_webrender(), &parent_style);
711
712 let shadows = &parent_style.get_inherited_text().text_shadow;
715 for shadow in shadows.0.iter().rev() {
716 builder.wr().push_shadow(
717 &wr::SpaceAndClipInfo {
718 spatial_id: common.spatial_id,
719 clip_chain_id: common.clip_chain_id,
720 },
721 wr::Shadow {
722 offset: LayoutVector2D::new(shadow.horizontal.px(), shadow.vertical.px()),
723 color: rgba(shadow.color.resolve_to_absolute(&color)),
724 blur_radius: shadow.blur.px(),
725 },
726 true, );
728 }
729
730 for text_decoration in text_decorations.iter() {
731 if text_decoration.line.contains(TextDecorationLine::UNDERLINE) {
732 let mut rect = rect;
733 rect.origin.y += font_metrics.ascent - font_metrics.underline_offset;
734 rect.size.height =
735 Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
736
737 self.build_display_list_for_text_decoration(
738 &parent_style,
739 builder,
740 &rect,
741 text_decoration,
742 TextDecorationLine::UNDERLINE,
743 );
744 }
745 }
746
747 for text_decoration in text_decorations.iter() {
748 if text_decoration.line.contains(TextDecorationLine::OVERLINE) {
749 let mut rect = rect;
750 rect.size.height =
751 Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
752 self.build_display_list_for_text_decoration(
753 &parent_style,
754 builder,
755 &rect,
756 text_decoration,
757 TextDecorationLine::OVERLINE,
758 );
759 }
760 }
761
762 if let Some(range) = fragment.selection_range {
765 let baseline_origin = rect.origin;
766 if !range.is_empty() {
767 let start = glyphs_advance_by_index(
768 &fragment.glyphs,
769 range.begin(),
770 baseline_origin,
771 fragment.justification_adjustment,
772 );
773
774 let end = glyphs_advance_by_index(
775 &fragment.glyphs,
776 range.end(),
777 baseline_origin,
778 fragment.justification_adjustment,
779 );
780
781 let selection_rect = LayoutRect::new(
782 Point2D::new(start.x.to_f32_px(), containing_block.min_y().to_f32_px()),
783 Point2D::new(end.x.to_f32_px(), containing_block.max_y().to_f32_px()),
784 );
785 if let Some(selection_color) = fragment
786 .inline_styles
787 .selected
788 .borrow()
789 .clone_background_color()
790 .as_absolute()
791 {
792 let selection_common = builder.common_properties(selection_rect, &parent_style);
793 builder.wr().push_rect(
794 &selection_common,
795 selection_rect,
796 rgba(*selection_color),
797 );
798 }
799 } else {
800 let insertion_point = glyphs_advance_by_index(
801 &fragment.glyphs,
802 range.begin(),
803 baseline_origin,
804 fragment.justification_adjustment,
805 );
806
807 let insertion_point_rect = LayoutRect::new(
808 Point2D::new(
809 insertion_point.x.to_f32_px(),
810 containing_block.min_y().to_f32_px(),
811 ),
812 Point2D::new(
813 insertion_point.x.to_f32_px() + INSERTION_POINT_LOGICAL_WIDTH.to_f32_px(),
814 containing_block.max_y().to_f32_px(),
815 ),
816 );
817 let insertion_point_common =
818 builder.common_properties(insertion_point_rect, &parent_style);
819 builder
822 .wr()
823 .push_rect(&insertion_point_common, insertion_point_rect, rgba(color));
824 }
825 }
826
827 builder.wr().push_text(
828 &common,
829 rect.to_webrender(),
830 &glyphs,
831 fragment.font_key,
832 rgba(color),
833 None,
834 );
835
836 for text_decoration in text_decorations.iter() {
837 if text_decoration
838 .line
839 .contains(TextDecorationLine::LINE_THROUGH)
840 {
841 let mut rect = rect;
842 rect.origin.y += font_metrics.ascent - font_metrics.strikeout_offset;
843 rect.size.height =
844 Au::from_f32_px(font_metrics.strikeout_size.to_nearest_pixel(dppx));
845 self.build_display_list_for_text_decoration(
846 &parent_style,
847 builder,
848 &rect,
849 text_decoration,
850 TextDecorationLine::LINE_THROUGH,
851 );
852 }
853 }
854
855 if !shadows.0.is_empty() {
856 builder.wr().pop_all_shadows();
857 }
858 }
859
860 fn build_display_list_for_text_decoration(
861 &self,
862 parent_style: &ServoArc<ComputedValues>,
863 builder: &mut DisplayListBuilder,
864 rect: &PhysicalRect<Au>,
865 text_decoration: &FragmentTextDecoration,
866 line: TextDecorationLine,
867 ) {
868 if text_decoration.style == ComputedTextDecorationStyle::MozNone {
869 return;
870 }
871
872 let mut rect = rect.to_webrender();
873 let line_thickness = rect.height().ceil();
874
875 if text_decoration.style == ComputedTextDecorationStyle::Wavy {
876 rect = rect.inflate(0.0, line_thickness * 1.0);
877 }
878
879 let common_properties = builder.common_properties(rect, parent_style);
880 builder.wr().push_line(
881 &common_properties,
882 &rect,
883 line_thickness,
884 wr::LineOrientation::Horizontal,
885 &rgba(text_decoration.color),
886 text_decoration.style.to_webrender(),
887 );
888
889 if text_decoration.style == TextDecorationStyle::Double {
890 let half_height = (rect.height() / 2.0).floor().max(1.0);
891 let y_offset = match line {
892 TextDecorationLine::OVERLINE => -rect.height() - half_height,
893 _ => rect.height() + half_height,
894 };
895 let rect = rect.translate(Vector2D::new(0.0, y_offset));
896 let common_properties = builder.common_properties(rect, parent_style);
897 builder.wr().push_line(
898 &common_properties,
899 &rect,
900 line_thickness,
901 wr::LineOrientation::Horizontal,
902 &rgba(text_decoration.color),
903 text_decoration.style.to_webrender(),
904 );
905 }
906 }
907}
908
909struct BuilderForBoxFragment<'a> {
910 fragment: &'a BoxFragment,
911 containing_block: &'a PhysicalRect<Au>,
912 border_rect: units::LayoutRect,
913 margin_rect: OnceCell<units::LayoutRect>,
914 padding_rect: OnceCell<units::LayoutRect>,
915 content_rect: OnceCell<units::LayoutRect>,
916 border_radius: wr::BorderRadius,
917 border_edge_clip_chain_id: RefCell<Option<ClipChainId>>,
918 padding_edge_clip_chain_id: RefCell<Option<ClipChainId>>,
919 content_edge_clip_chain_id: RefCell<Option<ClipChainId>>,
920 is_hit_test_for_scrollable_overflow: bool,
921 is_collapsed_table_borders: bool,
922}
923
924impl<'a> BuilderForBoxFragment<'a> {
925 fn new(
926 fragment: &'a BoxFragment,
927 containing_block: &'a PhysicalRect<Au>,
928 is_hit_test_for_scrollable_overflow: bool,
929 is_collapsed_table_borders: bool,
930 ) -> Self {
931 let border_rect = fragment
932 .border_rect()
933 .translate(containing_block.origin.to_vector());
934 Self {
935 fragment,
936 containing_block,
937 border_rect: border_rect.to_webrender(),
938 border_radius: fragment.border_radius(),
939 margin_rect: OnceCell::new(),
940 padding_rect: OnceCell::new(),
941 content_rect: OnceCell::new(),
942 border_edge_clip_chain_id: RefCell::new(None),
943 padding_edge_clip_chain_id: RefCell::new(None),
944 content_edge_clip_chain_id: RefCell::new(None),
945 is_hit_test_for_scrollable_overflow,
946 is_collapsed_table_borders,
947 }
948 }
949
950 fn content_rect(&self) -> &units::LayoutRect {
951 self.content_rect.get_or_init(|| {
952 self.fragment
953 .content_rect
954 .translate(self.containing_block.origin.to_vector())
955 .to_webrender()
956 })
957 }
958
959 fn padding_rect(&self) -> &units::LayoutRect {
960 self.padding_rect.get_or_init(|| {
961 self.fragment
962 .padding_rect()
963 .translate(self.containing_block.origin.to_vector())
964 .to_webrender()
965 })
966 }
967
968 fn margin_rect(&self) -> &units::LayoutRect {
969 self.margin_rect.get_or_init(|| {
970 self.fragment
971 .margin_rect()
972 .translate(self.containing_block.origin.to_vector())
973 .to_webrender()
974 })
975 }
976
977 fn border_edge_clip(
978 &self,
979 builder: &mut DisplayListBuilder,
980 force_clip_creation: bool,
981 ) -> Option<ClipChainId> {
982 if let Some(clip) = *self.border_edge_clip_chain_id.borrow() {
983 return Some(clip);
984 }
985
986 let maybe_clip =
987 builder.maybe_create_clip(self.border_radius, self.border_rect, force_clip_creation);
988 *self.border_edge_clip_chain_id.borrow_mut() = maybe_clip;
989 maybe_clip
990 }
991
992 fn padding_edge_clip(
993 &self,
994 builder: &mut DisplayListBuilder,
995 force_clip_creation: bool,
996 ) -> Option<ClipChainId> {
997 if let Some(clip) = *self.padding_edge_clip_chain_id.borrow() {
998 return Some(clip);
999 }
1000
1001 let radii = inner_radii(self.border_radius, self.fragment.border.to_webrender());
1002 let maybe_clip =
1003 builder.maybe_create_clip(radii, *self.padding_rect(), force_clip_creation);
1004 *self.padding_edge_clip_chain_id.borrow_mut() = maybe_clip;
1005 maybe_clip
1006 }
1007
1008 fn content_edge_clip(
1009 &self,
1010 builder: &mut DisplayListBuilder,
1011 force_clip_creation: bool,
1012 ) -> Option<ClipChainId> {
1013 if let Some(clip) = *self.content_edge_clip_chain_id.borrow() {
1014 return Some(clip);
1015 }
1016
1017 let radii = inner_radii(
1018 self.border_radius,
1019 (self.fragment.border + self.fragment.padding).to_webrender(),
1020 );
1021 let maybe_clip =
1022 builder.maybe_create_clip(radii, *self.content_rect(), force_clip_creation);
1023 *self.content_edge_clip_chain_id.borrow_mut() = maybe_clip;
1024 maybe_clip
1025 }
1026
1027 fn build(&mut self, builder: &mut DisplayListBuilder, section: StackingContextSection) {
1028 if self.is_hit_test_for_scrollable_overflow &&
1029 self.fragment.style.get_inherited_ui().pointer_events !=
1030 style::computed_values::pointer_events::T::None
1031 {
1032 self.build_hit_test(builder, self.fragment.scrollable_overflow().to_webrender());
1033 return;
1034 }
1035 if self.is_collapsed_table_borders {
1036 self.build_collapsed_table_borders(builder);
1037 return;
1038 }
1039
1040 if section == StackingContextSection::Outline {
1041 self.build_outline(builder);
1042 return;
1043 }
1044
1045 if self
1046 .fragment
1047 .base
1048 .flags
1049 .contains(FragmentFlags::DO_NOT_PAINT)
1050 {
1051 return;
1052 }
1053
1054 self.build_background(builder);
1055 self.build_box_shadow(builder);
1056 self.build_border(builder);
1057 }
1058
1059 fn build_hit_test(&self, builder: &mut DisplayListBuilder, rect: LayoutRect) {
1060 let external_scroll_node_id = builder
1061 .compositor_info
1062 .external_scroll_id_for_scroll_tree_node(builder.current_scroll_node_id);
1063
1064 let mut common = builder.common_properties(rect, &self.fragment.style);
1065 if let Some(clip_chain_id) = self.border_edge_clip(builder, false) {
1066 common.clip_chain_id = clip_chain_id;
1067 }
1068 builder.wr().push_hit_test(
1069 common.clip_rect,
1070 common.clip_chain_id,
1071 common.spatial_id,
1072 common.flags,
1073 (external_scroll_node_id.0, 0), );
1075 }
1076
1077 fn build_background_for_painter(
1078 &mut self,
1079 builder: &mut DisplayListBuilder,
1080 painter: &BackgroundPainter,
1081 ) {
1082 let b = painter.style.get_background();
1083 let background_color = painter.style.resolve_color(&b.background_color);
1084 if background_color.alpha > 0.0 {
1085 let layer_index = b.background_image.0.len() - 1;
1089 let bounds = painter.painting_area(self, builder, layer_index);
1090 let common = painter.common_properties(self, builder, layer_index, bounds);
1091 builder
1092 .wr()
1093 .push_rect(&common, bounds, rgba(background_color))
1094 }
1095
1096 self.build_background_image(builder, painter);
1097 }
1098
1099 fn build_background(&mut self, builder: &mut DisplayListBuilder) {
1100 let flags = self.fragment.base.flags;
1101
1102 if flags.intersects(FragmentFlags::IS_ROOT_ELEMENT) {
1105 return;
1106 }
1107 if !builder.paint_body_background &&
1109 flags.intersects(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
1110 {
1111 return;
1112 }
1113
1114 if let BackgroundMode::None = self.fragment.background_mode {
1116 return;
1117 }
1118
1119 if let BackgroundMode::Extra(ref extra_backgrounds) = self.fragment.background_mode {
1123 for extra_background in extra_backgrounds {
1124 let positioning_area = extra_background.rect;
1125 let painter = BackgroundPainter {
1126 style: &extra_background.style.borrow_mut(),
1127 painting_area_override: None,
1128 positioning_area_override: Some(
1129 positioning_area
1130 .translate(self.containing_block.origin.to_vector())
1131 .to_webrender(),
1132 ),
1133 };
1134 self.build_background_for_painter(builder, &painter);
1135 }
1136 }
1137
1138 let painter = BackgroundPainter {
1139 style: &self.fragment.style,
1140 painting_area_override: None,
1141 positioning_area_override: None,
1142 };
1143 self.build_background_for_painter(builder, &painter);
1144 }
1145
1146 fn build_background_image(
1147 &mut self,
1148 builder: &mut DisplayListBuilder,
1149 painter: &BackgroundPainter,
1150 ) {
1151 let style = painter.style;
1152 let b = style.get_background();
1153 let node = self.fragment.base.tag.map(|tag| tag.node);
1154 for (index, image) in b.background_image.0.iter().enumerate().rev() {
1156 match builder.image_resolver.resolve_image(node, image) {
1157 Err(_) => {},
1158 Ok(ResolvedImage::Gradient(gradient)) => {
1159 let intrinsic = NaturalSizes::empty();
1160 let Some(layer) =
1161 &background::layout_layer(self, painter, builder, index, intrinsic)
1162 else {
1163 continue;
1164 };
1165
1166 match gradient::build(style, gradient, layer.tile_size, builder) {
1167 WebRenderGradient::Linear(linear_gradient) => builder.wr().push_gradient(
1168 &layer.common,
1169 layer.bounds,
1170 linear_gradient,
1171 layer.tile_size,
1172 layer.tile_spacing,
1173 ),
1174 WebRenderGradient::Radial(radial_gradient) => {
1175 builder.wr().push_radial_gradient(
1176 &layer.common,
1177 layer.bounds,
1178 radial_gradient,
1179 layer.tile_size,
1180 layer.tile_spacing,
1181 )
1182 },
1183 WebRenderGradient::Conic(conic_gradient) => {
1184 builder.wr().push_conic_gradient(
1185 &layer.common,
1186 layer.bounds,
1187 conic_gradient,
1188 layer.tile_size,
1189 layer.tile_spacing,
1190 )
1191 },
1192 }
1193 },
1194 Ok(ResolvedImage::Image { image, size }) => {
1195 let dppx = 1.0;
1197 let intrinsic =
1198 NaturalSizes::from_width_and_height(size.width / dppx, size.height / dppx);
1199 let layer = background::layout_layer(self, painter, builder, index, intrinsic);
1200 let image_wr_key = match image {
1201 CachedImage::Raster(raster_image) => raster_image.id,
1202 CachedImage::Vector(vector_image) => {
1203 let scale = builder.device_pixel_ratio.get();
1204 let default_size: DeviceIntSize =
1205 Size2D::new(size.width * scale, size.height * scale).to_i32();
1206 let layer_size = layer.as_ref().map(|layer| {
1207 Size2D::new(
1208 layer.tile_size.width * scale,
1209 layer.tile_size.height * scale,
1210 )
1211 .to_i32()
1212 });
1213
1214 node.and_then(|node| {
1215 let size = layer_size.unwrap_or(default_size);
1216 builder.image_resolver.rasterize_vector_image(
1217 vector_image.id,
1218 size,
1219 node,
1220 )
1221 })
1222 .and_then(|rasterized_image| rasterized_image.id)
1223 },
1224 };
1225
1226 let Some(image_key) = image_wr_key else {
1227 continue;
1228 };
1229
1230 if let Some(layer) = layer {
1231 if layer.repeat {
1232 builder.wr().push_repeating_image(
1233 &layer.common,
1234 layer.bounds,
1235 layer.tile_size,
1236 layer.tile_spacing,
1237 style.clone_image_rendering().to_webrender(),
1238 wr::AlphaType::PremultipliedAlpha,
1239 image_key,
1240 wr::ColorF::WHITE,
1241 )
1242 } else {
1243 builder.wr().push_image(
1244 &layer.common,
1245 layer.bounds,
1246 style.clone_image_rendering().to_webrender(),
1247 wr::AlphaType::PremultipliedAlpha,
1248 image_key,
1249 wr::ColorF::WHITE,
1250 )
1251 }
1252 }
1253 },
1254 }
1255 }
1256 }
1257
1258 fn build_border_side(&mut self, style_color: BorderStyleColor) -> wr::BorderSide {
1259 wr::BorderSide {
1260 color: rgba(style_color.color),
1261 style: match style_color.style {
1262 BorderStyle::None => wr::BorderStyle::None,
1263 BorderStyle::Solid => wr::BorderStyle::Solid,
1264 BorderStyle::Double => wr::BorderStyle::Double,
1265 BorderStyle::Dotted => wr::BorderStyle::Dotted,
1266 BorderStyle::Dashed => wr::BorderStyle::Dashed,
1267 BorderStyle::Hidden => wr::BorderStyle::Hidden,
1268 BorderStyle::Groove => wr::BorderStyle::Groove,
1269 BorderStyle::Ridge => wr::BorderStyle::Ridge,
1270 BorderStyle::Inset => wr::BorderStyle::Inset,
1271 BorderStyle::Outset => wr::BorderStyle::Outset,
1272 },
1273 }
1274 }
1275
1276 fn build_collapsed_table_borders(&mut self, builder: &mut DisplayListBuilder) {
1277 let Some(SpecificLayoutInfo::TableGridWithCollapsedBorders(table_info)) =
1278 self.fragment.specific_layout_info()
1279 else {
1280 return;
1281 };
1282 let mut common =
1283 builder.common_properties(units::LayoutRect::default(), &self.fragment.style);
1284 let radius = wr::BorderRadius::default();
1285 let mut column_sum = Au::zero();
1286 for (x, column_size) in table_info.track_sizes.x.iter().enumerate() {
1287 let mut row_sum = Au::zero();
1288 for (y, row_size) in table_info.track_sizes.y.iter().enumerate() {
1289 let left_border = &table_info.collapsed_borders.x[x][y];
1290 let right_border = &table_info.collapsed_borders.x[x + 1][y];
1291 let top_border = &table_info.collapsed_borders.y[y][x];
1292 let bottom_border = &table_info.collapsed_borders.y[y + 1][x];
1293 let details = wr::BorderDetails::Normal(wr::NormalBorder {
1294 left: self.build_border_side(left_border.style_color.clone()),
1295 right: self.build_border_side(right_border.style_color.clone()),
1296 top: self.build_border_side(top_border.style_color.clone()),
1297 bottom: self.build_border_side(bottom_border.style_color.clone()),
1298 radius,
1299 do_aa: true,
1300 });
1301 let mut border_widths = PhysicalSides::new(
1302 top_border.width,
1303 right_border.width,
1304 bottom_border.width,
1305 left_border.width,
1306 );
1307 let left_adjustment = if x == 0 {
1308 -border_widths.left / 2
1309 } else {
1310 std::mem::take(&mut border_widths.left) / 2
1311 };
1312 let top_adjustment = if y == 0 {
1313 -border_widths.top / 2
1314 } else {
1315 std::mem::take(&mut border_widths.top) / 2
1316 };
1317 let origin =
1318 PhysicalPoint::new(column_sum + left_adjustment, row_sum + top_adjustment);
1319 let size = PhysicalSize::new(
1320 *column_size - left_adjustment + border_widths.right / 2,
1321 *row_size - top_adjustment + border_widths.bottom / 2,
1322 );
1323 let border_rect = PhysicalRect::new(origin, size)
1324 .translate(self.fragment.content_rect.origin.to_vector())
1325 .translate(self.containing_block.origin.to_vector())
1326 .to_webrender();
1327 common.clip_rect = border_rect;
1328 builder.wr().push_border(
1329 &common,
1330 border_rect,
1331 border_widths.to_webrender(),
1332 details,
1333 );
1334 row_sum += *row_size;
1335 }
1336 column_sum += *column_size;
1337 }
1338 }
1339
1340 fn build_border(&mut self, builder: &mut DisplayListBuilder) {
1341 if self.fragment.has_collapsed_borders() {
1342 return;
1345 }
1346
1347 let border = self.fragment.style.get_border();
1348 let border_widths = self.fragment.border.to_webrender();
1349
1350 if border_widths == SideOffsets2D::zero() {
1351 return;
1352 }
1353
1354 let common = builder.common_properties(self.border_rect, &self.fragment.style);
1356 if self.build_border_image(builder, &common, border, border_widths) {
1357 return;
1358 }
1359
1360 let current_color = self.fragment.style.get_inherited_text().clone_color();
1361 let style_color = BorderStyleColor::from_border(border, ¤t_color);
1362 let details = wr::BorderDetails::Normal(wr::NormalBorder {
1363 top: self.build_border_side(style_color.top),
1364 right: self.build_border_side(style_color.right),
1365 bottom: self.build_border_side(style_color.bottom),
1366 left: self.build_border_side(style_color.left),
1367 radius: self.border_radius,
1368 do_aa: true,
1369 });
1370 builder
1371 .wr()
1372 .push_border(&common, self.border_rect, border_widths, details)
1373 }
1374
1375 fn build_border_image(
1377 &self,
1378 builder: &mut DisplayListBuilder,
1379 common: &CommonItemProperties,
1380 border: &Border,
1381 border_widths: SideOffsets2D<f32, LayoutPixel>,
1382 ) -> bool {
1383 let border_style_struct = self.fragment.style.get_border();
1384 let border_image_outset =
1385 resolve_border_image_outset(border_style_struct.border_image_outset, border_widths);
1386 let border_image_area = self.border_rect.to_rect().outer_rect(border_image_outset);
1387 let border_image_size = border_image_area.size;
1388 let border_image_widths = resolve_border_image_width(
1389 &border_style_struct.border_image_width,
1390 border_widths,
1391 border_image_size,
1392 );
1393 let border_image_repeat = &border_style_struct.border_image_repeat;
1394 let border_image_fill = border_style_struct.border_image_slice.fill;
1395 let border_image_slice = &border_style_struct.border_image_slice.offsets;
1396
1397 let stops = Vec::new();
1398 let mut width = border_image_size.width;
1399 let mut height = border_image_size.height;
1400 let node = self.fragment.base.tag.map(|tag| tag.node);
1401 let source = match builder
1402 .image_resolver
1403 .resolve_image(node, &border.border_image_source)
1404 {
1405 Err(_) => return false,
1406 Ok(ResolvedImage::Image { image, size }) => {
1407 let Some(image) = image.as_raster_image() else {
1408 return false;
1409 };
1410
1411 let Some(key) = image.id else {
1412 return false;
1413 };
1414
1415 width = size.width;
1416 height = size.height;
1417 let image_rendering = self.fragment.style.clone_image_rendering().to_webrender();
1418 NinePatchBorderSource::Image(key, image_rendering)
1419 },
1420 Ok(ResolvedImage::Gradient(gradient)) => {
1421 match gradient::build(&self.fragment.style, gradient, border_image_size, builder) {
1422 WebRenderGradient::Linear(gradient) => {
1423 NinePatchBorderSource::Gradient(gradient)
1424 },
1425 WebRenderGradient::Radial(gradient) => {
1426 NinePatchBorderSource::RadialGradient(gradient)
1427 },
1428 WebRenderGradient::Conic(gradient) => {
1429 NinePatchBorderSource::ConicGradient(gradient)
1430 },
1431 }
1432 },
1433 };
1434
1435 let size = euclid::Size2D::new(width as i32, height as i32);
1436
1437 if size.is_empty() || border_image_size.is_empty() {
1440 return true;
1441 }
1442
1443 let details = BorderDetails::NinePatch(NinePatchBorder {
1444 source,
1445 width: size.width,
1446 height: size.height,
1447 slice: resolve_border_image_slice(border_image_slice, size),
1448 fill: border_image_fill,
1449 repeat_horizontal: border_image_repeat.0.to_webrender(),
1450 repeat_vertical: border_image_repeat.1.to_webrender(),
1451 });
1452 builder.wr().push_border(
1453 common,
1454 border_image_area.to_box2d(),
1455 border_image_widths,
1456 details,
1457 );
1458 builder.wr().push_stops(&stops);
1459 true
1460 }
1461
1462 fn build_outline(&mut self, builder: &mut DisplayListBuilder) {
1463 let style = &self.fragment.style;
1464 let outline = style.get_outline();
1465 let width = outline.outline_width.to_f32_px();
1466 if width == 0.0 {
1467 return;
1468 }
1469 let offset = outline.outline_offset.px() + width;
1478 let outline_rect = self.border_rect.inflate(
1479 offset.max(-self.border_rect.width() / 2.0 + width),
1480 offset.max(-self.border_rect.height() / 2.0 + width),
1481 );
1482 let common = builder.common_properties(outline_rect, &self.fragment.style);
1483 let widths = SideOffsets2D::new_all_same(width);
1484 let border_style = match outline.outline_style {
1485 OutlineStyle::Auto => BorderStyle::Solid,
1488 OutlineStyle::BorderStyle(s) => s,
1489 };
1490 let side = self.build_border_side(BorderStyleColor {
1491 style: border_style,
1492 color: style.resolve_color(&outline.outline_color),
1493 });
1494 let details = wr::BorderDetails::Normal(wr::NormalBorder {
1495 top: side,
1496 right: side,
1497 bottom: side,
1498 left: side,
1499 radius: offset_radii(self.border_radius, offset),
1500 do_aa: true,
1501 });
1502 builder
1503 .wr()
1504 .push_border(&common, outline_rect, widths, details)
1505 }
1506
1507 fn build_box_shadow(&self, builder: &mut DisplayListBuilder<'_>) {
1508 let box_shadows = &self.fragment.style.get_effects().box_shadow.0;
1509 if box_shadows.is_empty() {
1510 return;
1511 }
1512
1513 let common = builder.common_properties(MaxRect::max_rect(), &self.fragment.style);
1515 for box_shadow in box_shadows.iter().rev() {
1516 let (rect, clip_mode) = if box_shadow.inset {
1517 (*self.padding_rect(), BoxShadowClipMode::Inset)
1518 } else {
1519 (self.border_rect, BoxShadowClipMode::Outset)
1520 };
1521
1522 builder.wr().push_box_shadow(
1523 &common,
1524 rect,
1525 LayoutVector2D::new(
1526 box_shadow.base.horizontal.px(),
1527 box_shadow.base.vertical.px(),
1528 ),
1529 rgba(self.fragment.style.resolve_color(&box_shadow.base.color)),
1530 box_shadow.base.blur.px(),
1531 box_shadow.spread.px(),
1532 self.border_radius,
1533 clip_mode,
1534 );
1535 }
1536 }
1537}
1538
1539fn rgba(color: AbsoluteColor) -> wr::ColorF {
1540 let rgba = color.to_color_space(ColorSpace::Srgb);
1541 wr::ColorF::new(
1542 rgba.components.0.clamp(0.0, 1.0),
1543 rgba.components.1.clamp(0.0, 1.0),
1544 rgba.components.2.clamp(0.0, 1.0),
1545 rgba.alpha,
1546 )
1547}
1548
1549fn glyphs(
1550 glyph_runs: &[Arc<GlyphStore>],
1551 mut baseline_origin: PhysicalPoint<Au>,
1552 justification_adjustment: Au,
1553 include_whitespace: bool,
1554) -> Vec<wr::GlyphInstance> {
1555 use fonts_traits::ByteIndex;
1556 use range::Range;
1557
1558 let mut glyphs = vec![];
1559 for run in glyph_runs {
1560 for glyph in run.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), run.len())) {
1561 if !run.is_whitespace() || include_whitespace {
1562 let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
1563 let point = units::LayoutPoint::new(
1564 baseline_origin.x.to_f32_px() + glyph_offset.x.to_f32_px(),
1565 baseline_origin.y.to_f32_px() + glyph_offset.y.to_f32_px(),
1566 );
1567 let glyph = wr::GlyphInstance {
1568 index: glyph.id(),
1569 point,
1570 };
1571 glyphs.push(glyph);
1572 }
1573
1574 if glyph.char_is_word_separator() {
1575 baseline_origin.x += justification_adjustment;
1576 }
1577 baseline_origin.x += glyph.advance();
1578 }
1579 }
1580 glyphs
1581}
1582
1583fn glyphs_advance_by_index(
1585 glyph_runs: &[Arc<GlyphStore>],
1586 index: fonts_traits::ByteIndex,
1587 baseline_origin: PhysicalPoint<Au>,
1588 justification_adjustment: Au,
1589) -> PhysicalPoint<Au> {
1590 let mut point = baseline_origin;
1591 let mut index = index;
1592 for run in glyph_runs {
1593 let range = ServoRange::new(fonts::ByteIndex(0), index.min(run.len()));
1594 index = index - range.length();
1595 let total_advance = run.advance_for_byte_range(&range, justification_adjustment);
1596 point.x += total_advance;
1597 }
1598 point
1599}
1600
1601fn inner_radii(mut radii: wr::BorderRadius, insets: units::LayoutSideOffsets) -> wr::BorderRadius {
1603 assert!(insets.left >= 0.0, "left inset must not be negative");
1604 radii.top_left.width -= insets.left;
1605 radii.bottom_left.width -= insets.left;
1606
1607 assert!(insets.right >= 0.0, "left inset must not be negative");
1608 radii.top_right.width -= insets.right;
1609 radii.bottom_right.width -= insets.right;
1610
1611 assert!(insets.top >= 0.0, "top inset must not be negative");
1612 radii.top_left.height -= insets.top;
1613 radii.top_right.height -= insets.top;
1614
1615 assert!(insets.bottom >= 0.0, "bottom inset must not be negative");
1616 radii.bottom_left.height -= insets.bottom;
1617 radii.bottom_right.height -= insets.bottom;
1618 radii
1619}
1620
1621fn offset_radii(mut radii: wr::BorderRadius, offset: f32) -> wr::BorderRadius {
1622 if offset == 0.0 {
1623 return radii;
1624 }
1625 if offset < 0.0 {
1626 return inner_radii(radii, units::LayoutSideOffsets::new_all_same(-offset));
1627 }
1628 let expand = |radius: &mut f32| {
1629 if *radius > 0.0 {
1633 *radius += offset;
1634 }
1635 };
1636 expand(&mut radii.top_left.width);
1637 expand(&mut radii.top_left.height);
1638 expand(&mut radii.top_right.width);
1639 expand(&mut radii.top_right.height);
1640 expand(&mut radii.bottom_right.width);
1641 expand(&mut radii.bottom_right.height);
1642 expand(&mut radii.bottom_left.width);
1643 expand(&mut radii.bottom_left.height);
1644 radii
1645}
1646
1647fn resolve_border_image_outset(
1649 outset: BorderImageOutset,
1650 border: SideOffsets2D<f32, LayoutPixel>,
1651) -> SideOffsets2D<f32, LayoutPixel> {
1652 fn image_outset_for_side(outset: NonNegativeLengthOrNumber, border_width: f32) -> f32 {
1653 match outset {
1654 NonNegativeLengthOrNumber::Length(length) => length.px(),
1655 NonNegativeLengthOrNumber::Number(factor) => border_width * factor.0,
1656 }
1657 }
1658
1659 SideOffsets2D::new(
1660 image_outset_for_side(outset.0, border.top),
1661 image_outset_for_side(outset.1, border.right),
1662 image_outset_for_side(outset.2, border.bottom),
1663 image_outset_for_side(outset.3, border.left),
1664 )
1665}
1666
1667fn resolve_border_image_width(
1669 width: &BorderImageWidth,
1670 border: SideOffsets2D<f32, LayoutPixel>,
1671 border_area: Size2D<f32, LayoutPixel>,
1672) -> SideOffsets2D<f32, LayoutPixel> {
1673 fn image_width_for_side(
1674 border_image_width: &BorderImageSideWidth,
1675 border_width: f32,
1676 total_length: f32,
1677 ) -> f32 {
1678 match border_image_width {
1679 BorderImageSideWidth::LengthPercentage(v) => {
1680 v.to_used_value(Au::from_f32_px(total_length)).to_f32_px()
1681 },
1682 BorderImageSideWidth::Number(x) => border_width * x.0,
1683 BorderImageSideWidth::Auto => border_width,
1684 }
1685 }
1686
1687 SideOffsets2D::new(
1688 image_width_for_side(&width.0, border.top, border_area.height),
1689 image_width_for_side(&width.1, border.right, border_area.width),
1690 image_width_for_side(&width.2, border.bottom, border_area.height),
1691 image_width_for_side(&width.3, border.left, border_area.width),
1692 )
1693}
1694
1695fn resolve_border_image_slice(
1697 border_image_slice: &Rect<NonNegative<NumberOrPercentage>>,
1698 size: Size2D<i32, UnknownUnit>,
1699) -> SideOffsets2D<i32, DevicePixel> {
1700 fn resolve_percentage(value: NonNegative<NumberOrPercentage>, length: i32) -> i32 {
1701 match value.0 {
1702 NumberOrPercentage::Percentage(p) => (p.0 * length as f32).round() as i32,
1703 NumberOrPercentage::Number(n) => n.round() as i32,
1704 }
1705 }
1706
1707 SideOffsets2D::new(
1708 resolve_percentage(border_image_slice.0, size.height),
1709 resolve_percentage(border_image_slice.1, size.width),
1710 resolve_percentage(border_image_slice.2, size.height),
1711 resolve_percentage(border_image_slice.3, size.width),
1712 )
1713}
1714
1715pub(super) fn normalize_radii(rect: &units::LayoutRect, radius: &mut wr::BorderRadius) {
1716 let f = (rect.width() / (radius.top_left.width + radius.top_right.width))
1724 .min(rect.width() / (radius.bottom_left.width + radius.bottom_right.width))
1725 .min(rect.height() / (radius.top_left.height + radius.bottom_left.height))
1726 .min(rect.height() / (radius.top_right.height + radius.bottom_right.height));
1727 if f < 1.0 {
1728 radius.top_left *= f;
1729 radius.top_right *= f;
1730 radius.bottom_right *= f;
1731 radius.bottom_left *= f;
1732 }
1733}
1734
1735pub(super) fn compute_margin_box_radius(
1743 radius: wr::BorderRadius,
1744 layout_rect: LayoutSize,
1745 fragment: &BoxFragment,
1746) -> wr::BorderRadius {
1747 let margin = fragment.style.physical_margin();
1748 let adjust_radius = |radius: f32, margin: f32| -> f32 {
1749 if margin <= 0. || (radius / margin) >= 1. {
1750 (radius + margin).max(0.)
1751 } else {
1752 radius + (margin * (1. + (radius / margin - 1.).powf(3.)))
1753 }
1754 };
1755 let compute_margin_radius = |radius: LayoutSize,
1756 layout_rect: LayoutSize,
1757 margin: Size2D<LengthPercentageOrAuto, UnknownUnit>|
1758 -> LayoutSize {
1759 let zero = LengthPercentage::zero();
1760 let width = margin
1761 .width
1762 .auto_is(|| &zero)
1763 .to_used_value(Au::from_f32_px(layout_rect.width));
1764 let height = margin
1765 .height
1766 .auto_is(|| &zero)
1767 .to_used_value(Au::from_f32_px(layout_rect.height));
1768 LayoutSize::new(
1769 adjust_radius(radius.width, width.to_f32_px()),
1770 adjust_radius(radius.height, height.to_f32_px()),
1771 )
1772 };
1773 wr::BorderRadius {
1774 top_left: compute_margin_radius(
1775 radius.top_left,
1776 layout_rect,
1777 Size2D::new(margin.left, margin.top),
1778 ),
1779 top_right: compute_margin_radius(
1780 radius.top_right,
1781 layout_rect,
1782 Size2D::new(margin.right, margin.top),
1783 ),
1784 bottom_left: compute_margin_radius(
1785 radius.bottom_left,
1786 layout_rect,
1787 Size2D::new(margin.left, margin.bottom),
1788 ),
1789 bottom_right: compute_margin_radius(
1790 radius.bottom_right,
1791 layout_rect,
1792 Size2D::new(margin.right, margin.bottom),
1793 ),
1794 }
1795}
1796
1797impl BoxFragment {
1798 fn border_radius(&self) -> BorderRadius {
1799 let border = self.style.get_border();
1800 if border.border_top_left_radius.0.is_zero() &&
1801 border.border_top_right_radius.0.is_zero() &&
1802 border.border_bottom_right_radius.0.is_zero() &&
1803 border.border_bottom_left_radius.0.is_zero()
1804 {
1805 return BorderRadius::zero();
1806 }
1807
1808 let border_rect = self.border_rect();
1809 let resolve =
1810 |radius: &LengthPercentage, box_size: Au| radius.to_used_value(box_size).to_f32_px();
1811 let corner = |corner: &style::values::computed::BorderCornerRadius| {
1812 Size2D::new(
1813 resolve(&corner.0.width.0, border_rect.size.width),
1814 resolve(&corner.0.height.0, border_rect.size.height),
1815 )
1816 };
1817
1818 let mut radius = wr::BorderRadius {
1819 top_left: corner(&border.border_top_left_radius),
1820 top_right: corner(&border.border_top_right_radius),
1821 bottom_right: corner(&border.border_bottom_right_radius),
1822 bottom_left: corner(&border.border_bottom_left_radius),
1823 };
1824
1825 normalize_radii(&border_rect.to_webrender(), &mut radius);
1826 radius
1827 }
1828}