1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 //! A struct to encapsulate all the style fixups and flags propagations
6 //! a computed style needs in order for it to adhere to the CSS spec.
7 
8 use app_units::Au;
9 use dom::TElement;
10 use properties::{self, CascadeFlags, ComputedValues, StyleBuilder};
11 use properties::computed_value_flags::ComputedValueFlags;
12 use properties::longhands::display::computed_value::T as Display;
13 use properties::longhands::float::computed_value::T as Float;
14 use properties::longhands::overflow_x::computed_value::T as Overflow;
15 use properties::longhands::position::computed_value::T as Position;
16 
17 /// A struct that implements all the adjustment methods.
18 ///
19 /// NOTE(emilio): If new adjustments are introduced that depend on reset
20 /// properties of the parent, you may need tweaking the
21 /// `ChildCascadeRequirement` code in `matching.rs`.
22 pub struct StyleAdjuster<'a, 'b: 'a> {
23     style: &'a mut StyleBuilder<'b>,
24 }
25 
26 impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
27     /// Trivially constructs a new StyleAdjuster.
28     #[inline]
new(style: &'a mut StyleBuilder<'b>) -> Self29     pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
30         StyleAdjuster { style }
31     }
32 
33     /// <https://fullscreen.spec.whatwg.org/#new-stacking-layer>
34     ///
35     ///    Any position value other than 'absolute' and 'fixed' are
36     ///    computed to 'absolute' if the element is in a top layer.
37     ///
adjust_for_top_layer(&mut self)38     fn adjust_for_top_layer(&mut self) {
39         if !self.style.out_of_flow_positioned() && self.style.in_top_layer() {
40             self.style.mutate_box().set_position(Position::Absolute);
41         }
42     }
43 
44     /// CSS 2.1 section 9.7:
45     ///
46     ///    If 'position' has the value 'absolute' or 'fixed', [...] the computed
47     ///    value of 'float' is 'none'.
48     ///
adjust_for_position(&mut self)49     fn adjust_for_position(&mut self) {
50         if self.style.out_of_flow_positioned() && self.style.floated() {
51             self.style.mutate_box().set_float(Float::None);
52         }
53     }
54 
55     /// Whether we should skip any item-based display property blockification on
56     /// this element.
skip_item_display_fixup<E>(&self, element: Option<E>) -> bool where E: TElement,57     fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool
58     where
59         E: TElement,
60     {
61         if let Some(pseudo) = self.style.pseudo {
62             return pseudo.skip_item_display_fixup();
63         }
64 
65         element.map_or(false, |e| e.skip_item_display_fixup())
66     }
67 
68 
69     /// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
70     /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>
blockify_if_necessary<E>( &mut self, layout_parent_style: &ComputedValues, element: Option<E>, ) where E: TElement,71     fn blockify_if_necessary<E>(
72         &mut self,
73         layout_parent_style: &ComputedValues,
74         element: Option<E>,
75     )
76     where
77         E: TElement,
78     {
79         let mut blockify = false;
80         macro_rules! blockify_if {
81             ($if_what:expr) => {
82                 if !blockify {
83                     blockify = $if_what;
84                 }
85             }
86         }
87 
88         let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root());
89         blockify_if!(is_root);
90         if !self.skip_item_display_fixup(element) {
91             blockify_if!(layout_parent_style.get_box().clone_display().is_item_container());
92         }
93 
94         let is_item_or_root = blockify;
95 
96         blockify_if!(self.style.floated());
97         blockify_if!(self.style.out_of_flow_positioned());
98 
99         if !blockify {
100             return;
101         }
102 
103         let display = self.style.get_box().clone_display();
104         let blockified_display = display.equivalent_block_display(is_root);
105         if display != blockified_display {
106             self.style.mutate_box().set_adjusted_display(
107                 blockified_display,
108                 is_item_or_root,
109             );
110         }
111     }
112 
113     /// Compute a few common flags for both text and element's style.
set_bits(&mut self)114     pub fn set_bits(&mut self) {
115         let display = self.style.get_box().clone_display();
116 
117         if !display.is_contents() && !self.style.get_text().clone_text_decoration_line().is_empty() {
118             self.style.flags.insert(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
119         }
120 
121         if display == Display::None {
122             self.style.flags.insert(ComputedValueFlags::IS_IN_DISPLAY_NONE_SUBTREE);
123         }
124 
125         if self.style.is_pseudo_element() {
126             self.style.flags.insert(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
127         }
128 
129         #[cfg(feature = "servo")]
130         {
131             if self.style.get_parent_column().is_multicol() {
132                 self.style.flags.insert(ComputedValueFlags::CAN_BE_FRAGMENTED);
133             }
134         }
135     }
136 
137     /// Adjust the style for text style.
138     ///
139     /// The adjustments here are a subset of the adjustments generally, because
140     /// text only inherits properties.
141     ///
142     /// Note that this, for Gecko, comes through Servo_ComputedValues_Inherit.
143     #[cfg(feature = "gecko")]
adjust_for_text(&mut self)144     pub fn adjust_for_text(&mut self) {
145         self.adjust_for_text_combine_upright();
146         self.adjust_for_text_in_ruby();
147         self.set_bits();
148     }
149 
150     /// Change writing mode of the text frame for text-combine-upright.
151     ///
152     /// It is safe to look at our own style because we are looking at inherited
153     /// properties, and text is just plain inheritance.
154     ///
155     /// TODO(emilio): we should (Gecko too) revise these adjustments in presence
156     /// of display: contents.
157     ///
158     /// FIXME(emilio): How does this play with logical properties? Doesn't
159     /// mutating writing-mode change the potential physical sides chosen?
160     #[cfg(feature = "gecko")]
adjust_for_text_combine_upright(&mut self)161     fn adjust_for_text_combine_upright(&mut self) {
162         use computed_values::text_combine_upright::T as TextCombineUpright;
163         use computed_values::writing_mode::T as WritingMode;
164 
165         let writing_mode =
166             self.style.get_inheritedbox().clone_writing_mode();
167         let text_combine_upright =
168             self.style.get_inheritedtext().clone_text_combine_upright();
169 
170         if writing_mode != WritingMode::HorizontalTb &&
171            text_combine_upright == TextCombineUpright::All {
172             self.style.flags.insert(ComputedValueFlags::IS_TEXT_COMBINED);
173             self.style.mutate_inheritedbox().set_writing_mode(WritingMode::HorizontalTb);
174         }
175     }
176 
177     /// Unconditionally propagates the line break suppression flag to text, and
178     /// additionally it applies it if it is in any ruby box.
179     ///
180     /// This is necessary because its parent may not itself have the flag set
181     /// (e.g. ruby or ruby containers), thus we may not inherit the flag from
182     /// them.
183     #[cfg(feature = "gecko")]
adjust_for_text_in_ruby(&mut self)184     fn adjust_for_text_in_ruby(&mut self) {
185         let parent_display = self.style.get_parent_box().clone_display();
186         if parent_display.is_ruby_type() ||
187            self.style.get_parent_flags().contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
188         {
189             self.style.flags.insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
190         }
191     }
192 
193     /// <https://drafts.csswg.org/css-writing-modes-3/#block-flow:>
194     ///
195     ///    If a box has a different writing-mode value than its containing
196     ///    block:
197     ///
198     ///        - If the box has a specified display of inline, its display
199     ///          computes to inline-block. [CSS21]
200     ///
201     /// This matches the adjustment that Gecko does, not exactly following
202     /// the spec. See also:
203     ///
204     /// <https://lists.w3.org/Archives/Public/www-style/2017Mar/0045.html>
205     /// <https://github.com/servo/servo/issues/15754>
adjust_for_writing_mode( &mut self, layout_parent_style: &ComputedValues, )206     fn adjust_for_writing_mode(
207         &mut self,
208         layout_parent_style: &ComputedValues,
209     ) {
210         let our_writing_mode =
211             self.style.get_inheritedbox().clone_writing_mode();
212         let parent_writing_mode =
213             layout_parent_style.get_inheritedbox().clone_writing_mode();
214 
215         if our_writing_mode != parent_writing_mode &&
216            self.style.get_box().clone_display() == Display::Inline {
217             // TODO(emilio): Figure out if we can just set the adjusted display
218             // on Gecko too and unify this code path.
219             if cfg!(feature = "servo") {
220                 self.style.mutate_box().set_adjusted_display(
221                     Display::InlineBlock,
222                     false,
223                 );
224             } else {
225                 self.style.mutate_box().set_display(Display::InlineBlock);
226             }
227         }
228     }
229 
230     #[cfg(feature = "gecko")]
adjust_for_contain(&mut self)231     fn adjust_for_contain(&mut self) {
232         use properties::longhands::contain::SpecifiedValue;
233 
234         // An element with contain: paint needs to be a formatting context, and
235         // also implies overflow: clip.
236         //
237         // TODO(emilio): This mimics Gecko, but spec links are missing!
238         let contain = self.style.get_box().clone_contain();
239         if !contain.contains(SpecifiedValue::PAINT) {
240             return;
241         }
242 
243         if self.style.get_box().clone_display() == Display::Inline {
244             self.style.mutate_box().set_adjusted_display(Display::InlineBlock,
245                                                          false);
246         }
247 
248 
249         // When 'contain: paint', update overflow from 'visible' to 'clip'.
250         if self.style.get_box().clone_contain().contains(SpecifiedValue::PAINT) {
251             if self.style.get_box().clone_overflow_x() == Overflow::Visible {
252                 let box_style = self.style.mutate_box();
253                 box_style.set_overflow_x(Overflow::MozHiddenUnscrollable);
254                 box_style.set_overflow_y(Overflow::MozHiddenUnscrollable);
255             }
256         }
257     }
258 
259     /// When mathvariant is not "none", font-weight and font-style are
260     /// both forced to "normal".
261     #[cfg(feature = "gecko")]
adjust_for_mathvariant(&mut self)262     fn adjust_for_mathvariant(&mut self) {
263         use properties::longhands::_moz_math_variant::computed_value::T as MozMathVariant;
264         use properties::longhands::font_style::computed_value::T as FontStyle;
265         use properties::longhands::font_weight::computed_value::T as FontWeight;
266         if self.style.get_font().clone__moz_math_variant() != MozMathVariant::None {
267             let font_style = self.style.mutate_font();
268             // Sadly we don't have a nice name for the computed value
269             // of "font-weight: normal".
270             font_style.set_font_weight(FontWeight::normal());
271             font_style.set_font_style(FontStyle::Normal);
272         }
273     }
274 
275     /// This implements an out-of-date spec. The new spec moves the handling of
276     /// this to layout, which Gecko implements but Servo doesn't.
277     ///
278     /// See https://github.com/servo/servo/issues/15229
279     #[cfg(feature = "servo")]
adjust_for_alignment(&mut self, layout_parent_style: &ComputedValues)280     fn adjust_for_alignment(&mut self, layout_parent_style: &ComputedValues) {
281         use computed_values::align_items::T as AlignItems;
282         use computed_values::align_self::T as AlignSelf;
283 
284         if self.style.get_position().clone_align_self() == AlignSelf::Auto &&
285            !self.style.out_of_flow_positioned() {
286             let self_align =
287                 match layout_parent_style.get_position().clone_align_items() {
288                     AlignItems::Stretch => AlignSelf::Stretch,
289                     AlignItems::Baseline => AlignSelf::Baseline,
290                     AlignItems::FlexStart => AlignSelf::FlexStart,
291                     AlignItems::FlexEnd => AlignSelf::FlexEnd,
292                     AlignItems::Center => AlignSelf::Center,
293                 };
294             self.style.mutate_position().set_align_self(self_align);
295         }
296     }
297 
298     /// The initial value of border-*-width may be changed at computed value
299     /// time.
300     ///
301     /// This is moved to properties.rs for convenience.
adjust_for_border_width(&mut self)302     fn adjust_for_border_width(&mut self) {
303         properties::adjust_border_width(self.style);
304     }
305 
306     /// The initial value of outline-width may be changed at computed value time.
adjust_for_outline(&mut self)307     fn adjust_for_outline(&mut self) {
308         if self.style.get_outline().clone_outline_style().none_or_hidden() &&
309            self.style.get_outline().outline_has_nonzero_width() {
310             self.style.mutate_outline().set_outline_width(Au(0).into());
311         }
312     }
313 
314     /// CSS3 overflow-x and overflow-y require some fixup as well in some
315     /// cases.
316     ///
317     /// overflow: clip and overflow: visible are meaningful only when used in
318     /// both dimensions.
adjust_for_overflow(&mut self)319     fn adjust_for_overflow(&mut self) {
320         let original_overflow_x = self.style.get_box().clone_overflow_x();
321         let original_overflow_y = self.style.get_box().clone_overflow_y();
322 
323         let mut overflow_x = original_overflow_x;
324         let mut overflow_y = original_overflow_y;
325 
326         if overflow_x == overflow_y {
327             return;
328         }
329 
330         // If 'visible' is specified but doesn't match the other dimension,
331         // it turns into 'auto'.
332         if overflow_x == Overflow::Visible {
333             overflow_x = Overflow::Auto;
334         }
335 
336         if overflow_y == Overflow::Visible {
337             overflow_y = Overflow::Auto;
338         }
339 
340         #[cfg(feature = "gecko")]
341         {
342             // overflow: clip is deprecated, so convert to hidden if it's
343             // specified in only one dimension.
344             if overflow_x == Overflow::MozHiddenUnscrollable {
345                 overflow_x = Overflow::Hidden;
346             }
347             if overflow_y == Overflow::MozHiddenUnscrollable {
348                 overflow_y = Overflow::Hidden;
349             }
350         }
351 
352         if overflow_x != original_overflow_x ||
353            overflow_y != original_overflow_y {
354             let box_style = self.style.mutate_box();
355             box_style.set_overflow_x(overflow_x);
356             box_style.set_overflow_y(overflow_y);
357         }
358     }
359 
360     /// Native anonymous content converts display:contents into display:inline.
361     #[cfg(feature = "gecko")]
adjust_for_prohibited_display_contents(&mut self)362     fn adjust_for_prohibited_display_contents(&mut self) {
363         // TODO: We should probably convert display:contents into display:none
364         // in some cases too: https://drafts.csswg.org/css-display/#unbox
365         //
366         // FIXME(emilio): ::before and ::after should support display: contents,
367         // see bug 1418138.
368         if self.style.pseudo.is_none() ||
369            self.style.get_box().clone_display() != Display::Contents {
370             return;
371         }
372 
373         self.style.mutate_box().set_display(Display::Inline);
374     }
375 
376     /// If a <fieldset> has grid/flex display type, we need to inherit
377     /// this type into its ::-moz-fieldset-content anonymous box.
378     ///
379     /// NOTE(emilio): We don't need to handle the display change for this case
380     /// in matching.rs because anonymous box restyling works separately to the
381     /// normal cascading process.
382     #[cfg(feature = "gecko")]
adjust_for_fieldset_content( &mut self, layout_parent_style: &ComputedValues, )383     fn adjust_for_fieldset_content(
384         &mut self,
385         layout_parent_style: &ComputedValues,
386     ) {
387         match self.style.pseudo {
388             Some(ref p) if p.is_fieldset_content() => {},
389             _ => return,
390         }
391 
392         debug_assert_eq!(self.style.get_box().clone_display(), Display::Block);
393         // TODO We actually want style from parent rather than layout
394         // parent, so that this fixup doesn't happen incorrectly when
395         // when <fieldset> has "display: contents".
396         let parent_display = layout_parent_style.get_box().clone_display();
397         let new_display = match parent_display {
398             Display::Flex |
399             Display::InlineFlex => Some(Display::Flex),
400             Display::Grid |
401             Display::InlineGrid => Some(Display::Grid),
402             _ => None,
403         };
404         if let Some(new_display) = new_display {
405             self.style.mutate_box().set_display(new_display);
406         }
407     }
408 
409     /// -moz-center, -moz-left and -moz-right are used for HTML's alignment.
410     ///
411     /// This is covering the <div align="right"><table>...</table></div> case.
412     ///
413     /// In this case, we don't want to inherit the text alignment into the
414     /// table.
415     #[cfg(feature = "gecko")]
adjust_for_table_text_align(&mut self)416     fn adjust_for_table_text_align(&mut self) {
417         use properties::longhands::text_align::computed_value::T as TextAlign;
418         if self.style.get_box().clone_display() != Display::Table {
419             return;
420         }
421 
422         match self.style.get_inheritedtext().clone_text_align() {
423             TextAlign::MozLeft |
424             TextAlign::MozCenter |
425             TextAlign::MozRight => {},
426             _ => return,
427         }
428 
429         self.style.mutate_inheritedtext().set_text_align(TextAlign::Start)
430     }
431 
432     /// Computes the used text decoration for Servo.
433     ///
434     /// FIXME(emilio): This is a layout tree concept, should move away from
435     /// style, since otherwise we're going to have the same subtle bugs WebKit
436     /// and Blink have with this very same thing.
437     #[cfg(feature = "servo")]
adjust_for_text_decorations_in_effect(&mut self)438     fn adjust_for_text_decorations_in_effect(&mut self) {
439         use values::computed::text::TextDecorationsInEffect;
440 
441         let decorations_in_effect = TextDecorationsInEffect::from_style(&self.style);
442         if self.style.get_inheritedtext().text_decorations_in_effect != decorations_in_effect {
443             self.style.mutate_inheritedtext().text_decorations_in_effect = decorations_in_effect;
444         }
445     }
446 
447     #[cfg(feature = "gecko")]
should_suppress_linebreak( &self, layout_parent_style: &ComputedValues, ) -> bool448     fn should_suppress_linebreak(
449         &self,
450         layout_parent_style: &ComputedValues,
451     ) -> bool {
452         // Line break suppression should only be propagated to in-flow children.
453         if self.style.floated() || self.style.out_of_flow_positioned() {
454             return false;
455         }
456         let parent_display = layout_parent_style.get_box().clone_display();
457         if layout_parent_style.flags.contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK) {
458             // Line break suppression is propagated to any children of
459             // line participants.
460             if parent_display.is_line_participant() {
461                 return true;
462             }
463         }
464         match self.style.get_box().clone_display() {
465             // Ruby base and text are always non-breakable.
466             Display::RubyBase |
467             Display::RubyText => true,
468             // Ruby base container and text container are breakable.
469             // Note that, when certain HTML tags, e.g. form controls, have ruby
470             // level container display type, they could also escape from the
471             // line break suppression flag while they shouldn't. However, it is
472             // generally fine since they themselves are non-breakable.
473             Display::RubyBaseContainer |
474             Display::RubyTextContainer => false,
475             // Anything else is non-breakable if and only if its layout parent
476             // has a ruby display type, because any of the ruby boxes can be
477             // anonymous.
478             _ => parent_display.is_ruby_type(),
479         }
480     }
481 
482     /// Do ruby-related style adjustments, which include:
483     /// * propagate the line break suppression flag,
484     /// * inlinify block descendants,
485     /// * suppress border and padding for ruby level containers,
486     /// * correct unicode-bidi.
487     #[cfg(feature = "gecko")]
adjust_for_ruby<E>( &mut self, layout_parent_style: &ComputedValues, element: Option<E>, ) where E: TElement,488     fn adjust_for_ruby<E>(
489         &mut self,
490         layout_parent_style: &ComputedValues,
491         element: Option<E>,
492     )
493     where
494         E: TElement,
495     {
496         use properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
497 
498         let self_display = self.style.get_box().clone_display();
499         // Check whether line break should be suppressed for this element.
500         if self.should_suppress_linebreak(layout_parent_style) {
501             self.style.flags.insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
502             // Inlinify the display type if allowed.
503             if !self.skip_item_display_fixup(element) {
504                 let inline_display = self_display.inlinify();
505                 if self_display != inline_display {
506                     self.style.mutate_box().set_adjusted_display(inline_display, false);
507                 }
508             }
509         }
510         // Suppress border and padding for ruby level containers.
511         // This is actually not part of the spec. It is currently unspecified
512         // how border and padding should be handled for ruby level container,
513         // and suppressing them here make it easier for layout to handle.
514         if self_display.is_ruby_level_container() {
515             self.style.reset_border_struct();
516             self.style.reset_padding_struct();
517         }
518 
519         // Force bidi isolation on all internal ruby boxes and ruby container
520         // per spec https://drafts.csswg.org/css-ruby-1/#bidi
521         if self_display.is_ruby_type() {
522             let new_value = match self.style.get_text().clone_unicode_bidi() {
523                 UnicodeBidi::Normal |
524                 UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
525                 UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
526                 _ => None,
527             };
528             if let Some(new_value) = new_value {
529                 self.style.mutate_text().set_unicode_bidi(new_value);
530             }
531         }
532     }
533 
534     /// Computes the RELEVANT_LINK_VISITED flag based on the parent style and on
535     /// whether we're a relevant link.
536     ///
537     /// NOTE(emilio): We don't do this for text styles, which is... dubious, but
538     /// Gecko doesn't seem to do it either. It's extremely easy to do if needed
539     /// though.
540     ///
541     /// FIXME(emilio): This isn't technically a style adjustment thingie, could
542     /// it move somewhere else?
adjust_for_visited<E>(&mut self, element: Option<E>) where E: TElement,543     fn adjust_for_visited<E>(&mut self, element: Option<E>)
544     where
545         E: TElement,
546     {
547         if !self.style.has_visited_style() {
548             return;
549         }
550 
551         let is_link_element =
552             self.style.pseudo.is_none() &&
553             element.map_or(false, |e| e.is_link());
554 
555         if !is_link_element {
556             return;
557         }
558 
559         if element.unwrap().is_visited_link() {
560             self.style.flags.insert(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
561         } else {
562             // Need to remove to handle unvisited link inside visited.
563             self.style.flags.remove(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
564         }
565     }
566 
567     /// Resolves "justify-items: auto" based on the inherited style if needed to
568     /// comply with:
569     ///
570     /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>
571     ///
572     /// (Note that "auto" is being renamed to "legacy")
573     #[cfg(feature = "gecko")]
adjust_for_justify_items(&mut self)574     fn adjust_for_justify_items(&mut self) {
575         use values::specified::align;
576         let justify_items = self.style.get_position().clone_justify_items();
577         if justify_items.specified.0 != align::AlignFlags::AUTO {
578             return;
579         }
580 
581         let parent_justify_items =
582             self.style.get_parent_position().clone_justify_items();
583 
584         if !parent_justify_items.computed.0.contains(align::AlignFlags::LEGACY) {
585             return;
586         }
587 
588         if parent_justify_items.computed == justify_items.computed {
589             return;
590         }
591 
592         self.style
593             .mutate_position()
594             .set_computed_justify_items(parent_justify_items.computed);
595     }
596 
597     /// Adjusts the style to account for various fixups that don't fit naturally
598     /// into the cascade.
599     ///
600     /// When comparing to Gecko, this is similar to the work done by
601     /// `nsStyleContext::ApplyStyleFixups`, plus some parts of
602     /// `nsStyleSet::GetContext`.
adjust<E>( &mut self, layout_parent_style: &ComputedValues, element: Option<E>, flags: CascadeFlags, ) where E: TElement,603     pub fn adjust<E>(
604         &mut self,
605         layout_parent_style: &ComputedValues,
606         element: Option<E>,
607         flags: CascadeFlags,
608     )
609     where
610         E: TElement,
611     {
612         if cfg!(debug_assertions) {
613             if element.and_then(|e| e.implemented_pseudo_element()).is_some() {
614                 // It'd be nice to assert `self.style.pseudo == Some(&pseudo)`,
615                 // but we do resolve ::-moz-list pseudos on ::before / ::after
616                 // content, sigh.
617                 debug_assert!(
618                     self.style.pseudo.is_some(),
619                     "Someone really messed up"
620                 );
621             }
622         }
623         // FIXME(emilio): The apply_declarations callsite in Servo's
624         // animation, and the font stuff for Gecko
625         // (Stylist::compute_for_declarations) should pass an element to
626         // cascade(), then we can make this assertion hold everywhere.
627         // debug_assert!(
628         //     element.is_some() || self.style.pseudo.is_some(),
629         //     "Should always have an element around for non-pseudo styles"
630         // );
631 
632         // Don't adjust visited styles, visited-dependent properties aren't
633         // affected by these adjustments and it'd be just wasted work anyway.
634         //
635         // It also doesn't make much sense to adjust them, since we don't
636         // cascade most properties anyway, and they wouldn't be looked up.
637         if flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) {
638             return;
639         }
640 
641         self.adjust_for_visited(element);
642         #[cfg(feature = "gecko")]
643         {
644             self.adjust_for_prohibited_display_contents();
645             self.adjust_for_fieldset_content(layout_parent_style);
646         }
647         self.adjust_for_top_layer();
648         self.blockify_if_necessary(layout_parent_style, element);
649         self.adjust_for_position();
650         self.adjust_for_overflow();
651         #[cfg(feature = "gecko")]
652         {
653             self.adjust_for_table_text_align();
654             self.adjust_for_contain();
655             self.adjust_for_mathvariant();
656             self.adjust_for_justify_items();
657         }
658         #[cfg(feature = "servo")]
659         {
660             self.adjust_for_alignment(layout_parent_style);
661         }
662         self.adjust_for_border_width();
663         self.adjust_for_outline();
664         self.adjust_for_writing_mode(layout_parent_style);
665         #[cfg(feature = "gecko")]
666         {
667             self.adjust_for_ruby(layout_parent_style, element);
668         }
669         #[cfg(feature = "servo")]
670         {
671             self.adjust_for_text_decorations_in_effect();
672         }
673         self.set_bits();
674     }
675 }
676