1use crate::computed_value_flags::ComputedValueFlags;
9use crate::dom::TElement;
10use crate::logical_geometry::PhysicalSide;
11#[cfg(feature = "gecko")]
12use crate::properties::LonghandId;
13use crate::properties::longhands::display::computed_value::T as Display;
14use crate::properties::longhands::float::computed_value::T as Float;
15use crate::properties::longhands::position::computed_value::T as Position;
16#[cfg(feature = "gecko")]
17use crate::properties::longhands::{
18 contain::computed_value::T as Contain, container_type::computed_value::T as ContainerType,
19 content_visibility::computed_value::T as ContentVisibility,
20};
21use crate::properties::{ComputedValues, LonghandIdSet, StyleBuilder};
22use crate::values::computed::position::{
23 PositionTryFallbacksTryTactic, PositionTryFallbacksTryTacticKeyword, TryTacticAdjustment,
24};
25use crate::values::specified::align::AlignFlags;
26
27pub struct StyleAdjuster<'a, 'b: 'a> {
41 style: &'a mut StyleBuilder<'b>,
42}
43
44fn is_topmost_svg_svg_element<E>(e: E) -> bool
45where
46 E: TElement,
47{
48 debug_assert!(e.is_svg_element());
49 if e.local_name() != &*atom!("svg") {
50 return false;
51 }
52
53 let parent = match e.traversal_parent() {
54 Some(n) => n,
55 None => return true,
56 };
57
58 if !parent.is_svg_element() {
59 return true;
60 }
61
62 parent.local_name() == &*atom!("foreignObject")
63}
64
65fn is_effective_display_none_for_display_contents<E>(element: E) -> bool
67where
68 E: TElement,
69{
70 use crate::Atom;
71
72 const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
73 atom!("br"),
74 atom!("wbr"),
75 atom!("meter"),
76 atom!("progress"),
77 atom!("canvas"),
78 atom!("embed"),
79 atom!("object"),
80 atom!("audio"),
81 atom!("iframe"),
82 atom!("img"),
83 atom!("video"),
84 atom!("frame"),
85 atom!("frameset"),
86 atom!("input"),
87 atom!("textarea"),
88 atom!("select"),
89 ];
90
91 const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
97 atom!("svg"),
98 atom!("a"),
99 atom!("g"),
100 atom!("use"),
101 atom!("tspan"),
102 atom!("textPath"),
103 ];
104
105 if element.is_html_element() {
107 let local_name = element.local_name();
108 return SPECIAL_HTML_ELEMENTS
109 .iter()
110 .any(|name| &**name == local_name);
111 }
112
113 if element.is_svg_element() {
115 if is_topmost_svg_svg_element(element) {
116 return true;
117 }
118 let local_name = element.local_name();
119 return !SPECIAL_SVG_ELEMENTS
120 .iter()
121 .any(|name| &**name == local_name);
122 }
123
124 if element.is_mathml_element() {
126 return true;
127 }
128
129 false
130}
131
132impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
133 #[inline]
135 pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
136 StyleAdjuster { style }
137 }
138
139 fn adjust_for_top_layer(&mut self) {
145 if !self.style.in_top_layer() {
146 return;
147 }
148 if !self.style.is_absolutely_positioned() {
149 self.style.mutate_box().set_position(Position::Absolute);
150 }
151 if self.style.get_box().get_display().is_contents() {
152 self.style.mutate_box().set_display(Display::Block);
153 }
154 }
155
156 #[cfg(feature = "gecko")]
163 fn adjust_for_webkit_line_clamp(&mut self) {
164 use crate::properties::longhands::_moz_box_orient::computed_value::T as BoxOrient;
165 use crate::values::specified::box_::{DisplayInside, DisplayOutside};
166 let box_style = self.style.get_box();
167 let line_clamp = box_style.get_line_clamp();
168 if line_clamp.is_none() {
169 return;
170 }
171
172 let disp = *box_style.get_display();
173 if disp.inside() != DisplayInside::WebkitBox
174 || *self.style.get_xul().get__moz_box_orient() != BoxOrient::Vertical
175 {
176 return;
177 }
178 let new_display = if disp.outside() == DisplayOutside::Block {
179 Display::FlowRoot
180 } else {
181 debug_assert_eq!(disp.outside(), DisplayOutside::Inline);
182 Display::InlineBlock
183 };
184 self.style
185 .mutate_box()
186 .set_adjusted_display(new_display, false);
187 }
188
189 fn adjust_for_position(&mut self) {
195 if self.style.is_absolutely_positioned() && self.style.is_floating() {
196 self.style.mutate_box().set_float(Float::None);
197 }
198 }
199
200 fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool
203 where
204 E: TElement,
205 {
206 if let Some(pseudo) = self.style.pseudo {
207 return pseudo.skip_item_display_fixup();
208 }
209
210 element.is_some_and(|e| e.skip_item_display_fixup())
211 }
212
213 fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
220 where
221 E: TElement,
222 {
223 let mut blockify = false;
224 macro_rules! blockify_if {
225 ($if_what:expr) => {
226 if !blockify {
227 blockify = $if_what;
228 }
229 };
230 }
231
232 blockify_if!(self.style.is_root_element);
233 if !self.skip_item_display_fixup(element) {
234 let parent_display = *layout_parent_style.get_box().get_display();
235 blockify_if!(parent_display.is_item_container());
236 }
237
238 let is_item_or_root = blockify;
239
240 blockify_if!(self.style.is_floating());
241 blockify_if!(self.style.is_absolutely_positioned());
242
243 if !blockify {
244 return;
245 }
246
247 let display = *self.style.get_box().get_display();
248 let blockified_display = display.equivalent_block_display(self.style.is_root_element);
249 if display != blockified_display {
250 self.style
251 .mutate_box()
252 .set_adjusted_display(blockified_display, is_item_or_root);
253 }
254 }
255
256 fn set_bits(&mut self) {
258 let box_style = self.style.get_box();
259 let display = *box_style.get_display();
260
261 if !display.is_contents() {
262 if !self.style.get_text().get_text_decoration_line().is_empty() {
263 self.style
264 .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
265 }
266
267 if *self.style.get_effects().get_opacity() == 0. {
268 self.style
269 .add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);
270 }
271 } else if self
272 .style
273 .get_parent_box()
274 .get_display()
275 .is_item_container()
276 || self
277 .style
278 .get_parent_flags()
279 .contains(ComputedValueFlags::DISPLAY_CONTENTS_IN_ITEM_CONTAINER)
280 {
281 self.style
282 .add_flags(ComputedValueFlags::DISPLAY_CONTENTS_IN_ITEM_CONTAINER);
283 }
284
285 if self.style.pseudo.is_some_and(|p| p.is_first_line()) {
286 self.style
287 .add_flags(ComputedValueFlags::IS_IN_FIRST_LINE_SUBTREE);
288 }
289
290 if self.style.is_root_element {
291 self.style
292 .add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
293 }
294
295 #[cfg(feature = "gecko")]
296 if box_style
297 .get_effective_containment()
298 .contains(Contain::STYLE)
299 {
300 self.style
301 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
302 }
303
304 if box_style.get_container_type().is_size_container_type() {
305 self.style
306 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE);
307 }
308 }
309
310 #[cfg(feature = "gecko")]
317 pub fn adjust_for_text(&mut self) {
318 debug_assert!(!self.style.is_root_element);
319 self.adjust_for_text_combine_upright();
320 self.adjust_for_text_in_ruby();
321 self.set_bits();
322 }
323
324 #[cfg(feature = "gecko")]
335 fn adjust_for_text_combine_upright(&mut self) {
336 use crate::computed_values::text_combine_upright::T as TextCombineUpright;
337 use crate::computed_values::writing_mode::T as WritingMode;
338 use crate::logical_geometry;
339
340 let writing_mode = *self.style.get_inherited_box().get_writing_mode();
341 let text_combine_upright = *self.style.get_inherited_text().get_text_combine_upright();
342
343 if matches!(
344 writing_mode,
345 WritingMode::VerticalRl | WritingMode::VerticalLr
346 ) && text_combine_upright == TextCombineUpright::All
347 {
348 self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
349 self.style
350 .mutate_inherited_box()
351 .set_writing_mode(WritingMode::HorizontalTb);
352 self.style.writing_mode =
353 logical_geometry::WritingMode::new(self.style.get_inherited_box());
354 }
355 }
356
357 #[cfg(feature = "gecko")]
364 fn adjust_for_text_in_ruby(&mut self) {
365 let parent_display = *self.style.get_parent_box().get_display();
366 if parent_display.is_ruby_type()
367 || self
368 .style
369 .get_parent_flags()
370 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
371 {
372 self.style
373 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
374 }
375 }
376
377 fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {
391 let our_writing_mode = *self.style.get_inherited_box().get_writing_mode();
392 let parent_writing_mode = *layout_parent_style.get_inherited_box().get_writing_mode();
393
394 if our_writing_mode != parent_writing_mode
395 && *self.style.get_box().get_display() == Display::Inline
396 {
397 if cfg!(feature = "servo") {
400 self.style
401 .mutate_box()
402 .set_adjusted_display(Display::InlineBlock, false);
403 } else {
404 self.style.mutate_box().set_display(Display::InlineBlock);
405 }
406 }
407 }
408
409 fn adjust_for_overflow(&mut self) {
415 let overflow_x = *self.style.get_box().get_overflow_x();
416 let overflow_y = *self.style.get_box().get_overflow_y();
417 if overflow_x == overflow_y {
418 return; }
420
421 if overflow_x.is_scrollable() != overflow_y.is_scrollable() {
422 let box_style = self.style.mutate_box();
423 box_style.set_overflow_x(overflow_x.to_scrollable());
424 box_style.set_overflow_y(overflow_y.to_scrollable());
425 }
426 }
427
428 #[cfg(feature = "gecko")]
429 fn adjust_for_contain(&mut self) {
430 let box_style = self.style.get_box();
431 let container_type = *box_style.get_container_type();
432 let content_visibility = *box_style.get_content_visibility();
433 if !container_type.is_size_container_type()
434 && content_visibility == ContentVisibility::Visible
435 {
436 debug_assert_eq!(
437 box_style.get_contain(),
438 box_style.get_effective_containment()
439 );
440 return;
441 }
442 let old_contain = *box_style.get_contain();
443 let mut new_contain = old_contain;
444 match content_visibility {
445 ContentVisibility::Visible => {},
446 ContentVisibility::Auto => {
450 new_contain.insert(Contain::LAYOUT | Contain::PAINT | Contain::STYLE)
451 },
452 ContentVisibility::Hidden => new_contain
453 .insert(Contain::LAYOUT | Contain::PAINT | Contain::SIZE | Contain::STYLE),
454 }
455 if container_type.intersects(ContainerType::INLINE_SIZE) {
456 new_contain.insert(Contain::STYLE | Contain::INLINE_SIZE);
460 } else if container_type.intersects(ContainerType::SIZE) {
461 new_contain.insert(Contain::STYLE | Contain::SIZE);
465 }
466 if new_contain == old_contain {
467 debug_assert_eq!(
468 box_style.get_contain(),
469 box_style.get_effective_containment()
470 );
471 return;
472 }
473 self.style
474 .mutate_box()
475 .set_effective_containment(new_contain);
476 }
477
478 #[cfg(feature = "gecko")]
483 fn adjust_for_contain_intrinsic_size(&mut self) {
484 let content_visibility = *self.style.get_box().get_content_visibility();
485 if content_visibility != ContentVisibility::Auto {
486 return;
487 }
488
489 let pos = self.style.get_position();
490 let new_width = pos.get_contain_intrinsic_width().add_auto_if_needed();
491 let new_height = pos.get_contain_intrinsic_height().add_auto_if_needed();
492 if new_width.is_none() && new_height.is_none() {
493 return;
494 }
495
496 let pos = self.style.mutate_position();
497 if let Some(width) = new_width {
498 pos.set_contain_intrinsic_width(width);
499 }
500 if let Some(height) = new_height {
501 pos.set_contain_intrinsic_height(height);
502 }
503 }
504
505 fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)
511 where
512 E: TElement,
513 {
514 if *self.style.get_box().get_display() != Display::Contents {
515 return;
516 }
517
518 #[cfg(feature = "gecko")]
520 if self.style.pseudo.is_some_and(|p| !p.is_element_backed()) {
521 self.style.mutate_box().set_display(Display::Inline);
522 return;
523 }
524
525 let element = match element {
526 Some(e) => e,
527 None => return,
528 };
529
530 if is_effective_display_none_for_display_contents(element) {
531 self.style.mutate_box().set_display(Display::None);
532 }
533 }
534
535 #[cfg(feature = "gecko")]
538 fn adjust_for_text_control_editing_root(&mut self) {
539 use crate::properties::longhands::white_space_collapse::computed_value::T as WhiteSpaceCollapse;
540 use crate::selector_parser::PseudoElement;
541
542 if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {
543 return;
544 }
545
546 let old_collapse = *self.style.get_inherited_text().get_white_space_collapse();
547 let new_collapse = match old_collapse {
548 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => old_collapse,
549 WhiteSpaceCollapse::Collapse
550 | WhiteSpaceCollapse::PreserveSpaces
551 | WhiteSpaceCollapse::PreserveBreaks => WhiteSpaceCollapse::Preserve,
552 };
553 if new_collapse != old_collapse {
554 self.style
555 .mutate_inherited_text()
556 .set_white_space_collapse(new_collapse);
557 }
558 }
559
560 #[cfg(feature = "gecko")]
563 fn adjust_for_fieldset_content(&mut self) {
564 use crate::selector_parser::PseudoElement;
565 if self.style.pseudo != Some(&PseudoElement::MozFieldsetContent) {
566 return;
567 }
568 let parent_display = *self.style.get_parent_box().get_display();
569 debug_assert!(
570 !parent_display.is_contents(),
571 "How did we create a fieldset-content box with display: contents?"
572 );
573 let new_display = match parent_display {
574 Display::Flex | Display::InlineFlex => Some(Display::Flex),
575 Display::Grid | Display::InlineGrid => Some(Display::Grid),
576 _ => None,
577 };
578 if let Some(new_display) = new_display {
579 self.style.mutate_box().set_display(new_display);
580 }
581 }
582
583 fn adjust_for_table_text_align(&mut self) {
590 use crate::properties::longhands::text_align::computed_value::T as TextAlign;
591 if *self.style.get_box().get_display() != Display::Table {
592 return;
593 }
594
595 match *self.style.get_inherited_text().get_text_align() {
596 TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},
597 _ => return,
598 }
599
600 self.style
601 .mutate_inherited_text()
602 .set_text_align(TextAlign::Start)
603 }
604
605 #[cfg(feature = "gecko")]
606 fn should_suppress_linebreak<E>(&self, element: Option<E>) -> bool
607 where
608 E: TElement,
609 {
610 if self.style.is_floating() || self.style.is_absolutely_positioned() {
612 return false;
613 }
614 let parent_display = *self.style.get_parent_box().get_display();
615 if self
616 .style
617 .get_parent_flags()
618 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
619 {
620 if parent_display.is_line_participant() || parent_display.is_contents() {
623 return true;
624 }
625 }
626 match *self.style.get_box().get_display() {
627 Display::RubyBase | Display::RubyText => true,
629 Display::RubyBaseContainer | Display::RubyTextContainer
638 if element.is_none_or(|e| e.is_html_element()) =>
639 {
640 false
641 },
642 _ => parent_display.is_ruby_type(),
646 }
647 }
648
649 #[cfg(feature = "gecko")]
655 fn adjust_for_ruby<E>(&mut self, element: Option<E>)
656 where
657 E: TElement,
658 {
659 use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
660
661 let self_display = *self.style.get_box().get_display();
662 if self.should_suppress_linebreak(element) {
664 self.style
665 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
666 if !self.skip_item_display_fixup(element) {
668 let inline_display = self_display.inlinify();
669 if self_display != inline_display {
670 self.style
671 .mutate_box()
672 .set_adjusted_display(inline_display, false);
673 }
674 }
675 }
676 if self_display.is_ruby_level_container() {
681 self.style.reset_border_struct();
682 self.style.reset_padding_struct();
683 }
684
685 if self_display.is_ruby_type() {
688 let new_value = match *self.style.get_text().get_unicode_bidi() {
689 UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
690 UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
691 _ => None,
692 };
693 if let Some(new_value) = new_value {
694 self.style.mutate_text().set_unicode_bidi(new_value);
695 }
696 }
697 }
698
699 fn adjust_for_visited<E>(&mut self, element: Option<E>)
709 where
710 E: TElement,
711 {
712 if !self.style.has_visited_style() {
713 return;
714 }
715
716 let is_link_element = self.style.pseudo.is_none() && element.is_some_and(|e| e.is_link());
717
718 if !is_link_element {
719 return;
720 }
721
722 if element.unwrap().is_visited_link() {
723 self.style
724 .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
725 } else {
726 self.style
728 .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
729 }
730 }
731
732 fn adjust_for_justify_items(&mut self) {
737 use crate::values::specified::align;
738 let justify_items = *self.style.get_position().get_justify_items();
739 if justify_items.specified != align::JustifyItems::legacy() {
740 return;
741 }
742
743 let parent_justify_items = *self.style.get_parent_position().get_justify_items();
744
745 if !parent_justify_items.computed.contains(AlignFlags::LEGACY) {
746 return;
747 }
748
749 if parent_justify_items.computed == justify_items.computed {
750 return;
751 }
752
753 self.style
754 .mutate_position()
755 .set_computed_justify_items(parent_justify_items.computed);
756 }
757
758 fn adjust_for_appearance<E>(&mut self, element: Option<E>)
763 where
764 E: TElement,
765 {
766 use crate::properties::longhands::appearance::computed_value::T as Appearance;
767 use crate::properties::longhands::line_height::computed_value::T as LineHeight;
768
769 let box_ = self.style.get_box();
770 let appearance = match *box_.get_appearance() {
771 Appearance::Auto => *box_.get__moz_default_appearance(),
772 a => a,
773 };
774
775 if appearance == Appearance::Menulist {
776 if *self.style.get_font().get_line_height() == LineHeight::normal() {
777 return;
778 }
779 if self.style.pseudo.is_some() {
780 return;
781 }
782 let is_html_select_element =
783 element.is_some_and(|e| e.is_html_element() && e.local_name() == &*atom!("select"));
784 if !is_html_select_element {
785 return;
786 }
787 self.style
788 .mutate_font()
789 .set_line_height(LineHeight::normal());
790 }
791 }
792
793 #[cfg(feature = "gecko")]
804 fn adjust_for_marker_pseudo(&mut self, author_or_user_specified_properties: &LonghandIdSet) {
805 use crate::values::computed::counters::Content;
806 use crate::values::computed::font::{FontFamily, FontSynthesis, FontSynthesisStyle};
807 use crate::values::computed::text::{LetterSpacing, WordSpacing};
808
809 let is_legacy_marker = self.style.pseudo.is_some_and(|p| p.is_marker())
810 && self.style.get_list().get_list_style_type().is_bullet()
811 && matches!(self.style.get_counters().get_content(), Content::Normal);
812 if !is_legacy_marker {
813 return;
814 }
815 if !author_or_user_specified_properties.contains(LonghandId::FontFamily) {
816 self.style
817 .mutate_font()
818 .set_font_family(FontFamily::moz_bullet().clone());
819
820 if !author_or_user_specified_properties.contains(LonghandId::FontSynthesisWeight) {
824 self.style
825 .mutate_font()
826 .set_font_synthesis_weight(FontSynthesis::None);
827 }
828 if !author_or_user_specified_properties.contains(LonghandId::FontSynthesisStyle) {
829 self.style
830 .mutate_font()
831 .set_font_synthesis_style(FontSynthesisStyle::None);
832 }
833 }
834 if !author_or_user_specified_properties.contains(LonghandId::LetterSpacing) {
835 self.style
836 .mutate_inherited_text()
837 .set_letter_spacing(LetterSpacing::normal());
838 }
839 if !author_or_user_specified_properties.contains(LonghandId::WordSpacing) {
840 self.style
841 .mutate_inherited_text()
842 .set_word_spacing(WordSpacing::normal());
843 }
844 }
845
846 fn adjust_for_try_tactic(&mut self, tactic: &PositionTryFallbacksTryTactic) {
854 debug_assert!(!tactic.is_empty());
855 let wm = self.style.writing_mode;
857 for tactic in tactic.iter() {
859 use PositionTryFallbacksTryTacticKeyword::*;
860 match tactic {
861 FlipBlock => {
862 self.flip_self_alignment(true);
863 self.flip_insets_and_margins(wm.is_vertical());
864 },
865 FlipInline => {
866 self.flip_self_alignment(false);
867 self.flip_insets_and_margins(wm.is_horizontal());
868 },
869 FlipX => {
870 self.flip_self_alignment(wm.is_vertical());
871 self.flip_insets_and_margins(true);
872 },
873 FlipY => {
874 self.flip_self_alignment(wm.is_horizontal());
875 self.flip_insets_and_margins(false);
876 },
877 FlipStart => {
878 self.flip_start();
879 },
880 }
881 self.apply_position_area_tactic(*tactic);
882 }
883 }
884
885 fn apply_position_area_tactic(&mut self, tactic: PositionTryFallbacksTryTacticKeyword) {
886 let pos = self.style.get_position();
887 let old = *pos.get_position_area();
888 let wm = self.style.writing_mode;
889 let new = old.with_tactic(wm, tactic);
890 if new == old {
891 return;
892 }
893 let pos = self.style.mutate_position();
894 pos.set_position_area(new);
895 }
896
897 fn swap_insets(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
899 debug_assert_ne!(a_side, b_side);
900 let pos = self.style.mutate_position();
901 let mut a = pos.get_inset(a_side).clone();
902 a.try_tactic_adjustment(a_side, b_side);
903 let mut b = pos.get_inset(b_side).clone();
904 b.try_tactic_adjustment(b_side, a_side);
905 pos.set_inset(a_side, b);
906 pos.set_inset(b_side, a);
907 }
908
909 fn swap_margins(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
910 debug_assert_ne!(a_side, b_side);
911 let margin = self.style.get_margin();
912 let mut a = margin.get_margin(a_side).clone();
913 a.try_tactic_adjustment(a_side, b_side);
914 let mut b = margin.get_margin(b_side).clone();
915 b.try_tactic_adjustment(b_side, a_side);
916 let margin = self.style.mutate_margin();
917 margin.set_margin(a_side, b);
918 margin.set_margin(b_side, a);
919 }
920
921 fn swap_sizes(&mut self, block_start: PhysicalSide, inline_start: PhysicalSide) {
922 let pos = self.style.mutate_position();
923 let mut min_width = pos.slow_clone_min_width();
924 min_width.try_tactic_adjustment(inline_start, block_start);
925 let mut max_width = pos.slow_clone_max_width();
926 max_width.try_tactic_adjustment(inline_start, block_start);
927 let mut width = pos.slow_clone_width();
928 width.try_tactic_adjustment(inline_start, block_start);
929
930 let mut min_height = pos.slow_clone_min_height();
931 min_height.try_tactic_adjustment(block_start, inline_start);
932 let mut max_height = pos.slow_clone_max_height();
933 max_height.try_tactic_adjustment(block_start, inline_start);
934 let mut height = pos.slow_clone_height();
935 height.try_tactic_adjustment(block_start, inline_start);
936
937 let pos = self.style.mutate_position();
938 pos.set_width(height);
939 pos.set_height(width);
940 pos.set_max_width(max_height);
941 pos.set_max_height(max_width);
942 pos.set_min_width(min_height);
943 pos.set_min_height(min_width);
944 }
945
946 fn flip_start(&mut self) {
947 let wm = self.style.writing_mode;
948 let bs = wm.block_start_physical_side();
949 let is = wm.inline_start_physical_side();
950 let be = wm.block_end_physical_side();
951 let ie = wm.inline_end_physical_side();
952 self.swap_sizes(bs, is);
953 self.swap_insets(bs, is);
954 self.swap_insets(ie, be);
955 self.swap_margins(bs, is);
956 self.swap_margins(ie, be);
957 self.flip_alignment_start();
958 }
959
960 fn flip_insets_and_margins(&mut self, horizontal: bool) {
961 if horizontal {
962 self.swap_insets(PhysicalSide::Left, PhysicalSide::Right);
963 self.swap_margins(PhysicalSide::Left, PhysicalSide::Right);
964 } else {
965 self.swap_insets(PhysicalSide::Top, PhysicalSide::Bottom);
966 self.swap_margins(PhysicalSide::Top, PhysicalSide::Bottom);
967 }
968 }
969
970 fn flip_alignment_start(&mut self) {
971 let pos = self.style.get_position();
972 let align = *pos.get_align_self();
973 let mut justify = *pos.get_justify_self();
974 if align == justify {
975 return;
976 }
977
978 if matches!(justify.value(), AlignFlags::LEFT | AlignFlags::RIGHT) {
981 let left = justify.value() == AlignFlags::LEFT;
982 let ltr = self.style.writing_mode.is_bidi_ltr();
983 justify = justify.with_value(if left == ltr {
984 AlignFlags::SELF_START
985 } else {
986 AlignFlags::SELF_END
987 });
988 }
989
990 let pos = self.style.mutate_position();
991 pos.set_align_self(justify);
992 pos.set_justify_self(align);
993 }
994
995 fn flip_self_alignment(&mut self, block: bool) {
996 let pos = self.style.get_position();
997 let cur = if block {
998 *pos.get_align_self()
999 } else {
1000 *pos.get_justify_self()
1001 };
1002 let flipped = cur.flip_position();
1003 if flipped == cur {
1004 return;
1005 }
1006 let pos = self.style.mutate_position();
1007 if block {
1008 pos.set_align_self(flipped);
1009 } else {
1010 pos.set_justify_self(flipped);
1011 }
1012 }
1013
1014 #[allow(unused_variables)]
1016 pub fn adjust<E>(
1017 &mut self,
1018 layout_parent_style: &ComputedValues,
1019 element: Option<E>,
1020 try_tactic: &PositionTryFallbacksTryTactic,
1021 author_or_user_specified_properties: &LonghandIdSet,
1022 ) where
1023 E: TElement,
1024 {
1025 if cfg!(debug_assertions)
1026 && let Some(e) = element
1027 && let Some(p) = e.implemented_pseudo_element()
1028 {
1029 debug_assert!(
1033 self.style.pseudo.is_some(),
1034 "Someone really messed up (no pseudo style for {e:?}, {p:?})"
1035 );
1036 }
1037 self.adjust_for_visited(element);
1047 self.adjust_for_prohibited_display_contents(element);
1048 #[cfg(feature = "gecko")]
1049 {
1050 self.adjust_for_fieldset_content();
1051 self.adjust_for_text_control_editing_root();
1052 }
1053 self.adjust_for_top_layer();
1054 self.blockify_if_necessary(layout_parent_style, element);
1055 #[cfg(feature = "gecko")]
1056 self.adjust_for_webkit_line_clamp();
1057 self.adjust_for_position();
1058 self.adjust_for_overflow();
1059 #[cfg(feature = "gecko")]
1060 {
1061 self.adjust_for_contain();
1062 self.adjust_for_contain_intrinsic_size();
1063 }
1064 self.adjust_for_justify_items();
1065 self.adjust_for_table_text_align();
1066 self.adjust_for_writing_mode(layout_parent_style);
1067 #[cfg(feature = "gecko")]
1068 self.adjust_for_ruby(element);
1069 self.adjust_for_appearance(element);
1070 #[cfg(feature = "gecko")]
1071 self.adjust_for_marker_pseudo(author_or_user_specified_properties);
1072 if !try_tactic.is_empty() {
1073 self.adjust_for_try_tactic(try_tactic);
1074 }
1075 self.set_bits();
1076 }
1077}