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::{ComputedValues, StyleBuilder};
21use crate::values::computed::position::{
22 PositionTryFallbacksTryTactic, PositionTryFallbacksTryTacticKeyword, TryTacticAdjustment,
23};
24use crate::values::specified::align::AlignFlags;
25
26pub struct StyleAdjuster<'a, 'b: 'a> {
40 style: &'a mut StyleBuilder<'b>,
41}
42
43#[cfg(feature = "gecko")]
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
65#[cfg(feature = "gecko")]
67fn is_effective_display_none_for_display_contents<E>(element: E) -> bool
68where
69 E: TElement,
70{
71 use crate::Atom;
72
73 const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
74 atom!("br"),
75 atom!("wbr"),
76 atom!("meter"),
77 atom!("progress"),
78 atom!("canvas"),
79 atom!("embed"),
80 atom!("object"),
81 atom!("audio"),
82 atom!("iframe"),
83 atom!("img"),
84 atom!("video"),
85 atom!("frame"),
86 atom!("frameset"),
87 atom!("input"),
88 atom!("textarea"),
89 atom!("select"),
90 ];
91
92 const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
98 atom!("svg"),
99 atom!("a"),
100 atom!("g"),
101 atom!("use"),
102 atom!("tspan"),
103 atom!("textPath"),
104 ];
105
106 if element.is_html_element() {
108 let local_name = element.local_name();
109 return SPECIAL_HTML_ELEMENTS
110 .iter()
111 .any(|name| &**name == local_name);
112 }
113
114 if element.is_svg_element() {
116 if is_topmost_svg_svg_element(element) {
117 return true;
118 }
119 let local_name = element.local_name();
120 return !SPECIAL_SVG_ELEMENTS
121 .iter()
122 .any(|name| &**name == local_name);
123 }
124
125 if element.is_mathml_element() {
127 return true;
128 }
129
130 false
131}
132
133impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
134 #[inline]
136 pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
137 StyleAdjuster { style }
138 }
139
140 fn adjust_for_top_layer(&mut self) {
146 if !self.style.in_top_layer() {
147 return;
148 }
149 if !self.style.is_absolutely_positioned() {
150 self.style.mutate_box().set_position(Position::Absolute);
151 }
152 if self.style.get_box().clone_display().is_contents() {
153 self.style.mutate_box().set_display(Display::Block);
154 }
155 }
156
157 #[cfg(feature = "gecko")]
164 fn adjust_for_webkit_line_clamp(&mut self) {
165 use crate::properties::longhands::_moz_box_orient::computed_value::T as BoxOrient;
166 use crate::values::specified::box_::{DisplayInside, DisplayOutside};
167 let box_style = self.style.get_box();
168 if box_style.clone__webkit_line_clamp().is_none() {
169 return;
170 }
171 let disp = box_style.clone_display();
172 if disp.inside() != DisplayInside::WebkitBox {
173 return;
174 }
175 if self.style.get_xul().clone__moz_box_orient() != BoxOrient::Vertical {
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().clone_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().clone_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.clone_display();
260
261 if !display.is_contents() {
262 if !self
263 .style
264 .get_text()
265 .clone_text_decoration_line()
266 .is_empty()
267 {
268 self.style
269 .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
270 }
271
272 if self.style.get_effects().clone_opacity() == 0. {
273 self.style
274 .add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);
275 }
276 } else if self
277 .style
278 .get_parent_box()
279 .clone_display()
280 .is_item_container()
281 || self
282 .style
283 .get_parent_flags()
284 .contains(ComputedValueFlags::DISPLAY_CONTENTS_IN_ITEM_CONTAINER)
285 {
286 self.style
287 .add_flags(ComputedValueFlags::DISPLAY_CONTENTS_IN_ITEM_CONTAINER);
288 }
289
290 if self.style.pseudo.is_some_and(|p| p.is_first_line()) {
291 self.style
292 .add_flags(ComputedValueFlags::IS_IN_FIRST_LINE_SUBTREE);
293 }
294
295 if self.style.is_root_element {
296 self.style
297 .add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
298 }
299
300 #[cfg(feature = "gecko")]
301 if box_style
302 .clone_effective_containment()
303 .contains(Contain::STYLE)
304 {
305 self.style
306 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
307 }
308
309 if box_style.clone_container_type().is_size_container_type() {
310 self.style
311 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE);
312 }
313
314 #[cfg(feature = "servo")]
315 if self.style.get_parent_column().is_multicol() {
316 self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
317 }
318 }
319
320 #[cfg(feature = "gecko")]
327 pub fn adjust_for_text(&mut self) {
328 debug_assert!(!self.style.is_root_element);
329 self.adjust_for_text_combine_upright();
330 self.adjust_for_text_in_ruby();
331 self.set_bits();
332 }
333
334 #[cfg(feature = "gecko")]
345 fn adjust_for_text_combine_upright(&mut self) {
346 use crate::computed_values::text_combine_upright::T as TextCombineUpright;
347 use crate::computed_values::writing_mode::T as WritingMode;
348 use crate::logical_geometry;
349
350 let writing_mode = self.style.get_inherited_box().clone_writing_mode();
351 let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
352
353 if matches!(
354 writing_mode,
355 WritingMode::VerticalRl | WritingMode::VerticalLr
356 ) && text_combine_upright == TextCombineUpright::All
357 {
358 self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
359 self.style
360 .mutate_inherited_box()
361 .set_writing_mode(WritingMode::HorizontalTb);
362 self.style.writing_mode =
363 logical_geometry::WritingMode::new(self.style.get_inherited_box());
364 }
365 }
366
367 #[cfg(feature = "gecko")]
374 fn adjust_for_text_in_ruby(&mut self) {
375 let parent_display = self.style.get_parent_box().clone_display();
376 if parent_display.is_ruby_type()
377 || self
378 .style
379 .get_parent_flags()
380 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
381 {
382 self.style
383 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
384 }
385 }
386
387 fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {
401 let our_writing_mode = self.style.get_inherited_box().clone_writing_mode();
402 let parent_writing_mode = layout_parent_style.get_inherited_box().clone_writing_mode();
403
404 if our_writing_mode != parent_writing_mode
405 && self.style.get_box().clone_display() == Display::Inline
406 {
407 if cfg!(feature = "servo") {
410 self.style
411 .mutate_box()
412 .set_adjusted_display(Display::InlineBlock, false);
413 } else {
414 self.style.mutate_box().set_display(Display::InlineBlock);
415 }
416 }
417 }
418
419 fn adjust_for_overflow(&mut self) {
425 let overflow_x = self.style.get_box().clone_overflow_x();
426 let overflow_y = self.style.get_box().clone_overflow_y();
427 if overflow_x == overflow_y {
428 return; }
430
431 if overflow_x.is_scrollable() != overflow_y.is_scrollable() {
432 let box_style = self.style.mutate_box();
433 box_style.set_overflow_x(overflow_x.to_scrollable());
434 box_style.set_overflow_y(overflow_y.to_scrollable());
435 }
436 }
437
438 #[cfg(feature = "gecko")]
439 fn adjust_for_contain(&mut self) {
440 let box_style = self.style.get_box();
441 let container_type = box_style.clone_container_type();
442 let content_visibility = box_style.clone_content_visibility();
443 if !container_type.is_size_container_type()
444 && content_visibility == ContentVisibility::Visible
445 {
446 debug_assert_eq!(
447 box_style.clone_contain(),
448 box_style.clone_effective_containment()
449 );
450 return;
451 }
452 let old_contain = box_style.clone_contain();
453 let mut new_contain = old_contain;
454 match content_visibility {
455 ContentVisibility::Visible => {},
456 ContentVisibility::Auto => {
460 new_contain.insert(Contain::LAYOUT | Contain::PAINT | Contain::STYLE)
461 },
462 ContentVisibility::Hidden => new_contain
463 .insert(Contain::LAYOUT | Contain::PAINT | Contain::SIZE | Contain::STYLE),
464 }
465 if container_type.intersects(ContainerType::INLINE_SIZE) {
466 new_contain.insert(Contain::STYLE | Contain::INLINE_SIZE);
470 } else if container_type.intersects(ContainerType::SIZE) {
471 new_contain.insert(Contain::STYLE | Contain::SIZE);
475 }
476 if new_contain == old_contain {
477 debug_assert_eq!(
478 box_style.clone_contain(),
479 box_style.clone_effective_containment()
480 );
481 return;
482 }
483 self.style
484 .mutate_box()
485 .set_effective_containment(new_contain);
486 }
487
488 #[cfg(feature = "gecko")]
493 fn adjust_for_contain_intrinsic_size(&mut self) {
494 let content_visibility = self.style.get_box().clone_content_visibility();
495 if content_visibility != ContentVisibility::Auto {
496 return;
497 }
498
499 let pos = self.style.get_position();
500 let new_width = pos.clone_contain_intrinsic_width().add_auto_if_needed();
501 let new_height = pos.clone_contain_intrinsic_height().add_auto_if_needed();
502 if new_width.is_none() && new_height.is_none() {
503 return;
504 }
505
506 let pos = self.style.mutate_position();
507 if let Some(width) = new_width {
508 pos.set_contain_intrinsic_width(width);
509 }
510 if let Some(height) = new_height {
511 pos.set_contain_intrinsic_height(height);
512 }
513 }
514
515 #[cfg(feature = "gecko")]
521 fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)
522 where
523 E: TElement,
524 {
525 if self.style.get_box().clone_display() != Display::Contents {
526 return;
527 }
528
529 if self.style.pseudo.is_some_and(|p| !p.is_element_backed()) {
531 self.style.mutate_box().set_display(Display::Inline);
532 return;
533 }
534
535 let element = match element {
536 Some(e) => e,
537 None => return,
538 };
539
540 if is_effective_display_none_for_display_contents(element) {
541 self.style.mutate_box().set_display(Display::None);
542 }
543 }
544
545 #[cfg(feature = "gecko")]
548 fn adjust_for_text_control_editing_root(&mut self) {
549 use crate::properties::longhands::white_space_collapse::computed_value::T as WhiteSpaceCollapse;
550 use crate::selector_parser::PseudoElement;
551
552 if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {
553 return;
554 }
555
556 let old_collapse = self.style.get_inherited_text().clone_white_space_collapse();
557 let new_collapse = match old_collapse {
558 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => old_collapse,
559 WhiteSpaceCollapse::Collapse
560 | WhiteSpaceCollapse::PreserveSpaces
561 | WhiteSpaceCollapse::PreserveBreaks => WhiteSpaceCollapse::Preserve,
562 };
563 if new_collapse != old_collapse {
564 self.style
565 .mutate_inherited_text()
566 .set_white_space_collapse(new_collapse);
567 }
568
569 let box_style = self.style.get_box();
570 let overflow_x = box_style.clone_overflow_x();
571 let overflow_y = box_style.clone_overflow_y();
572
573 if overflow_x.is_scrollable() || overflow_y.is_scrollable() {
576 return;
577 }
578
579 let box_style = self.style.mutate_box();
580 box_style.set_overflow_x(Overflow::Auto);
581 box_style.set_overflow_y(Overflow::Auto);
582 }
583
584 #[cfg(feature = "gecko")]
587 fn adjust_for_fieldset_content(&mut self) {
588 use crate::selector_parser::PseudoElement;
589 if self.style.pseudo != Some(&PseudoElement::MozFieldsetContent) {
590 return;
591 }
592 let parent_display = self.style.get_parent_box().clone_display();
593 debug_assert!(
594 !parent_display.is_contents(),
595 "How did we create a fieldset-content box with display: contents?"
596 );
597 let new_display = match parent_display {
598 Display::Flex | Display::InlineFlex => Some(Display::Flex),
599 Display::Grid | Display::InlineGrid => Some(Display::Grid),
600 _ => None,
601 };
602 if let Some(new_display) = new_display {
603 self.style.mutate_box().set_display(new_display);
604 }
605 }
606
607 fn adjust_for_table_text_align(&mut self) {
614 use crate::properties::longhands::text_align::computed_value::T as TextAlign;
615 if self.style.get_box().clone_display() != Display::Table {
616 return;
617 }
618
619 match self.style.get_inherited_text().clone_text_align() {
620 TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},
621 _ => return,
622 }
623
624 self.style
625 .mutate_inherited_text()
626 .set_text_align(TextAlign::Start)
627 }
628
629 #[cfg(feature = "gecko")]
630 fn should_suppress_linebreak<E>(&self, element: Option<E>) -> bool
631 where
632 E: TElement,
633 {
634 if self.style.is_floating() || self.style.is_absolutely_positioned() {
636 return false;
637 }
638 let parent_display = self.style.get_parent_box().clone_display();
639 if self
640 .style
641 .get_parent_flags()
642 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
643 {
644 if parent_display.is_line_participant() || parent_display.is_contents() {
647 return true;
648 }
649 }
650 match self.style.get_box().clone_display() {
651 Display::RubyBase | Display::RubyText => true,
653 Display::RubyBaseContainer | Display::RubyTextContainer
662 if element.map_or(true, |e| e.is_html_element()) =>
663 {
664 false
665 },
666 _ => parent_display.is_ruby_type(),
670 }
671 }
672
673 #[cfg(feature = "gecko")]
679 fn adjust_for_ruby<E>(&mut self, element: Option<E>)
680 where
681 E: TElement,
682 {
683 use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
684
685 let self_display = self.style.get_box().clone_display();
686 if self.should_suppress_linebreak(element) {
688 self.style
689 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
690 if !self.skip_item_display_fixup(element) {
692 let inline_display = self_display.inlinify();
693 if self_display != inline_display {
694 self.style
695 .mutate_box()
696 .set_adjusted_display(inline_display, false);
697 }
698 }
699 }
700 if self_display.is_ruby_level_container() {
705 self.style.reset_border_struct();
706 self.style.reset_padding_struct();
707 }
708
709 if self_display.is_ruby_type() {
712 let new_value = match self.style.get_text().clone_unicode_bidi() {
713 UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
714 UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
715 _ => None,
716 };
717 if let Some(new_value) = new_value {
718 self.style.mutate_text().set_unicode_bidi(new_value);
719 }
720 }
721 }
722
723 fn adjust_for_visited<E>(&mut self, element: Option<E>)
733 where
734 E: TElement,
735 {
736 if !self.style.has_visited_style() {
737 return;
738 }
739
740 let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());
741
742 if !is_link_element {
743 return;
744 }
745
746 if element.unwrap().is_visited_link() {
747 self.style
748 .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
749 } else {
750 self.style
752 .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
753 }
754 }
755
756 #[cfg(feature = "gecko")]
761 fn adjust_for_justify_items(&mut self) {
762 use crate::values::specified::align;
763 let justify_items = self.style.get_position().clone_justify_items();
764 if justify_items.specified != align::JustifyItems::legacy() {
765 return;
766 }
767
768 let parent_justify_items = self.style.get_parent_position().clone_justify_items();
769
770 if !parent_justify_items.computed.contains(AlignFlags::LEGACY) {
771 return;
772 }
773
774 if parent_justify_items.computed == justify_items.computed {
775 return;
776 }
777
778 self.style
779 .mutate_position()
780 .set_computed_justify_items(parent_justify_items.computed);
781 }
782
783 #[cfg(feature = "gecko")]
788 fn adjust_for_appearance<E>(&mut self, element: Option<E>)
789 where
790 E: TElement,
791 {
792 use crate::properties::longhands::appearance::computed_value::T as Appearance;
793 use crate::properties::longhands::line_height::computed_value::T as LineHeight;
794
795 let box_ = self.style.get_box();
796 let appearance = match box_.clone_appearance() {
797 Appearance::Auto => box_.clone__moz_default_appearance(),
798 a => a,
799 };
800
801 if appearance == Appearance::Menulist {
802 if self.style.get_font().clone_line_height() == LineHeight::normal() {
803 return;
804 }
805 if self.style.pseudo.is_some() {
806 return;
807 }
808 let is_html_select_element = element.map_or(false, |e| {
809 e.is_html_element() && e.local_name() == &*atom!("select")
810 });
811 if !is_html_select_element {
812 return;
813 }
814 self.style
815 .mutate_font()
816 .set_line_height(LineHeight::normal());
817 }
818 }
819
820 #[cfg(feature = "gecko")]
831 fn adjust_for_marker_pseudo(&mut self) {
832 use crate::values::computed::counters::Content;
833 use crate::values::computed::font::{FontFamily, FontSynthesis, FontSynthesisStyle};
834 use crate::values::computed::text::{LetterSpacing, WordSpacing};
835
836 let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker())
837 && self.style.get_list().clone_list_style_type().is_bullet()
838 && self.style.get_counters().clone_content() == Content::Normal;
839 if !is_legacy_marker {
840 return;
841 }
842 let flags = self.style.flags.get();
843 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) {
844 self.style
845 .mutate_font()
846 .set_font_family(FontFamily::moz_bullet().clone());
847
848 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT) {
852 self.style
853 .mutate_font()
854 .set_font_synthesis_weight(FontSynthesis::None);
855 }
856 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE) {
857 self.style
858 .mutate_font()
859 .set_font_synthesis_style(FontSynthesisStyle::None);
860 }
861 }
862 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) {
863 self.style
864 .mutate_inherited_text()
865 .set_letter_spacing(LetterSpacing::normal());
866 }
867 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) {
868 self.style
869 .mutate_inherited_text()
870 .set_word_spacing(WordSpacing::normal());
871 }
872 }
873
874 fn adjust_for_try_tactic(&mut self, tactic: &PositionTryFallbacksTryTactic) {
882 debug_assert!(!tactic.is_empty());
883 let wm = self.style.writing_mode;
885 for tactic in tactic.iter() {
887 use PositionTryFallbacksTryTacticKeyword::*;
888 match tactic {
889 FlipBlock => {
890 self.flip_self_alignment(true);
891 self.flip_insets_and_margins(wm.is_vertical());
892 },
893 FlipInline => {
894 self.flip_self_alignment(false);
895 self.flip_insets_and_margins(wm.is_horizontal());
896 },
897 FlipX => {
898 self.flip_self_alignment(wm.is_vertical());
899 self.flip_insets_and_margins(true);
900 },
901 FlipY => {
902 self.flip_self_alignment(wm.is_horizontal());
903 self.flip_insets_and_margins(false);
904 },
905 FlipStart => {
906 self.flip_start();
907 },
908 }
909 self.apply_position_area_tactic(*tactic);
910 }
911 }
912
913 fn apply_position_area_tactic(&mut self, tactic: PositionTryFallbacksTryTacticKeyword) {
914 let pos = self.style.get_position();
915 let old = pos.clone_position_area();
916 let wm = self.style.writing_mode;
917 let new = old.with_tactic(wm, tactic);
918 if new == old {
919 return;
920 }
921 let pos = self.style.mutate_position();
922 pos.set_position_area(new);
923 }
924
925 fn swap_insets(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
927 debug_assert_ne!(a_side, b_side);
928 let pos = self.style.mutate_position();
929 let mut a = pos.get_inset(a_side).clone();
930 a.try_tactic_adjustment(a_side, b_side);
931 let mut b = pos.get_inset(b_side).clone();
932 b.try_tactic_adjustment(b_side, a_side);
933 pos.set_inset(a_side, b);
934 pos.set_inset(b_side, a);
935 }
936
937 fn swap_margins(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
938 debug_assert_ne!(a_side, b_side);
939 let margin = self.style.get_margin();
940 let mut a = margin.get_margin(a_side).clone();
941 a.try_tactic_adjustment(a_side, b_side);
942 let mut b = margin.get_margin(b_side).clone();
943 b.try_tactic_adjustment(b_side, a_side);
944 let margin = self.style.mutate_margin();
945 margin.set_margin(a_side, b);
946 margin.set_margin(b_side, a);
947 }
948
949 fn swap_sizes(&mut self, block_start: PhysicalSide, inline_start: PhysicalSide) {
950 let pos = self.style.mutate_position();
951 let mut min_width = pos.clone_min_width();
952 min_width.try_tactic_adjustment(inline_start, block_start);
953 let mut max_width = pos.clone_max_width();
954 max_width.try_tactic_adjustment(inline_start, block_start);
955 let mut width = pos.clone_width();
956 width.try_tactic_adjustment(inline_start, block_start);
957
958 let mut min_height = pos.clone_min_height();
959 min_height.try_tactic_adjustment(block_start, inline_start);
960 let mut max_height = pos.clone_max_height();
961 max_height.try_tactic_adjustment(block_start, inline_start);
962 let mut height = pos.clone_height();
963 height.try_tactic_adjustment(block_start, inline_start);
964
965 let pos = self.style.mutate_position();
966 pos.set_width(height);
967 pos.set_height(width);
968 pos.set_max_width(max_height);
969 pos.set_max_height(max_width);
970 pos.set_min_width(min_height);
971 pos.set_min_height(min_width);
972 }
973
974 fn flip_start(&mut self) {
975 let wm = self.style.writing_mode;
976 let bs = wm.block_start_physical_side();
977 let is = wm.inline_start_physical_side();
978 let be = wm.block_end_physical_side();
979 let ie = wm.inline_end_physical_side();
980 self.swap_sizes(bs, is);
981 self.swap_insets(bs, is);
982 self.swap_insets(ie, be);
983 self.swap_margins(bs, is);
984 self.swap_margins(ie, be);
985 self.flip_alignment_start();
986 }
987
988 fn flip_insets_and_margins(&mut self, horizontal: bool) {
989 if horizontal {
990 self.swap_insets(PhysicalSide::Left, PhysicalSide::Right);
991 self.swap_margins(PhysicalSide::Left, PhysicalSide::Right);
992 } else {
993 self.swap_insets(PhysicalSide::Top, PhysicalSide::Bottom);
994 self.swap_margins(PhysicalSide::Top, PhysicalSide::Bottom);
995 }
996 }
997
998 fn flip_alignment_start(&mut self) {
999 let pos = self.style.get_position();
1000 let align = pos.clone_align_self();
1001 let mut justify = pos.clone_justify_self();
1002 if align == justify {
1003 return;
1004 }
1005
1006 if matches!(justify.value(), AlignFlags::LEFT | AlignFlags::RIGHT) {
1009 let left = justify.value() == AlignFlags::LEFT;
1010 let ltr = self.style.writing_mode.is_bidi_ltr();
1011 justify = justify.with_value(if left == ltr {
1012 AlignFlags::SELF_START
1013 } else {
1014 AlignFlags::SELF_END
1015 });
1016 }
1017
1018 let pos = self.style.mutate_position();
1019 pos.set_align_self(justify);
1020 pos.set_justify_self(align);
1021 }
1022
1023 fn flip_self_alignment(&mut self, block: bool) {
1024 let pos = self.style.get_position();
1025 let cur = if block {
1026 pos.clone_align_self()
1027 } else {
1028 pos.clone_justify_self()
1029 };
1030 let flipped = cur.flip_position();
1031 if flipped == cur {
1032 return;
1033 }
1034 let pos = self.style.mutate_position();
1035 if block {
1036 pos.set_align_self(flipped);
1037 } else {
1038 pos.set_justify_self(flipped);
1039 }
1040 }
1041
1042 pub fn adjust<E>(
1044 &mut self,
1045 layout_parent_style: &ComputedValues,
1046 element: Option<E>,
1047 try_tactic: &PositionTryFallbacksTryTactic,
1048 ) where
1049 E: TElement,
1050 {
1051 if cfg!(debug_assertions) {
1052 if let Some(e) = element {
1053 if let Some(p) = e.implemented_pseudo_element() {
1054 debug_assert!(
1058 self.style.pseudo.is_some(),
1059 "Someone really messed up (no pseudo style for {e:?}, {p:?})"
1060 );
1061 }
1062 }
1063 }
1064 self.adjust_for_visited(element);
1074 #[cfg(feature = "gecko")]
1075 {
1076 self.adjust_for_prohibited_display_contents(element);
1077 self.adjust_for_fieldset_content();
1078 self.adjust_for_text_control_editing_root();
1081 }
1082 self.adjust_for_top_layer();
1083 self.blockify_if_necessary(layout_parent_style, element);
1084 #[cfg(feature = "gecko")]
1085 self.adjust_for_webkit_line_clamp();
1086 self.adjust_for_position();
1087 self.adjust_for_overflow();
1088 #[cfg(feature = "gecko")]
1089 {
1090 self.adjust_for_contain();
1091 self.adjust_for_contain_intrinsic_size();
1092 self.adjust_for_justify_items();
1093 }
1094 self.adjust_for_table_text_align();
1095 self.adjust_for_writing_mode(layout_parent_style);
1096 #[cfg(feature = "gecko")]
1097 {
1098 self.adjust_for_ruby(element);
1099 self.adjust_for_appearance(element);
1100 self.adjust_for_marker_pseudo();
1101 }
1102 if !try_tactic.is_empty() {
1103 self.adjust_for_try_tactic(try_tactic);
1104 }
1105 self.set_bits();
1106 }
1107}