1use crate::computed_value_flags::ComputedValueFlags;
9use crate::dom::TElement;
10use crate::logical_geometry::PhysicalSide;
11use crate::properties::longhands::display::computed_value::T as Display;
12use crate::properties::longhands::float::computed_value::T as Float;
13use crate::properties::longhands::position::computed_value::T as Position;
14#[cfg(feature = "gecko")]
15use crate::properties::longhands::{
16 contain::computed_value::T as Contain, container_type::computed_value::T as ContainerType,
17 content_visibility::computed_value::T as ContentVisibility,
18 overflow_x::computed_value::T as Overflow,
19};
20use crate::properties::{self, ComputedValues, StyleBuilder};
21use crate::values::computed::position::{
22 PositionTryFallbacksTryTactic, PositionTryFallbacksTryTacticKeyword, TryTacticAdjustment,
23};
24use crate::values::specified::align::AlignFlags;
25
26#[cfg(feature = "gecko")]
27use selectors::parser::PseudoElement;
28
29pub struct StyleAdjuster<'a, 'b: 'a> {
43 style: &'a mut StyleBuilder<'b>,
44}
45
46#[cfg(feature = "gecko")]
47fn is_topmost_svg_svg_element<E>(e: E) -> bool
48where
49 E: TElement,
50{
51 debug_assert!(e.is_svg_element());
52 if e.local_name() != &*atom!("svg") {
53 return false;
54 }
55
56 let parent = match e.traversal_parent() {
57 Some(n) => n,
58 None => return true,
59 };
60
61 if !parent.is_svg_element() {
62 return true;
63 }
64
65 parent.local_name() == &*atom!("foreignObject")
66}
67
68#[cfg(feature = "gecko")]
70fn is_effective_display_none_for_display_contents<E>(element: E) -> bool
71where
72 E: TElement,
73{
74 use crate::Atom;
75
76 const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
77 atom!("br"),
78 atom!("wbr"),
79 atom!("meter"),
80 atom!("progress"),
81 atom!("canvas"),
82 atom!("embed"),
83 atom!("object"),
84 atom!("audio"),
85 atom!("iframe"),
86 atom!("img"),
87 atom!("video"),
88 atom!("frame"),
89 atom!("frameset"),
90 atom!("input"),
91 atom!("textarea"),
92 atom!("select"),
93 ];
94
95 const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
101 atom!("svg"),
102 atom!("a"),
103 atom!("g"),
104 atom!("use"),
105 atom!("tspan"),
106 atom!("textPath"),
107 ];
108
109 if element.is_html_element() {
111 let local_name = element.local_name();
112 return SPECIAL_HTML_ELEMENTS
113 .iter()
114 .any(|name| &**name == local_name);
115 }
116
117 if element.is_svg_element() {
119 if is_topmost_svg_svg_element(element) {
120 return true;
121 }
122 let local_name = element.local_name();
123 return !SPECIAL_SVG_ELEMENTS
124 .iter()
125 .any(|name| &**name == local_name);
126 }
127
128 if element.is_mathml_element() {
130 return true;
131 }
132
133 false
134}
135
136impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
137 #[inline]
139 pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
140 StyleAdjuster { style }
141 }
142
143 fn adjust_for_top_layer(&mut self) {
149 if !self.style.in_top_layer() {
150 return;
151 }
152 if !self.style.is_absolutely_positioned() {
153 self.style.mutate_box().set_position(Position::Absolute);
154 }
155 if self.style.get_box().clone_display().is_contents() {
156 self.style.mutate_box().set_display(Display::Block);
157 }
158 }
159
160 #[cfg(feature = "gecko")]
167 fn adjust_for_webkit_line_clamp(&mut self) {
168 use crate::properties::longhands::_moz_box_orient::computed_value::T as BoxOrient;
169 use crate::values::specified::box_::{DisplayInside, DisplayOutside};
170 let box_style = self.style.get_box();
171 if box_style.clone__webkit_line_clamp().is_none() {
172 return;
173 }
174 let disp = box_style.clone_display();
175 if disp.inside() != DisplayInside::WebkitBox {
176 return;
177 }
178 if self.style.get_xul().clone__moz_box_orient() != BoxOrient::Vertical {
179 return;
180 }
181 let new_display = if disp.outside() == DisplayOutside::Block {
182 Display::FlowRoot
183 } else {
184 debug_assert_eq!(disp.outside(), DisplayOutside::Inline);
185 Display::InlineBlock
186 };
187 self.style
188 .mutate_box()
189 .set_adjusted_display(new_display, false);
190 }
191
192 fn adjust_for_position(&mut self) {
198 if self.style.is_absolutely_positioned() && self.style.is_floating() {
199 self.style.mutate_box().set_float(Float::None);
200 }
201 }
202
203 fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool
206 where
207 E: TElement,
208 {
209 if let Some(pseudo) = self.style.pseudo {
210 return pseudo.skip_item_display_fixup();
211 }
212
213 element.is_some_and(|e| e.skip_item_display_fixup())
214 }
215
216 fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
223 where
224 E: TElement,
225 {
226 let mut blockify = false;
227 macro_rules! blockify_if {
228 ($if_what:expr) => {
229 if !blockify {
230 blockify = $if_what;
231 }
232 };
233 }
234
235 blockify_if!(self.style.is_root_element);
236 if !self.skip_item_display_fixup(element) {
237 let parent_display = layout_parent_style.get_box().clone_display();
238 blockify_if!(parent_display.is_item_container());
239 }
240
241 let is_item_or_root = blockify;
242
243 blockify_if!(self.style.is_floating());
244 blockify_if!(self.style.is_absolutely_positioned());
245
246 if !blockify {
247 return;
248 }
249
250 let display = self.style.get_box().clone_display();
251 let blockified_display = display.equivalent_block_display(self.style.is_root_element);
252 if display != blockified_display {
253 self.style
254 .mutate_box()
255 .set_adjusted_display(blockified_display, is_item_or_root);
256 }
257 }
258
259 fn set_bits(&mut self) {
261 let box_style = self.style.get_box();
262 let display = box_style.clone_display();
263
264 if !display.is_contents() {
265 if !self
266 .style
267 .get_text()
268 .clone_text_decoration_line()
269 .is_empty()
270 {
271 self.style
272 .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
273 }
274
275 if self.style.get_effects().clone_opacity() == 0. {
276 self.style
277 .add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);
278 }
279 }
280
281 if self.style.pseudo.is_some_and(|p| p.is_first_line()) {
282 self.style
283 .add_flags(ComputedValueFlags::IS_IN_FIRST_LINE_SUBTREE);
284 }
285
286 if self.style.is_root_element {
287 self.style
288 .add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
289 }
290
291 #[cfg(feature = "gecko")]
292 if box_style
293 .clone_effective_containment()
294 .contains(Contain::STYLE)
295 {
296 self.style
297 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
298 }
299
300 if box_style.clone_container_type().is_size_container_type() {
301 self.style
302 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE);
303 }
304
305 #[cfg(feature = "servo")]
306 if self.style.get_parent_column().is_multicol() {
307 self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
308 }
309 }
310
311 #[cfg(feature = "gecko")]
318 pub fn adjust_for_text(&mut self) {
319 debug_assert!(!self.style.is_root_element);
320 self.adjust_for_text_combine_upright();
321 self.adjust_for_text_in_ruby();
322 self.set_bits();
323 }
324
325 #[cfg(feature = "gecko")]
336 fn adjust_for_text_combine_upright(&mut self) {
337 use crate::computed_values::text_combine_upright::T as TextCombineUpright;
338 use crate::computed_values::writing_mode::T as WritingMode;
339 use crate::logical_geometry;
340
341 let writing_mode = self.style.get_inherited_box().clone_writing_mode();
342 let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
343
344 if matches!(
345 writing_mode,
346 WritingMode::VerticalRl | WritingMode::VerticalLr
347 ) && text_combine_upright == TextCombineUpright::All
348 {
349 self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
350 self.style
351 .mutate_inherited_box()
352 .set_writing_mode(WritingMode::HorizontalTb);
353 self.style.writing_mode =
354 logical_geometry::WritingMode::new(self.style.get_inherited_box());
355 }
356 }
357
358 #[cfg(feature = "gecko")]
365 fn adjust_for_text_in_ruby(&mut self) {
366 let parent_display = self.style.get_parent_box().clone_display();
367 if parent_display.is_ruby_type()
368 || self
369 .style
370 .get_parent_flags()
371 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
372 {
373 self.style
374 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
375 }
376 }
377
378 fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {
392 let our_writing_mode = self.style.get_inherited_box().clone_writing_mode();
393 let parent_writing_mode = layout_parent_style.get_inherited_box().clone_writing_mode();
394
395 if our_writing_mode != parent_writing_mode
396 && self.style.get_box().clone_display() == Display::Inline
397 {
398 if cfg!(feature = "servo") {
401 self.style
402 .mutate_box()
403 .set_adjusted_display(Display::InlineBlock, false);
404 } else {
405 self.style.mutate_box().set_display(Display::InlineBlock);
406 }
407 }
408 }
409
410 fn adjust_for_border_width(&mut self) {
415 properties::adjust_border_width(self.style);
416 }
417
418 #[cfg(feature = "gecko")]
421 fn adjust_for_column_rule_width(&mut self) {
422 let column_style = self.style.get_column();
423 if !column_style.clone_column_rule_style().none_or_hidden() {
424 return;
425 }
426 if !column_style.column_rule_has_nonzero_width() {
427 return;
428 }
429 self.style
430 .mutate_column()
431 .set_column_rule_width(crate::Zero::zero());
432 }
433
434 fn adjust_for_outline_width(&mut self) {
437 let outline = self.style.get_outline();
438 if !outline.clone_outline_style().none_or_hidden() {
439 return;
440 }
441 if !outline.outline_has_nonzero_width() {
442 return;
443 }
444 self.style
445 .mutate_outline()
446 .set_outline_width(crate::Zero::zero());
447 }
448
449 fn adjust_for_overflow(&mut self) {
455 let overflow_x = self.style.get_box().clone_overflow_x();
456 let overflow_y = self.style.get_box().clone_overflow_y();
457 if overflow_x == overflow_y {
458 return; }
460
461 if overflow_x.is_scrollable() != overflow_y.is_scrollable() {
462 let box_style = self.style.mutate_box();
463 box_style.set_overflow_x(overflow_x.to_scrollable());
464 box_style.set_overflow_y(overflow_y.to_scrollable());
465 }
466 }
467
468 #[cfg(feature = "gecko")]
469 fn adjust_for_contain(&mut self) {
470 let box_style = self.style.get_box();
471 let container_type = box_style.clone_container_type();
472 let content_visibility = box_style.clone_content_visibility();
473 if !container_type.is_size_container_type()
474 && content_visibility == ContentVisibility::Visible
475 {
476 debug_assert_eq!(
477 box_style.clone_contain(),
478 box_style.clone_effective_containment()
479 );
480 return;
481 }
482 let old_contain = box_style.clone_contain();
483 let mut new_contain = old_contain;
484 match content_visibility {
485 ContentVisibility::Visible => {},
486 ContentVisibility::Auto => {
490 new_contain.insert(Contain::LAYOUT | Contain::PAINT | Contain::STYLE)
491 },
492 ContentVisibility::Hidden => new_contain
493 .insert(Contain::LAYOUT | Contain::PAINT | Contain::SIZE | Contain::STYLE),
494 }
495 if container_type.intersects(ContainerType::INLINE_SIZE) {
496 new_contain.insert(Contain::STYLE | Contain::INLINE_SIZE);
500 } else if container_type.intersects(ContainerType::SIZE) {
501 new_contain.insert(Contain::STYLE | Contain::SIZE);
505 }
506 if new_contain == old_contain {
507 debug_assert_eq!(
508 box_style.clone_contain(),
509 box_style.clone_effective_containment()
510 );
511 return;
512 }
513 self.style
514 .mutate_box()
515 .set_effective_containment(new_contain);
516 }
517
518 #[cfg(feature = "gecko")]
523 fn adjust_for_contain_intrinsic_size(&mut self) {
524 let content_visibility = self.style.get_box().clone_content_visibility();
525 if content_visibility != ContentVisibility::Auto {
526 return;
527 }
528
529 let pos = self.style.get_position();
530 let new_width = pos.clone_contain_intrinsic_width().add_auto_if_needed();
531 let new_height = pos.clone_contain_intrinsic_height().add_auto_if_needed();
532 if new_width.is_none() && new_height.is_none() {
533 return;
534 }
535
536 let pos = self.style.mutate_position();
537 if let Some(width) = new_width {
538 pos.set_contain_intrinsic_width(width);
539 }
540 if let Some(height) = new_height {
541 pos.set_contain_intrinsic_height(height);
542 }
543 }
544
545 #[cfg(feature = "gecko")]
551 fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)
552 where
553 E: TElement,
554 {
555 if self.style.get_box().clone_display() != Display::Contents {
556 return;
557 }
558
559 if self.style.pseudo.is_some_and(|p| !p.is_element_backed()) {
561 self.style.mutate_box().set_display(Display::Inline);
562 return;
563 }
564
565 let element = match element {
566 Some(e) => e,
567 None => return,
568 };
569
570 if is_effective_display_none_for_display_contents(element) {
571 self.style.mutate_box().set_display(Display::None);
572 }
573 }
574
575 #[cfg(feature = "gecko")]
578 fn adjust_for_text_control_editing_root(&mut self) {
579 use crate::properties::longhands::white_space_collapse::computed_value::T as WhiteSpaceCollapse;
580 use crate::selector_parser::PseudoElement;
581
582 if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {
583 return;
584 }
585
586 let old_collapse = self.style.get_inherited_text().clone_white_space_collapse();
587 let new_collapse = match old_collapse {
588 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => old_collapse,
589 WhiteSpaceCollapse::Collapse
590 | WhiteSpaceCollapse::PreserveSpaces
591 | WhiteSpaceCollapse::PreserveBreaks => WhiteSpaceCollapse::Preserve,
592 };
593 if new_collapse != old_collapse {
594 self.style
595 .mutate_inherited_text()
596 .set_white_space_collapse(new_collapse);
597 }
598
599 let box_style = self.style.get_box();
600 let overflow_x = box_style.clone_overflow_x();
601 let overflow_y = box_style.clone_overflow_y();
602
603 if overflow_x.is_scrollable() || overflow_y.is_scrollable() {
606 return;
607 }
608
609 let box_style = self.style.mutate_box();
610 box_style.set_overflow_x(Overflow::Auto);
611 box_style.set_overflow_y(Overflow::Auto);
612 }
613
614 #[cfg(feature = "gecko")]
621 fn adjust_for_fieldset_content(&mut self, layout_parent_style: &ComputedValues) {
622 use crate::selector_parser::PseudoElement;
623
624 if self.style.pseudo != Some(&PseudoElement::FieldsetContent) {
625 return;
626 }
627
628 let parent_display = layout_parent_style.get_box().clone_display();
632 let new_display = match parent_display {
633 Display::Flex | Display::InlineFlex => Some(Display::Flex),
634 Display::Grid | Display::InlineGrid => Some(Display::Grid),
635 _ => None,
636 };
637 if let Some(new_display) = new_display {
638 self.style.mutate_box().set_display(new_display);
639 }
640 }
641
642 fn adjust_for_table_text_align(&mut self) {
649 use crate::properties::longhands::text_align::computed_value::T as TextAlign;
650 if self.style.get_box().clone_display() != Display::Table {
651 return;
652 }
653
654 match self.style.get_inherited_text().clone_text_align() {
655 TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},
656 _ => return,
657 }
658
659 self.style
660 .mutate_inherited_text()
661 .set_text_align(TextAlign::Start)
662 }
663
664 #[cfg(feature = "gecko")]
665 fn should_suppress_linebreak<E>(
666 &self,
667 layout_parent_style: &ComputedValues,
668 element: Option<E>,
669 ) -> bool
670 where
671 E: TElement,
672 {
673 if self.style.is_floating() || self.style.is_absolutely_positioned() {
675 return false;
676 }
677 let parent_display = layout_parent_style.get_box().clone_display();
678 if layout_parent_style
679 .flags
680 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
681 {
682 if parent_display.is_line_participant() {
685 return true;
686 }
687 }
688 match self.style.get_box().clone_display() {
689 Display::RubyBase | Display::RubyText => true,
691 Display::RubyBaseContainer | Display::RubyTextContainer
700 if element.map_or(true, |e| e.is_html_element()) =>
701 {
702 false
703 },
704 _ => parent_display.is_ruby_type(),
708 }
709 }
710
711 #[cfg(feature = "gecko")]
717 fn adjust_for_ruby<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
718 where
719 E: TElement,
720 {
721 use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
722
723 let self_display = self.style.get_box().clone_display();
724 if self.should_suppress_linebreak(layout_parent_style, element) {
726 self.style
727 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
728 if !self.skip_item_display_fixup(element) {
730 let inline_display = self_display.inlinify();
731 if self_display != inline_display {
732 self.style
733 .mutate_box()
734 .set_adjusted_display(inline_display, false);
735 }
736 }
737 }
738 if self_display.is_ruby_level_container() {
743 self.style.reset_border_struct();
744 self.style.reset_padding_struct();
745 }
746
747 if self_display.is_ruby_type() {
750 let new_value = match self.style.get_text().clone_unicode_bidi() {
751 UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
752 UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
753 _ => None,
754 };
755 if let Some(new_value) = new_value {
756 self.style.mutate_text().set_unicode_bidi(new_value);
757 }
758 }
759 }
760
761 fn adjust_for_visited<E>(&mut self, element: Option<E>)
771 where
772 E: TElement,
773 {
774 if !self.style.has_visited_style() {
775 return;
776 }
777
778 let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());
779
780 if !is_link_element {
781 return;
782 }
783
784 if element.unwrap().is_visited_link() {
785 self.style
786 .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
787 } else {
788 self.style
790 .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
791 }
792 }
793
794 #[cfg(feature = "gecko")]
799 fn adjust_for_justify_items(&mut self) {
800 use crate::values::specified::align;
801 let justify_items = self.style.get_position().clone_justify_items();
802 if justify_items.specified != align::JustifyItems::legacy() {
803 return;
804 }
805
806 let parent_justify_items = self.style.get_parent_position().clone_justify_items();
807
808 if !parent_justify_items.computed.contains(AlignFlags::LEGACY) {
809 return;
810 }
811
812 if parent_justify_items.computed == justify_items.computed {
813 return;
814 }
815
816 self.style
817 .mutate_position()
818 .set_computed_justify_items(parent_justify_items.computed);
819 }
820
821 #[cfg(feature = "gecko")]
826 fn adjust_for_appearance<E>(&mut self, element: Option<E>)
827 where
828 E: TElement,
829 {
830 use crate::properties::longhands::appearance::computed_value::T as Appearance;
831 use crate::properties::longhands::line_height::computed_value::T as LineHeight;
832
833 let box_ = self.style.get_box();
834 let appearance = match box_.clone_appearance() {
835 Appearance::Auto => box_.clone__moz_default_appearance(),
836 a => a,
837 };
838
839 if appearance == Appearance::Menulist {
840 if self.style.get_font().clone_line_height() == LineHeight::normal() {
841 return;
842 }
843 if self.style.pseudo.is_some() {
844 return;
845 }
846 let is_html_select_element = element.map_or(false, |e| {
847 e.is_html_element() && e.local_name() == &*atom!("select")
848 });
849 if !is_html_select_element {
850 return;
851 }
852 self.style
853 .mutate_font()
854 .set_line_height(LineHeight::normal());
855 }
856 }
857
858 #[cfg(feature = "gecko")]
869 fn adjust_for_marker_pseudo(&mut self) {
870 use crate::values::computed::counters::Content;
871 use crate::values::computed::font::{FontFamily, FontSynthesis, FontSynthesisStyle};
872 use crate::values::computed::text::{LetterSpacing, WordSpacing};
873
874 let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker())
875 && self.style.get_list().clone_list_style_type().is_bullet()
876 && self.style.get_counters().clone_content() == Content::Normal;
877 if !is_legacy_marker {
878 return;
879 }
880 let flags = self.style.flags.get();
881 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) {
882 self.style
883 .mutate_font()
884 .set_font_family(FontFamily::moz_bullet().clone());
885
886 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT) {
890 self.style
891 .mutate_font()
892 .set_font_synthesis_weight(FontSynthesis::None);
893 }
894 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE) {
895 self.style
896 .mutate_font()
897 .set_font_synthesis_style(FontSynthesisStyle::None);
898 }
899 }
900 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) {
901 self.style
902 .mutate_inherited_text()
903 .set_letter_spacing(LetterSpacing::normal());
904 }
905 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) {
906 self.style
907 .mutate_inherited_text()
908 .set_word_spacing(WordSpacing::normal());
909 }
910 }
911
912 fn adjust_for_try_tactic(&mut self, tactic: &PositionTryFallbacksTryTactic) {
920 debug_assert!(!tactic.is_empty());
921 let wm = self.style.writing_mode;
923 for tactic in tactic.iter() {
925 use PositionTryFallbacksTryTacticKeyword::*;
926 match tactic {
927 FlipBlock => {
928 self.flip_self_alignment(true);
929 self.flip_insets_and_margins(wm.is_vertical());
930 },
931 FlipInline => {
932 self.flip_self_alignment(false);
933 self.flip_insets_and_margins(wm.is_horizontal());
934 },
935 FlipX => {
936 self.flip_self_alignment(wm.is_vertical());
937 self.flip_insets_and_margins(true);
938 },
939 FlipY => {
940 self.flip_self_alignment(wm.is_horizontal());
941 self.flip_insets_and_margins(false);
942 },
943 FlipStart => {
944 self.flip_start();
945 },
946 }
947 self.apply_position_area_tactic(*tactic);
948 }
949 }
950
951 fn apply_position_area_tactic(&mut self, tactic: PositionTryFallbacksTryTacticKeyword) {
952 let pos = self.style.get_position();
953 let old = pos.clone_position_area();
954 let wm = self.style.writing_mode;
955 let new = old.with_tactic(wm, tactic);
956 if new == old {
957 return;
958 }
959 let pos = self.style.mutate_position();
960 pos.set_position_area(new);
961 }
962
963 fn swap_insets(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
965 debug_assert_ne!(a_side, b_side);
966 let pos = self.style.mutate_position();
967 let mut a = pos.get_inset(a_side).clone();
968 a.try_tactic_adjustment(a_side, b_side);
969 let mut b = pos.get_inset(b_side).clone();
970 b.try_tactic_adjustment(b_side, a_side);
971 pos.set_inset(a_side, b);
972 pos.set_inset(b_side, a);
973 }
974
975 fn swap_margins(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
976 debug_assert_ne!(a_side, b_side);
977 let margin = self.style.get_margin();
978 let mut a = margin.get_margin(a_side).clone();
979 a.try_tactic_adjustment(a_side, b_side);
980 let mut b = margin.get_margin(b_side).clone();
981 b.try_tactic_adjustment(b_side, a_side);
982 let margin = self.style.mutate_margin();
983 margin.set_margin(a_side, b);
984 margin.set_margin(b_side, a);
985 }
986
987 fn swap_sizes(&mut self, block_start: PhysicalSide, inline_start: PhysicalSide) {
988 let pos = self.style.mutate_position();
989 let mut min_width = pos.clone_min_width();
990 min_width.try_tactic_adjustment(inline_start, block_start);
991 let mut max_width = pos.clone_max_width();
992 max_width.try_tactic_adjustment(inline_start, block_start);
993 let mut width = pos.clone_width();
994 width.try_tactic_adjustment(inline_start, block_start);
995
996 let mut min_height = pos.clone_min_height();
997 min_height.try_tactic_adjustment(block_start, inline_start);
998 let mut max_height = pos.clone_max_height();
999 max_height.try_tactic_adjustment(block_start, inline_start);
1000 let mut height = pos.clone_height();
1001 height.try_tactic_adjustment(block_start, inline_start);
1002
1003 let pos = self.style.mutate_position();
1004 pos.set_width(height);
1005 pos.set_height(width);
1006 pos.set_max_width(max_height);
1007 pos.set_max_height(max_width);
1008 pos.set_min_width(min_height);
1009 pos.set_min_height(min_width);
1010 }
1011
1012 fn flip_start(&mut self) {
1013 let wm = self.style.writing_mode;
1014 let bs = wm.block_start_physical_side();
1015 let is = wm.inline_start_physical_side();
1016 let be = wm.block_end_physical_side();
1017 let ie = wm.inline_end_physical_side();
1018 self.swap_sizes(bs, is);
1019 self.swap_insets(bs, is);
1020 self.swap_insets(ie, be);
1021 self.swap_margins(bs, is);
1022 self.swap_margins(ie, be);
1023 self.flip_alignment_start();
1024 }
1025
1026 fn flip_insets_and_margins(&mut self, horizontal: bool) {
1027 if horizontal {
1028 self.swap_insets(PhysicalSide::Left, PhysicalSide::Right);
1029 self.swap_margins(PhysicalSide::Left, PhysicalSide::Right);
1030 } else {
1031 self.swap_insets(PhysicalSide::Top, PhysicalSide::Bottom);
1032 self.swap_margins(PhysicalSide::Top, PhysicalSide::Bottom);
1033 }
1034 }
1035
1036 fn flip_alignment_start(&mut self) {
1037 let pos = self.style.get_position();
1038 let align = pos.clone_align_self();
1039 let mut justify = pos.clone_justify_self();
1040 if align == justify {
1041 return;
1042 }
1043
1044 if matches!(justify.value(), AlignFlags::LEFT | AlignFlags::RIGHT) {
1047 let left = justify.value() == AlignFlags::LEFT;
1048 let ltr = self.style.writing_mode.is_bidi_ltr();
1049 justify = justify.with_value(if left == ltr {
1050 AlignFlags::SELF_START
1051 } else {
1052 AlignFlags::SELF_END
1053 });
1054 }
1055
1056 let pos = self.style.mutate_position();
1057 pos.set_align_self(justify);
1058 pos.set_justify_self(align);
1059 }
1060
1061 fn flip_self_alignment(&mut self, block: bool) {
1062 let pos = self.style.get_position();
1063 let cur = if block {
1064 pos.clone_align_self()
1065 } else {
1066 pos.clone_justify_self()
1067 };
1068 let flipped = cur.flip_position();
1069 if flipped == cur {
1070 return;
1071 }
1072 let pos = self.style.mutate_position();
1073 if block {
1074 pos.set_align_self(flipped);
1075 } else {
1076 pos.set_justify_self(flipped);
1077 }
1078 }
1079
1080 pub fn adjust<E>(
1082 &mut self,
1083 layout_parent_style: &ComputedValues,
1084 element: Option<E>,
1085 try_tactic: &PositionTryFallbacksTryTactic,
1086 ) where
1087 E: TElement,
1088 {
1089 if cfg!(debug_assertions) {
1090 if let Some(e) = element {
1091 if let Some(p) = e.implemented_pseudo_element() {
1092 debug_assert!(
1096 self.style.pseudo.is_some(),
1097 "Someone really messed up (no pseudo style for {e:?}, {p:?})"
1098 );
1099 }
1100 }
1101 }
1102 self.adjust_for_visited(element);
1112 #[cfg(feature = "gecko")]
1113 {
1114 self.adjust_for_prohibited_display_contents(element);
1115 self.adjust_for_fieldset_content(layout_parent_style);
1116 self.adjust_for_text_control_editing_root();
1119 }
1120 self.adjust_for_top_layer();
1121 self.blockify_if_necessary(layout_parent_style, element);
1122 #[cfg(feature = "gecko")]
1123 self.adjust_for_webkit_line_clamp();
1124 self.adjust_for_position();
1125 self.adjust_for_overflow();
1126 #[cfg(feature = "gecko")]
1127 {
1128 self.adjust_for_contain();
1129 self.adjust_for_contain_intrinsic_size();
1130 self.adjust_for_justify_items();
1131 }
1132 self.adjust_for_table_text_align();
1133 self.adjust_for_border_width();
1134 #[cfg(feature = "gecko")]
1135 self.adjust_for_column_rule_width();
1136 self.adjust_for_outline_width();
1137 self.adjust_for_writing_mode(layout_parent_style);
1138 #[cfg(feature = "gecko")]
1139 {
1140 self.adjust_for_ruby(layout_parent_style, element);
1141 self.adjust_for_appearance(element);
1142 self.adjust_for_marker_pseudo();
1143 }
1144 if !try_tactic.is_empty() {
1145 self.adjust_for_try_tactic(try_tactic);
1146 }
1147 self.set_bits();
1148 }
1149}