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
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 } else if self
280 .style
281 .get_parent_box()
282 .clone_display()
283 .is_item_container()
284 || self
285 .style
286 .get_parent_flags()
287 .contains(ComputedValueFlags::DIPLAY_CONTENTS_IN_ITEM_CONTAINER)
288 {
289 self.style
290 .add_flags(ComputedValueFlags::DIPLAY_CONTENTS_IN_ITEM_CONTAINER);
291 }
292
293 if self.style.pseudo.is_some_and(|p| p.is_first_line()) {
294 self.style
295 .add_flags(ComputedValueFlags::IS_IN_FIRST_LINE_SUBTREE);
296 }
297
298 if self.style.is_root_element {
299 self.style
300 .add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
301 }
302
303 #[cfg(feature = "gecko")]
304 if box_style
305 .clone_effective_containment()
306 .contains(Contain::STYLE)
307 {
308 self.style
309 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
310 }
311
312 if box_style.clone_container_type().is_size_container_type() {
313 self.style
314 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE);
315 }
316
317 #[cfg(feature = "servo")]
318 if self.style.get_parent_column().is_multicol() {
319 self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
320 }
321 }
322
323 #[cfg(feature = "gecko")]
330 pub fn adjust_for_text(&mut self) {
331 debug_assert!(!self.style.is_root_element);
332 self.adjust_for_text_combine_upright();
333 self.adjust_for_text_in_ruby();
334 self.set_bits();
335 }
336
337 #[cfg(feature = "gecko")]
348 fn adjust_for_text_combine_upright(&mut self) {
349 use crate::computed_values::text_combine_upright::T as TextCombineUpright;
350 use crate::computed_values::writing_mode::T as WritingMode;
351 use crate::logical_geometry;
352
353 let writing_mode = self.style.get_inherited_box().clone_writing_mode();
354 let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
355
356 if matches!(
357 writing_mode,
358 WritingMode::VerticalRl | WritingMode::VerticalLr
359 ) && text_combine_upright == TextCombineUpright::All
360 {
361 self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
362 self.style
363 .mutate_inherited_box()
364 .set_writing_mode(WritingMode::HorizontalTb);
365 self.style.writing_mode =
366 logical_geometry::WritingMode::new(self.style.get_inherited_box());
367 }
368 }
369
370 #[cfg(feature = "gecko")]
377 fn adjust_for_text_in_ruby(&mut self) {
378 let parent_display = self.style.get_parent_box().clone_display();
379 if parent_display.is_ruby_type()
380 || self
381 .style
382 .get_parent_flags()
383 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
384 {
385 self.style
386 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
387 }
388 }
389
390 fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {
404 let our_writing_mode = self.style.get_inherited_box().clone_writing_mode();
405 let parent_writing_mode = layout_parent_style.get_inherited_box().clone_writing_mode();
406
407 if our_writing_mode != parent_writing_mode
408 && self.style.get_box().clone_display() == Display::Inline
409 {
410 if cfg!(feature = "servo") {
413 self.style
414 .mutate_box()
415 .set_adjusted_display(Display::InlineBlock, false);
416 } else {
417 self.style.mutate_box().set_display(Display::InlineBlock);
418 }
419 }
420 }
421
422 fn adjust_for_overflow(&mut self) {
428 let overflow_x = self.style.get_box().clone_overflow_x();
429 let overflow_y = self.style.get_box().clone_overflow_y();
430 if overflow_x == overflow_y {
431 return; }
433
434 if overflow_x.is_scrollable() != overflow_y.is_scrollable() {
435 let box_style = self.style.mutate_box();
436 box_style.set_overflow_x(overflow_x.to_scrollable());
437 box_style.set_overflow_y(overflow_y.to_scrollable());
438 }
439 }
440
441 #[cfg(feature = "gecko")]
442 fn adjust_for_contain(&mut self) {
443 let box_style = self.style.get_box();
444 let container_type = box_style.clone_container_type();
445 let content_visibility = box_style.clone_content_visibility();
446 if !container_type.is_size_container_type()
447 && content_visibility == ContentVisibility::Visible
448 {
449 debug_assert_eq!(
450 box_style.clone_contain(),
451 box_style.clone_effective_containment()
452 );
453 return;
454 }
455 let old_contain = box_style.clone_contain();
456 let mut new_contain = old_contain;
457 match content_visibility {
458 ContentVisibility::Visible => {},
459 ContentVisibility::Auto => {
463 new_contain.insert(Contain::LAYOUT | Contain::PAINT | Contain::STYLE)
464 },
465 ContentVisibility::Hidden => new_contain
466 .insert(Contain::LAYOUT | Contain::PAINT | Contain::SIZE | Contain::STYLE),
467 }
468 if container_type.intersects(ContainerType::INLINE_SIZE) {
469 new_contain.insert(Contain::STYLE | Contain::INLINE_SIZE);
473 } else if container_type.intersects(ContainerType::SIZE) {
474 new_contain.insert(Contain::STYLE | Contain::SIZE);
478 }
479 if new_contain == old_contain {
480 debug_assert_eq!(
481 box_style.clone_contain(),
482 box_style.clone_effective_containment()
483 );
484 return;
485 }
486 self.style
487 .mutate_box()
488 .set_effective_containment(new_contain);
489 }
490
491 #[cfg(feature = "gecko")]
496 fn adjust_for_contain_intrinsic_size(&mut self) {
497 let content_visibility = self.style.get_box().clone_content_visibility();
498 if content_visibility != ContentVisibility::Auto {
499 return;
500 }
501
502 let pos = self.style.get_position();
503 let new_width = pos.clone_contain_intrinsic_width().add_auto_if_needed();
504 let new_height = pos.clone_contain_intrinsic_height().add_auto_if_needed();
505 if new_width.is_none() && new_height.is_none() {
506 return;
507 }
508
509 let pos = self.style.mutate_position();
510 if let Some(width) = new_width {
511 pos.set_contain_intrinsic_width(width);
512 }
513 if let Some(height) = new_height {
514 pos.set_contain_intrinsic_height(height);
515 }
516 }
517
518 #[cfg(feature = "gecko")]
524 fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)
525 where
526 E: TElement,
527 {
528 if self.style.get_box().clone_display() != Display::Contents {
529 return;
530 }
531
532 if self.style.pseudo.is_some_and(|p| !p.is_element_backed()) {
534 self.style.mutate_box().set_display(Display::Inline);
535 return;
536 }
537
538 let element = match element {
539 Some(e) => e,
540 None => return,
541 };
542
543 if is_effective_display_none_for_display_contents(element) {
544 self.style.mutate_box().set_display(Display::None);
545 }
546 }
547
548 #[cfg(feature = "gecko")]
551 fn adjust_for_text_control_editing_root(&mut self) {
552 use crate::properties::longhands::white_space_collapse::computed_value::T as WhiteSpaceCollapse;
553 use crate::selector_parser::PseudoElement;
554
555 if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {
556 return;
557 }
558
559 let old_collapse = self.style.get_inherited_text().clone_white_space_collapse();
560 let new_collapse = match old_collapse {
561 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => old_collapse,
562 WhiteSpaceCollapse::Collapse
563 | WhiteSpaceCollapse::PreserveSpaces
564 | WhiteSpaceCollapse::PreserveBreaks => WhiteSpaceCollapse::Preserve,
565 };
566 if new_collapse != old_collapse {
567 self.style
568 .mutate_inherited_text()
569 .set_white_space_collapse(new_collapse);
570 }
571
572 let box_style = self.style.get_box();
573 let overflow_x = box_style.clone_overflow_x();
574 let overflow_y = box_style.clone_overflow_y();
575
576 if overflow_x.is_scrollable() || overflow_y.is_scrollable() {
579 return;
580 }
581
582 let box_style = self.style.mutate_box();
583 box_style.set_overflow_x(Overflow::Auto);
584 box_style.set_overflow_y(Overflow::Auto);
585 }
586
587 #[cfg(feature = "gecko")]
590 fn adjust_for_fieldset_content(&mut self) {
591 use crate::selector_parser::PseudoElement;
592 if self.style.pseudo != Some(&PseudoElement::FieldsetContent) {
593 return;
594 }
595 let parent_display = self.style.get_parent_box().clone_display();
596 debug_assert!(
597 !parent_display.is_contents(),
598 "How did we create a fieldset-content box with display: contents?"
599 );
600 let new_display = match parent_display {
601 Display::Flex | Display::InlineFlex => Some(Display::Flex),
602 Display::Grid | Display::InlineGrid => Some(Display::Grid),
603 _ => None,
604 };
605 if let Some(new_display) = new_display {
606 self.style.mutate_box().set_display(new_display);
607 }
608 }
609
610 fn adjust_for_table_text_align(&mut self) {
617 use crate::properties::longhands::text_align::computed_value::T as TextAlign;
618 if self.style.get_box().clone_display() != Display::Table {
619 return;
620 }
621
622 match self.style.get_inherited_text().clone_text_align() {
623 TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},
624 _ => return,
625 }
626
627 self.style
628 .mutate_inherited_text()
629 .set_text_align(TextAlign::Start)
630 }
631
632 #[cfg(feature = "gecko")]
633 fn should_suppress_linebreak<E>(&self, element: Option<E>) -> bool
634 where
635 E: TElement,
636 {
637 if self.style.is_floating() || self.style.is_absolutely_positioned() {
639 return false;
640 }
641 let parent_display = self.style.get_parent_box().clone_display();
642 if self
643 .style
644 .get_parent_flags()
645 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
646 {
647 if parent_display.is_line_participant() || parent_display.is_contents() {
650 return true;
651 }
652 }
653 match self.style.get_box().clone_display() {
654 Display::RubyBase | Display::RubyText => true,
656 Display::RubyBaseContainer | Display::RubyTextContainer
665 if element.map_or(true, |e| e.is_html_element()) =>
666 {
667 false
668 },
669 _ => parent_display.is_ruby_type(),
673 }
674 }
675
676 #[cfg(feature = "gecko")]
682 fn adjust_for_ruby<E>(&mut self, element: Option<E>)
683 where
684 E: TElement,
685 {
686 use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
687
688 let self_display = self.style.get_box().clone_display();
689 if self.should_suppress_linebreak(element) {
691 self.style
692 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
693 if !self.skip_item_display_fixup(element) {
695 let inline_display = self_display.inlinify();
696 if self_display != inline_display {
697 self.style
698 .mutate_box()
699 .set_adjusted_display(inline_display, false);
700 }
701 }
702 }
703 if self_display.is_ruby_level_container() {
708 self.style.reset_border_struct();
709 self.style.reset_padding_struct();
710 }
711
712 if self_display.is_ruby_type() {
715 let new_value = match self.style.get_text().clone_unicode_bidi() {
716 UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
717 UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
718 _ => None,
719 };
720 if let Some(new_value) = new_value {
721 self.style.mutate_text().set_unicode_bidi(new_value);
722 }
723 }
724 }
725
726 fn adjust_for_visited<E>(&mut self, element: Option<E>)
736 where
737 E: TElement,
738 {
739 if !self.style.has_visited_style() {
740 return;
741 }
742
743 let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());
744
745 if !is_link_element {
746 return;
747 }
748
749 if element.unwrap().is_visited_link() {
750 self.style
751 .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
752 } else {
753 self.style
755 .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
756 }
757 }
758
759 #[cfg(feature = "gecko")]
764 fn adjust_for_justify_items(&mut self) {
765 use crate::values::specified::align;
766 let justify_items = self.style.get_position().clone_justify_items();
767 if justify_items.specified != align::JustifyItems::legacy() {
768 return;
769 }
770
771 let parent_justify_items = self.style.get_parent_position().clone_justify_items();
772
773 if !parent_justify_items.computed.contains(AlignFlags::LEGACY) {
774 return;
775 }
776
777 if parent_justify_items.computed == justify_items.computed {
778 return;
779 }
780
781 self.style
782 .mutate_position()
783 .set_computed_justify_items(parent_justify_items.computed);
784 }
785
786 #[cfg(feature = "gecko")]
791 fn adjust_for_appearance<E>(&mut self, element: Option<E>)
792 where
793 E: TElement,
794 {
795 use crate::properties::longhands::appearance::computed_value::T as Appearance;
796 use crate::properties::longhands::line_height::computed_value::T as LineHeight;
797
798 let box_ = self.style.get_box();
799 let appearance = match box_.clone_appearance() {
800 Appearance::Auto => box_.clone__moz_default_appearance(),
801 a => a,
802 };
803
804 if appearance == Appearance::Menulist {
805 if self.style.get_font().clone_line_height() == LineHeight::normal() {
806 return;
807 }
808 if self.style.pseudo.is_some() {
809 return;
810 }
811 let is_html_select_element = element.map_or(false, |e| {
812 e.is_html_element() && e.local_name() == &*atom!("select")
813 });
814 if !is_html_select_element {
815 return;
816 }
817 self.style
818 .mutate_font()
819 .set_line_height(LineHeight::normal());
820 }
821 }
822
823 #[cfg(feature = "gecko")]
834 fn adjust_for_marker_pseudo(&mut self) {
835 use crate::values::computed::counters::Content;
836 use crate::values::computed::font::{FontFamily, FontSynthesis, FontSynthesisStyle};
837 use crate::values::computed::text::{LetterSpacing, WordSpacing};
838
839 let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker())
840 && self.style.get_list().clone_list_style_type().is_bullet()
841 && self.style.get_counters().clone_content() == Content::Normal;
842 if !is_legacy_marker {
843 return;
844 }
845 let flags = self.style.flags.get();
846 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) {
847 self.style
848 .mutate_font()
849 .set_font_family(FontFamily::moz_bullet().clone());
850
851 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT) {
855 self.style
856 .mutate_font()
857 .set_font_synthesis_weight(FontSynthesis::None);
858 }
859 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE) {
860 self.style
861 .mutate_font()
862 .set_font_synthesis_style(FontSynthesisStyle::None);
863 }
864 }
865 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) {
866 self.style
867 .mutate_inherited_text()
868 .set_letter_spacing(LetterSpacing::normal());
869 }
870 if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) {
871 self.style
872 .mutate_inherited_text()
873 .set_word_spacing(WordSpacing::normal());
874 }
875 }
876
877 fn adjust_for_try_tactic(&mut self, tactic: &PositionTryFallbacksTryTactic) {
885 debug_assert!(!tactic.is_empty());
886 let wm = self.style.writing_mode;
888 for tactic in tactic.iter() {
890 use PositionTryFallbacksTryTacticKeyword::*;
891 match tactic {
892 FlipBlock => {
893 self.flip_self_alignment(true);
894 self.flip_insets_and_margins(wm.is_vertical());
895 },
896 FlipInline => {
897 self.flip_self_alignment(false);
898 self.flip_insets_and_margins(wm.is_horizontal());
899 },
900 FlipX => {
901 self.flip_self_alignment(wm.is_vertical());
902 self.flip_insets_and_margins(true);
903 },
904 FlipY => {
905 self.flip_self_alignment(wm.is_horizontal());
906 self.flip_insets_and_margins(false);
907 },
908 FlipStart => {
909 self.flip_start();
910 },
911 }
912 self.apply_position_area_tactic(*tactic);
913 }
914 }
915
916 fn apply_position_area_tactic(&mut self, tactic: PositionTryFallbacksTryTacticKeyword) {
917 let pos = self.style.get_position();
918 let old = pos.clone_position_area();
919 let wm = self.style.writing_mode;
920 let new = old.with_tactic(wm, tactic);
921 if new == old {
922 return;
923 }
924 let pos = self.style.mutate_position();
925 pos.set_position_area(new);
926 }
927
928 fn swap_insets(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
930 debug_assert_ne!(a_side, b_side);
931 let pos = self.style.mutate_position();
932 let mut a = pos.get_inset(a_side).clone();
933 a.try_tactic_adjustment(a_side, b_side);
934 let mut b = pos.get_inset(b_side).clone();
935 b.try_tactic_adjustment(b_side, a_side);
936 pos.set_inset(a_side, b);
937 pos.set_inset(b_side, a);
938 }
939
940 fn swap_margins(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
941 debug_assert_ne!(a_side, b_side);
942 let margin = self.style.get_margin();
943 let mut a = margin.get_margin(a_side).clone();
944 a.try_tactic_adjustment(a_side, b_side);
945 let mut b = margin.get_margin(b_side).clone();
946 b.try_tactic_adjustment(b_side, a_side);
947 let margin = self.style.mutate_margin();
948 margin.set_margin(a_side, b);
949 margin.set_margin(b_side, a);
950 }
951
952 fn swap_sizes(&mut self, block_start: PhysicalSide, inline_start: PhysicalSide) {
953 let pos = self.style.mutate_position();
954 let mut min_width = pos.clone_min_width();
955 min_width.try_tactic_adjustment(inline_start, block_start);
956 let mut max_width = pos.clone_max_width();
957 max_width.try_tactic_adjustment(inline_start, block_start);
958 let mut width = pos.clone_width();
959 width.try_tactic_adjustment(inline_start, block_start);
960
961 let mut min_height = pos.clone_min_height();
962 min_height.try_tactic_adjustment(block_start, inline_start);
963 let mut max_height = pos.clone_max_height();
964 max_height.try_tactic_adjustment(block_start, inline_start);
965 let mut height = pos.clone_height();
966 height.try_tactic_adjustment(block_start, inline_start);
967
968 let pos = self.style.mutate_position();
969 pos.set_width(height);
970 pos.set_height(width);
971 pos.set_max_width(max_height);
972 pos.set_max_height(max_width);
973 pos.set_min_width(min_height);
974 pos.set_min_height(min_width);
975 }
976
977 fn flip_start(&mut self) {
978 let wm = self.style.writing_mode;
979 let bs = wm.block_start_physical_side();
980 let is = wm.inline_start_physical_side();
981 let be = wm.block_end_physical_side();
982 let ie = wm.inline_end_physical_side();
983 self.swap_sizes(bs, is);
984 self.swap_insets(bs, is);
985 self.swap_insets(ie, be);
986 self.swap_margins(bs, is);
987 self.swap_margins(ie, be);
988 self.flip_alignment_start();
989 }
990
991 fn flip_insets_and_margins(&mut self, horizontal: bool) {
992 if horizontal {
993 self.swap_insets(PhysicalSide::Left, PhysicalSide::Right);
994 self.swap_margins(PhysicalSide::Left, PhysicalSide::Right);
995 } else {
996 self.swap_insets(PhysicalSide::Top, PhysicalSide::Bottom);
997 self.swap_margins(PhysicalSide::Top, PhysicalSide::Bottom);
998 }
999 }
1000
1001 fn flip_alignment_start(&mut self) {
1002 let pos = self.style.get_position();
1003 let align = pos.clone_align_self();
1004 let mut justify = pos.clone_justify_self();
1005 if align == justify {
1006 return;
1007 }
1008
1009 if matches!(justify.value(), AlignFlags::LEFT | AlignFlags::RIGHT) {
1012 let left = justify.value() == AlignFlags::LEFT;
1013 let ltr = self.style.writing_mode.is_bidi_ltr();
1014 justify = justify.with_value(if left == ltr {
1015 AlignFlags::SELF_START
1016 } else {
1017 AlignFlags::SELF_END
1018 });
1019 }
1020
1021 let pos = self.style.mutate_position();
1022 pos.set_align_self(justify);
1023 pos.set_justify_self(align);
1024 }
1025
1026 fn flip_self_alignment(&mut self, block: bool) {
1027 let pos = self.style.get_position();
1028 let cur = if block {
1029 pos.clone_align_self()
1030 } else {
1031 pos.clone_justify_self()
1032 };
1033 let flipped = cur.flip_position();
1034 if flipped == cur {
1035 return;
1036 }
1037 let pos = self.style.mutate_position();
1038 if block {
1039 pos.set_align_self(flipped);
1040 } else {
1041 pos.set_justify_self(flipped);
1042 }
1043 }
1044
1045 pub fn adjust<E>(
1047 &mut self,
1048 layout_parent_style: &ComputedValues,
1049 element: Option<E>,
1050 try_tactic: &PositionTryFallbacksTryTactic,
1051 ) where
1052 E: TElement,
1053 {
1054 if cfg!(debug_assertions) {
1055 if let Some(e) = element {
1056 if let Some(p) = e.implemented_pseudo_element() {
1057 debug_assert!(
1061 self.style.pseudo.is_some(),
1062 "Someone really messed up (no pseudo style for {e:?}, {p:?})"
1063 );
1064 }
1065 }
1066 }
1067 self.adjust_for_visited(element);
1077 #[cfg(feature = "gecko")]
1078 {
1079 self.adjust_for_prohibited_display_contents(element);
1080 self.adjust_for_fieldset_content();
1081 self.adjust_for_text_control_editing_root();
1084 }
1085 self.adjust_for_top_layer();
1086 self.blockify_if_necessary(layout_parent_style, element);
1087 #[cfg(feature = "gecko")]
1088 self.adjust_for_webkit_line_clamp();
1089 self.adjust_for_position();
1090 self.adjust_for_overflow();
1091 #[cfg(feature = "gecko")]
1092 {
1093 self.adjust_for_contain();
1094 self.adjust_for_contain_intrinsic_size();
1095 self.adjust_for_justify_items();
1096 }
1097 self.adjust_for_table_text_align();
1098 self.adjust_for_writing_mode(layout_parent_style);
1099 #[cfg(feature = "gecko")]
1100 {
1101 self.adjust_for_ruby(element);
1102 self.adjust_for_appearance(element);
1103 self.adjust_for_marker_pseudo();
1104 }
1105 if !try_tactic.is_empty() {
1106 self.adjust_for_try_tactic(try_tactic);
1107 }
1108 self.set_bits();
1109 }
1110}