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};
19#[cfg(feature = "gecko")]
20use crate::properties::LonghandId;
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
44#[cfg(feature = "gecko")]
45fn is_topmost_svg_svg_element<E>(e: E) -> bool
46where
47 E: TElement,
48{
49 debug_assert!(e.is_svg_element());
50 if e.local_name() != &*atom!("svg") {
51 return false;
52 }
53
54 let parent = match e.traversal_parent() {
55 Some(n) => n,
56 None => return true,
57 };
58
59 if !parent.is_svg_element() {
60 return true;
61 }
62
63 parent.local_name() == &*atom!("foreignObject")
64}
65
66#[cfg(feature = "gecko")]
68fn is_effective_display_none_for_display_contents<E>(element: E) -> bool
69where
70 E: TElement,
71{
72 use crate::Atom;
73
74 const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
75 atom!("br"),
76 atom!("wbr"),
77 atom!("meter"),
78 atom!("progress"),
79 atom!("canvas"),
80 atom!("embed"),
81 atom!("object"),
82 atom!("audio"),
83 atom!("iframe"),
84 atom!("img"),
85 atom!("video"),
86 atom!("frame"),
87 atom!("frameset"),
88 atom!("input"),
89 atom!("textarea"),
90 atom!("select"),
91 ];
92
93 const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
99 atom!("svg"),
100 atom!("a"),
101 atom!("g"),
102 atom!("use"),
103 atom!("tspan"),
104 atom!("textPath"),
105 ];
106
107 if element.is_html_element() {
109 let local_name = element.local_name();
110 return SPECIAL_HTML_ELEMENTS
111 .iter()
112 .any(|name| &**name == local_name);
113 }
114
115 if element.is_svg_element() {
117 if is_topmost_svg_svg_element(element) {
118 return true;
119 }
120 let local_name = element.local_name();
121 return !SPECIAL_SVG_ELEMENTS
122 .iter()
123 .any(|name| &**name == local_name);
124 }
125
126 if element.is_mathml_element() {
128 return true;
129 }
130
131 false
132}
133
134impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
135 #[inline]
137 pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
138 StyleAdjuster { style }
139 }
140
141 fn adjust_for_top_layer(&mut self) {
147 if !self.style.in_top_layer() {
148 return;
149 }
150 if !self.style.is_absolutely_positioned() {
151 self.style.mutate_box().set_position(Position::Absolute);
152 }
153 if self.style.get_box().clone_display().is_contents() {
154 self.style.mutate_box().set_display(Display::Block);
155 }
156 }
157
158 #[cfg(feature = "gecko")]
165 fn adjust_for_webkit_line_clamp(&mut self) {
166 use crate::properties::longhands::_moz_box_orient::computed_value::T as BoxOrient;
167 use crate::values::specified::box_::{DisplayInside, DisplayOutside};
168 let box_style = self.style.get_box();
169 if box_style.clone__webkit_line_clamp().is_none() {
170 return;
171 }
172 let disp = box_style.clone_display();
173 if disp.inside() != DisplayInside::WebkitBox {
174 return;
175 }
176 if self.style.get_xul().clone__moz_box_orient() != BoxOrient::Vertical {
177 return;
178 }
179 let new_display = if disp.outside() == DisplayOutside::Block {
180 Display::FlowRoot
181 } else {
182 debug_assert_eq!(disp.outside(), DisplayOutside::Inline);
183 Display::InlineBlock
184 };
185 self.style
186 .mutate_box()
187 .set_adjusted_display(new_display, false);
188 }
189
190 fn adjust_for_position(&mut self) {
196 if self.style.is_absolutely_positioned() && self.style.is_floating() {
197 self.style.mutate_box().set_float(Float::None);
198 }
199 }
200
201 fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool
204 where
205 E: TElement,
206 {
207 if let Some(pseudo) = self.style.pseudo {
208 return pseudo.skip_item_display_fixup();
209 }
210
211 element.is_some_and(|e| e.skip_item_display_fixup())
212 }
213
214 fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
221 where
222 E: TElement,
223 {
224 let mut blockify = false;
225 macro_rules! blockify_if {
226 ($if_what:expr) => {
227 if !blockify {
228 blockify = $if_what;
229 }
230 };
231 }
232
233 blockify_if!(self.style.is_root_element);
234 if !self.skip_item_display_fixup(element) {
235 let parent_display = layout_parent_style.get_box().clone_display();
236 blockify_if!(parent_display.is_item_container());
237 }
238
239 let is_item_or_root = blockify;
240
241 blockify_if!(self.style.is_floating());
242 blockify_if!(self.style.is_absolutely_positioned());
243
244 if !blockify {
245 return;
246 }
247
248 let display = self.style.get_box().clone_display();
249 let blockified_display = display.equivalent_block_display(self.style.is_root_element);
250 if display != blockified_display {
251 self.style
252 .mutate_box()
253 .set_adjusted_display(blockified_display, is_item_or_root);
254 }
255 }
256
257 fn set_bits(&mut self) {
259 let box_style = self.style.get_box();
260 let display = box_style.clone_display();
261
262 if !display.is_contents() {
263 if !self
264 .style
265 .get_text()
266 .clone_text_decoration_line()
267 .is_empty()
268 {
269 self.style
270 .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
271 }
272
273 if self.style.get_effects().clone_opacity() == 0. {
274 self.style
275 .add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);
276 }
277 } else if self
278 .style
279 .get_parent_box()
280 .clone_display()
281 .is_item_container()
282 || self
283 .style
284 .get_parent_flags()
285 .contains(ComputedValueFlags::DISPLAY_CONTENTS_IN_ITEM_CONTAINER)
286 {
287 self.style
288 .add_flags(ComputedValueFlags::DISPLAY_CONTENTS_IN_ITEM_CONTAINER);
289 }
290
291 if self.style.pseudo.is_some_and(|p| p.is_first_line()) {
292 self.style
293 .add_flags(ComputedValueFlags::IS_IN_FIRST_LINE_SUBTREE);
294 }
295
296 if self.style.is_root_element {
297 self.style
298 .add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
299 }
300
301 #[cfg(feature = "gecko")]
302 if box_style
303 .clone_effective_containment()
304 .contains(Contain::STYLE)
305 {
306 self.style
307 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
308 }
309
310 if box_style.clone_container_type().is_size_container_type() {
311 self.style
312 .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE);
313 }
314 }
315
316 #[cfg(feature = "gecko")]
323 pub fn adjust_for_text(&mut self) {
324 debug_assert!(!self.style.is_root_element);
325 self.adjust_for_text_combine_upright();
326 self.adjust_for_text_in_ruby();
327 self.set_bits();
328 }
329
330 #[cfg(feature = "gecko")]
341 fn adjust_for_text_combine_upright(&mut self) {
342 use crate::computed_values::text_combine_upright::T as TextCombineUpright;
343 use crate::computed_values::writing_mode::T as WritingMode;
344 use crate::logical_geometry;
345
346 let writing_mode = self.style.get_inherited_box().clone_writing_mode();
347 let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
348
349 if matches!(
350 writing_mode,
351 WritingMode::VerticalRl | WritingMode::VerticalLr
352 ) && text_combine_upright == TextCombineUpright::All
353 {
354 self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
355 self.style
356 .mutate_inherited_box()
357 .set_writing_mode(WritingMode::HorizontalTb);
358 self.style.writing_mode =
359 logical_geometry::WritingMode::new(self.style.get_inherited_box());
360 }
361 }
362
363 #[cfg(feature = "gecko")]
370 fn adjust_for_text_in_ruby(&mut self) {
371 let parent_display = self.style.get_parent_box().clone_display();
372 if parent_display.is_ruby_type()
373 || self
374 .style
375 .get_parent_flags()
376 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
377 {
378 self.style
379 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
380 }
381 }
382
383 fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {
397 let our_writing_mode = self.style.get_inherited_box().clone_writing_mode();
398 let parent_writing_mode = layout_parent_style.get_inherited_box().clone_writing_mode();
399
400 if our_writing_mode != parent_writing_mode
401 && self.style.get_box().clone_display() == Display::Inline
402 {
403 if cfg!(feature = "servo") {
406 self.style
407 .mutate_box()
408 .set_adjusted_display(Display::InlineBlock, false);
409 } else {
410 self.style.mutate_box().set_display(Display::InlineBlock);
411 }
412 }
413 }
414
415 fn adjust_for_overflow(&mut self) {
421 let overflow_x = self.style.get_box().clone_overflow_x();
422 let overflow_y = self.style.get_box().clone_overflow_y();
423 if overflow_x == overflow_y {
424 return; }
426
427 if overflow_x.is_scrollable() != overflow_y.is_scrollable() {
428 let box_style = self.style.mutate_box();
429 box_style.set_overflow_x(overflow_x.to_scrollable());
430 box_style.set_overflow_y(overflow_y.to_scrollable());
431 }
432 }
433
434 #[cfg(feature = "gecko")]
435 fn adjust_for_contain(&mut self) {
436 let box_style = self.style.get_box();
437 let container_type = box_style.clone_container_type();
438 let content_visibility = box_style.clone_content_visibility();
439 if !container_type.is_size_container_type()
440 && content_visibility == ContentVisibility::Visible
441 {
442 debug_assert_eq!(
443 box_style.clone_contain(),
444 box_style.clone_effective_containment()
445 );
446 return;
447 }
448 let old_contain = box_style.clone_contain();
449 let mut new_contain = old_contain;
450 match content_visibility {
451 ContentVisibility::Visible => {},
452 ContentVisibility::Auto => {
456 new_contain.insert(Contain::LAYOUT | Contain::PAINT | Contain::STYLE)
457 },
458 ContentVisibility::Hidden => new_contain
459 .insert(Contain::LAYOUT | Contain::PAINT | Contain::SIZE | Contain::STYLE),
460 }
461 if container_type.intersects(ContainerType::INLINE_SIZE) {
462 new_contain.insert(Contain::STYLE | Contain::INLINE_SIZE);
466 } else if container_type.intersects(ContainerType::SIZE) {
467 new_contain.insert(Contain::STYLE | Contain::SIZE);
471 }
472 if new_contain == old_contain {
473 debug_assert_eq!(
474 box_style.clone_contain(),
475 box_style.clone_effective_containment()
476 );
477 return;
478 }
479 self.style
480 .mutate_box()
481 .set_effective_containment(new_contain);
482 }
483
484 #[cfg(feature = "gecko")]
489 fn adjust_for_contain_intrinsic_size(&mut self) {
490 let content_visibility = self.style.get_box().clone_content_visibility();
491 if content_visibility != ContentVisibility::Auto {
492 return;
493 }
494
495 let pos = self.style.get_position();
496 let new_width = pos.clone_contain_intrinsic_width().add_auto_if_needed();
497 let new_height = pos.clone_contain_intrinsic_height().add_auto_if_needed();
498 if new_width.is_none() && new_height.is_none() {
499 return;
500 }
501
502 let pos = self.style.mutate_position();
503 if let Some(width) = new_width {
504 pos.set_contain_intrinsic_width(width);
505 }
506 if let Some(height) = new_height {
507 pos.set_contain_intrinsic_height(height);
508 }
509 }
510
511 #[cfg(feature = "gecko")]
517 fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)
518 where
519 E: TElement,
520 {
521 if self.style.get_box().clone_display() != Display::Contents {
522 return;
523 }
524
525 if self.style.pseudo.is_some_and(|p| !p.is_element_backed()) {
527 self.style.mutate_box().set_display(Display::Inline);
528 return;
529 }
530
531 let element = match element {
532 Some(e) => e,
533 None => return,
534 };
535
536 if is_effective_display_none_for_display_contents(element) {
537 self.style.mutate_box().set_display(Display::None);
538 }
539 }
540
541 #[cfg(feature = "gecko")]
544 fn adjust_for_text_control_editing_root(&mut self) {
545 use crate::properties::longhands::white_space_collapse::computed_value::T as WhiteSpaceCollapse;
546 use crate::selector_parser::PseudoElement;
547
548 if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {
549 return;
550 }
551
552 let old_collapse = self.style.get_inherited_text().clone_white_space_collapse();
553 let new_collapse = match old_collapse {
554 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => old_collapse,
555 WhiteSpaceCollapse::Collapse
556 | WhiteSpaceCollapse::PreserveSpaces
557 | WhiteSpaceCollapse::PreserveBreaks => WhiteSpaceCollapse::Preserve,
558 };
559 if new_collapse != old_collapse {
560 self.style
561 .mutate_inherited_text()
562 .set_white_space_collapse(new_collapse);
563 }
564 }
565
566 #[cfg(feature = "gecko")]
569 fn adjust_for_fieldset_content(&mut self) {
570 use crate::selector_parser::PseudoElement;
571 if self.style.pseudo != Some(&PseudoElement::MozFieldsetContent) {
572 return;
573 }
574 let parent_display = self.style.get_parent_box().clone_display();
575 debug_assert!(
576 !parent_display.is_contents(),
577 "How did we create a fieldset-content box with display: contents?"
578 );
579 let new_display = match parent_display {
580 Display::Flex | Display::InlineFlex => Some(Display::Flex),
581 Display::Grid | Display::InlineGrid => Some(Display::Grid),
582 _ => None,
583 };
584 if let Some(new_display) = new_display {
585 self.style.mutate_box().set_display(new_display);
586 }
587 }
588
589 fn adjust_for_table_text_align(&mut self) {
596 use crate::properties::longhands::text_align::computed_value::T as TextAlign;
597 if self.style.get_box().clone_display() != Display::Table {
598 return;
599 }
600
601 match self.style.get_inherited_text().clone_text_align() {
602 TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},
603 _ => return,
604 }
605
606 self.style
607 .mutate_inherited_text()
608 .set_text_align(TextAlign::Start)
609 }
610
611 #[cfg(feature = "gecko")]
612 fn should_suppress_linebreak<E>(&self, element: Option<E>) -> bool
613 where
614 E: TElement,
615 {
616 if self.style.is_floating() || self.style.is_absolutely_positioned() {
618 return false;
619 }
620 let parent_display = self.style.get_parent_box().clone_display();
621 if self
622 .style
623 .get_parent_flags()
624 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
625 {
626 if parent_display.is_line_participant() || parent_display.is_contents() {
629 return true;
630 }
631 }
632 match self.style.get_box().clone_display() {
633 Display::RubyBase | Display::RubyText => true,
635 Display::RubyBaseContainer | Display::RubyTextContainer
644 if element.map_or(true, |e| e.is_html_element()) =>
645 {
646 false
647 },
648 _ => parent_display.is_ruby_type(),
652 }
653 }
654
655 #[cfg(feature = "gecko")]
661 fn adjust_for_ruby<E>(&mut self, element: Option<E>)
662 where
663 E: TElement,
664 {
665 use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
666
667 let self_display = self.style.get_box().clone_display();
668 if self.should_suppress_linebreak(element) {
670 self.style
671 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
672 if !self.skip_item_display_fixup(element) {
674 let inline_display = self_display.inlinify();
675 if self_display != inline_display {
676 self.style
677 .mutate_box()
678 .set_adjusted_display(inline_display, false);
679 }
680 }
681 }
682 if self_display.is_ruby_level_container() {
687 self.style.reset_border_struct();
688 self.style.reset_padding_struct();
689 }
690
691 if self_display.is_ruby_type() {
694 let new_value = match self.style.get_text().clone_unicode_bidi() {
695 UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
696 UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
697 _ => None,
698 };
699 if let Some(new_value) = new_value {
700 self.style.mutate_text().set_unicode_bidi(new_value);
701 }
702 }
703 }
704
705 fn adjust_for_visited<E>(&mut self, element: Option<E>)
715 where
716 E: TElement,
717 {
718 if !self.style.has_visited_style() {
719 return;
720 }
721
722 let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());
723
724 if !is_link_element {
725 return;
726 }
727
728 if element.unwrap().is_visited_link() {
729 self.style
730 .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
731 } else {
732 self.style
734 .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
735 }
736 }
737
738 #[cfg(feature = "gecko")]
743 fn adjust_for_justify_items(&mut self) {
744 use crate::values::specified::align;
745 let justify_items = self.style.get_position().clone_justify_items();
746 if justify_items.specified != align::JustifyItems::legacy() {
747 return;
748 }
749
750 let parent_justify_items = self.style.get_parent_position().clone_justify_items();
751
752 if !parent_justify_items.computed.contains(AlignFlags::LEGACY) {
753 return;
754 }
755
756 if parent_justify_items.computed == justify_items.computed {
757 return;
758 }
759
760 self.style
761 .mutate_position()
762 .set_computed_justify_items(parent_justify_items.computed);
763 }
764
765 #[cfg(feature = "gecko")]
770 fn adjust_for_appearance<E>(&mut self, element: Option<E>)
771 where
772 E: TElement,
773 {
774 use crate::properties::longhands::appearance::computed_value::T as Appearance;
775 use crate::properties::longhands::line_height::computed_value::T as LineHeight;
776
777 let box_ = self.style.get_box();
778 let appearance = match box_.clone_appearance() {
779 Appearance::Auto => box_.clone__moz_default_appearance(),
780 a => a,
781 };
782
783 if appearance == Appearance::Menulist {
784 if self.style.get_font().clone_line_height() == LineHeight::normal() {
785 return;
786 }
787 if self.style.pseudo.is_some() {
788 return;
789 }
790 let is_html_select_element = element.map_or(false, |e| {
791 e.is_html_element() && e.local_name() == &*atom!("select")
792 });
793 if !is_html_select_element {
794 return;
795 }
796 self.style
797 .mutate_font()
798 .set_line_height(LineHeight::normal());
799 }
800 }
801
802 #[cfg(feature = "gecko")]
813 fn adjust_for_marker_pseudo(&mut self, author_specified_properties: &LonghandIdSet) {
814 use crate::values::computed::counters::Content;
815 use crate::values::computed::font::{FontFamily, FontSynthesis, FontSynthesisStyle};
816 use crate::values::computed::text::{LetterSpacing, WordSpacing};
817
818 let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker())
819 && self.style.get_list().clone_list_style_type().is_bullet()
820 && self.style.get_counters().clone_content() == Content::Normal;
821 if !is_legacy_marker {
822 return;
823 }
824 if !author_specified_properties.contains(LonghandId::FontFamily) {
825 self.style
826 .mutate_font()
827 .set_font_family(FontFamily::moz_bullet().clone());
828
829 if !author_specified_properties.contains(LonghandId::FontSynthesisWeight) {
833 self.style
834 .mutate_font()
835 .set_font_synthesis_weight(FontSynthesis::None);
836 }
837 if !author_specified_properties.contains(LonghandId::FontSynthesisStyle) {
838 self.style
839 .mutate_font()
840 .set_font_synthesis_style(FontSynthesisStyle::None);
841 }
842 }
843 if !author_specified_properties.contains(LonghandId::LetterSpacing) {
844 self.style
845 .mutate_inherited_text()
846 .set_letter_spacing(LetterSpacing::normal());
847 }
848 if !author_specified_properties.contains(LonghandId::WordSpacing) {
849 self.style
850 .mutate_inherited_text()
851 .set_word_spacing(WordSpacing::normal());
852 }
853 }
854
855 fn adjust_for_try_tactic(&mut self, tactic: &PositionTryFallbacksTryTactic) {
863 debug_assert!(!tactic.is_empty());
864 let wm = self.style.writing_mode;
866 for tactic in tactic.iter() {
868 use PositionTryFallbacksTryTacticKeyword::*;
869 match tactic {
870 FlipBlock => {
871 self.flip_self_alignment(true);
872 self.flip_insets_and_margins(wm.is_vertical());
873 },
874 FlipInline => {
875 self.flip_self_alignment(false);
876 self.flip_insets_and_margins(wm.is_horizontal());
877 },
878 FlipX => {
879 self.flip_self_alignment(wm.is_vertical());
880 self.flip_insets_and_margins(true);
881 },
882 FlipY => {
883 self.flip_self_alignment(wm.is_horizontal());
884 self.flip_insets_and_margins(false);
885 },
886 FlipStart => {
887 self.flip_start();
888 },
889 }
890 self.apply_position_area_tactic(*tactic);
891 }
892 }
893
894 fn apply_position_area_tactic(&mut self, tactic: PositionTryFallbacksTryTacticKeyword) {
895 let pos = self.style.get_position();
896 let old = pos.clone_position_area();
897 let wm = self.style.writing_mode;
898 let new = old.with_tactic(wm, tactic);
899 if new == old {
900 return;
901 }
902 let pos = self.style.mutate_position();
903 pos.set_position_area(new);
904 }
905
906 fn swap_insets(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
908 debug_assert_ne!(a_side, b_side);
909 let pos = self.style.mutate_position();
910 let mut a = pos.get_inset(a_side).clone();
911 a.try_tactic_adjustment(a_side, b_side);
912 let mut b = pos.get_inset(b_side).clone();
913 b.try_tactic_adjustment(b_side, a_side);
914 pos.set_inset(a_side, b);
915 pos.set_inset(b_side, a);
916 }
917
918 fn swap_margins(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {
919 debug_assert_ne!(a_side, b_side);
920 let margin = self.style.get_margin();
921 let mut a = margin.get_margin(a_side).clone();
922 a.try_tactic_adjustment(a_side, b_side);
923 let mut b = margin.get_margin(b_side).clone();
924 b.try_tactic_adjustment(b_side, a_side);
925 let margin = self.style.mutate_margin();
926 margin.set_margin(a_side, b);
927 margin.set_margin(b_side, a);
928 }
929
930 fn swap_sizes(&mut self, block_start: PhysicalSide, inline_start: PhysicalSide) {
931 let pos = self.style.mutate_position();
932 let mut min_width = pos.clone_min_width();
933 min_width.try_tactic_adjustment(inline_start, block_start);
934 let mut max_width = pos.clone_max_width();
935 max_width.try_tactic_adjustment(inline_start, block_start);
936 let mut width = pos.clone_width();
937 width.try_tactic_adjustment(inline_start, block_start);
938
939 let mut min_height = pos.clone_min_height();
940 min_height.try_tactic_adjustment(block_start, inline_start);
941 let mut max_height = pos.clone_max_height();
942 max_height.try_tactic_adjustment(block_start, inline_start);
943 let mut height = pos.clone_height();
944 height.try_tactic_adjustment(block_start, inline_start);
945
946 let pos = self.style.mutate_position();
947 pos.set_width(height);
948 pos.set_height(width);
949 pos.set_max_width(max_height);
950 pos.set_max_height(max_width);
951 pos.set_min_width(min_height);
952 pos.set_min_height(min_width);
953 }
954
955 fn flip_start(&mut self) {
956 let wm = self.style.writing_mode;
957 let bs = wm.block_start_physical_side();
958 let is = wm.inline_start_physical_side();
959 let be = wm.block_end_physical_side();
960 let ie = wm.inline_end_physical_side();
961 self.swap_sizes(bs, is);
962 self.swap_insets(bs, is);
963 self.swap_insets(ie, be);
964 self.swap_margins(bs, is);
965 self.swap_margins(ie, be);
966 self.flip_alignment_start();
967 }
968
969 fn flip_insets_and_margins(&mut self, horizontal: bool) {
970 if horizontal {
971 self.swap_insets(PhysicalSide::Left, PhysicalSide::Right);
972 self.swap_margins(PhysicalSide::Left, PhysicalSide::Right);
973 } else {
974 self.swap_insets(PhysicalSide::Top, PhysicalSide::Bottom);
975 self.swap_margins(PhysicalSide::Top, PhysicalSide::Bottom);
976 }
977 }
978
979 fn flip_alignment_start(&mut self) {
980 let pos = self.style.get_position();
981 let align = pos.clone_align_self();
982 let mut justify = pos.clone_justify_self();
983 if align == justify {
984 return;
985 }
986
987 if matches!(justify.value(), AlignFlags::LEFT | AlignFlags::RIGHT) {
990 let left = justify.value() == AlignFlags::LEFT;
991 let ltr = self.style.writing_mode.is_bidi_ltr();
992 justify = justify.with_value(if left == ltr {
993 AlignFlags::SELF_START
994 } else {
995 AlignFlags::SELF_END
996 });
997 }
998
999 let pos = self.style.mutate_position();
1000 pos.set_align_self(justify);
1001 pos.set_justify_self(align);
1002 }
1003
1004 fn flip_self_alignment(&mut self, block: bool) {
1005 let pos = self.style.get_position();
1006 let cur = if block {
1007 pos.clone_align_self()
1008 } else {
1009 pos.clone_justify_self()
1010 };
1011 let flipped = cur.flip_position();
1012 if flipped == cur {
1013 return;
1014 }
1015 let pos = self.style.mutate_position();
1016 if block {
1017 pos.set_align_self(flipped);
1018 } else {
1019 pos.set_justify_self(flipped);
1020 }
1021 }
1022
1023 #[allow(unused_variables)]
1025 pub fn adjust<E>(
1026 &mut self,
1027 layout_parent_style: &ComputedValues,
1028 element: Option<E>,
1029 try_tactic: &PositionTryFallbacksTryTactic,
1030 author_specified_properties: &LonghandIdSet,
1031 ) where
1032 E: TElement,
1033 {
1034 if cfg!(debug_assertions) {
1035 if let Some(e) = element {
1036 if let Some(p) = e.implemented_pseudo_element() {
1037 debug_assert!(
1041 self.style.pseudo.is_some(),
1042 "Someone really messed up (no pseudo style for {e:?}, {p:?})"
1043 );
1044 }
1045 }
1046 }
1047 self.adjust_for_visited(element);
1057 #[cfg(feature = "gecko")]
1058 {
1059 self.adjust_for_prohibited_display_contents(element);
1060 self.adjust_for_fieldset_content();
1061 self.adjust_for_text_control_editing_root();
1062 }
1063 self.adjust_for_top_layer();
1064 self.blockify_if_necessary(layout_parent_style, element);
1065 #[cfg(feature = "gecko")]
1066 self.adjust_for_webkit_line_clamp();
1067 self.adjust_for_position();
1068 self.adjust_for_overflow();
1069 #[cfg(feature = "gecko")]
1070 {
1071 self.adjust_for_contain();
1072 self.adjust_for_contain_intrinsic_size();
1073 self.adjust_for_justify_items();
1074 }
1075 self.adjust_for_table_text_align();
1076 self.adjust_for_writing_mode(layout_parent_style);
1077 #[cfg(feature = "gecko")]
1078 {
1079 self.adjust_for_ruby(element);
1080 self.adjust_for_appearance(element);
1081 self.adjust_for_marker_pseudo(author_specified_properties);
1082 }
1083 if !try_tactic.is_empty() {
1084 self.adjust_for_try_tactic(try_tactic);
1085 }
1086 self.set_bits();
1087 }
1088}