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 https://mozilla.org/MPL/2.0/. */
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.
8 use crate::computed_value_flags::ComputedValueFlags;
9 use crate::dom::TElement;
10 use crate::properties::longhands::display::computed_value::T as Display;
11 use crate::properties::longhands::float::computed_value::T as Float;
12 use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
13 use crate::properties::longhands::position::computed_value::T as Position;
14 use crate::properties::{self, ComputedValues, StyleBuilder};
16 /// A struct that implements all the adjustment methods.
18 /// NOTE(emilio): If new adjustments are introduced that depend on reset
19 /// properties of the parent, you may need tweaking the
20 /// `ChildCascadeRequirement` code in `matching.rs`.
22 /// NOTE(emilio): Also, if new adjustments are introduced that break the
23 /// following invariant:
25 /// Given same tag name, namespace, rules and parent style, two elements would
26 /// end up with exactly the same style.
28 /// Then you need to adjust the lookup_by_rules conditions in the sharing cache.
29 pub struct StyleAdjuster<'a, 'b: 'a> {
30 style: &'a mut StyleBuilder<'b>,
33 #[cfg(feature = "gecko")]
34 fn is_topmost_svg_svg_element<E>(e: E) -> bool
38 debug_assert!(e.is_svg_element());
39 if e.local_name() != &*atom!("svg") {
43 let parent = match e.traversal_parent() {
48 if !parent.is_svg_element() {
52 parent.local_name() == &*atom!("foreignObject")
55 // https://drafts.csswg.org/css-display/#unbox
56 #[cfg(feature = "gecko")]
57 fn is_effective_display_none_for_display_contents<E>(element: E) -> bool
63 const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
82 // https://drafts.csswg.org/css-display/#unbox-svg
84 // There's a note about "Unknown elements", but there's not a good way to
85 // know what that means, or to get that information from here, and no other
86 // UA implements this either.
87 const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
96 // https://drafts.csswg.org/css-display/#unbox-html
97 if element.is_html_element() {
98 let local_name = element.local_name();
99 return SPECIAL_HTML_ELEMENTS
101 .any(|name| &**name == local_name);
104 // https://drafts.csswg.org/css-display/#unbox-svg
105 if element.is_svg_element() {
106 if is_topmost_svg_svg_element(element) {
109 let local_name = element.local_name();
110 return !SPECIAL_SVG_ELEMENTS
112 .any(|name| &**name == local_name);
115 // https://drafts.csswg.org/css-display/#unbox-mathml
117 // We always treat XUL as display: none. We don't use display:
118 // contents in XUL anyway, so should be fine to be consistent with
119 // MathML unless there's a use case for it.
120 if element.is_mathml_element() || element.is_xul_element() {
127 impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
128 /// Trivially constructs a new StyleAdjuster.
130 pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
131 StyleAdjuster { style }
134 /// <https://fullscreen.spec.whatwg.org/#new-stacking-layer>
136 /// Any position value other than 'absolute' and 'fixed' are
137 /// computed to 'absolute' if the element is in a top layer.
139 fn adjust_for_top_layer(&mut self) {
140 if !self.style.in_top_layer() {
143 if !self.style.is_absolutely_positioned() {
144 self.style.mutate_box().set_position(Position::Absolute);
146 if self.style.get_box().clone_display().is_contents() {
147 self.style.mutate_box().set_display(Display::Block);
151 /// CSS 2.1 section 9.7:
153 /// If 'position' has the value 'absolute' or 'fixed', [...] the computed
154 /// value of 'float' is 'none'.
156 fn adjust_for_position(&mut self) {
157 if self.style.is_absolutely_positioned() && self.style.is_floating() {
158 self.style.mutate_box().set_float(Float::None);
162 /// Whether we should skip any item-based display property blockification on
164 fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool
168 if let Some(pseudo) = self.style.pseudo {
169 return pseudo.skip_item_display_fixup();
172 element.map_or(false, |e| e.skip_item_display_fixup())
175 /// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
176 /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>
177 /// A ::marker pseudo-element with 'list-style-position:outside' needs to
178 /// have its 'display' blockified, unless the ::marker is for an inline
179 /// list-item (for which 'list-style-position:outside' behaves as 'inside').
180 /// https://drafts.csswg.org/css-lists-3/#list-style-position-property
181 fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
185 let mut blockify = false;
186 macro_rules! blockify_if {
194 blockify_if!(self.style.is_root_element);
195 if !self.skip_item_display_fixup(element) {
196 let parent_display = layout_parent_style.get_box().clone_display();
197 blockify_if!(parent_display.is_item_container());
200 let is_item_or_root = blockify;
202 blockify_if!(self.style.is_floating());
203 blockify_if!(self.style.is_absolutely_positioned());
209 let display = self.style.get_box().clone_display();
210 let blockified_display = display.equivalent_block_display(self.style.is_root_element);
211 if display != blockified_display {
214 .set_adjusted_display(blockified_display, is_item_or_root);
218 /// Compute a few common flags for both text and element's style.
219 fn set_bits(&mut self) {
220 let display = self.style.get_box().clone_display();
222 if !display.is_contents() {
226 .clone_text_decoration_line()
230 .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
233 if self.style.get_effects().clone_opacity() == 0. {
235 .add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);
239 if self.style.is_pseudo_element() {
241 .add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
244 if self.style.is_root_element {
246 .add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
249 #[cfg(feature = "servo-layout-2013")]
251 if self.style.get_parent_column().is_multicol() {
252 self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
257 /// Adjust the style for text style.
259 /// The adjustments here are a subset of the adjustments generally, because
260 /// text only inherits properties.
262 /// Note that this, for Gecko, comes through Servo_ComputedValues_Inherit.
263 #[cfg(feature = "gecko")]
264 pub fn adjust_for_text(&mut self) {
265 debug_assert!(!self.style.is_root_element);
266 self.adjust_for_text_combine_upright();
267 self.adjust_for_text_in_ruby();
271 /// Change writing mode of the text frame for text-combine-upright.
273 /// It is safe to look at our own style because we are looking at inherited
274 /// properties, and text is just plain inheritance.
276 /// TODO(emilio): we should (Gecko too) revise these adjustments in presence
277 /// of display: contents.
279 /// FIXME(emilio): How does this play with logical properties? Doesn't
280 /// mutating writing-mode change the potential physical sides chosen?
281 #[cfg(feature = "gecko")]
282 fn adjust_for_text_combine_upright(&mut self) {
283 use crate::computed_values::text_combine_upright::T as TextCombineUpright;
284 use crate::computed_values::writing_mode::T as WritingMode;
285 use crate::logical_geometry;
287 let writing_mode = self.style.get_inherited_box().clone_writing_mode();
288 let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
292 WritingMode::VerticalRl | WritingMode::VerticalLr
293 ) && text_combine_upright == TextCombineUpright::All
295 self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
297 .mutate_inherited_box()
298 .set_writing_mode(WritingMode::HorizontalTb);
299 self.style.writing_mode =
300 logical_geometry::WritingMode::new(self.style.get_inherited_box());
304 /// Unconditionally propagates the line break suppression flag to text, and
305 /// additionally it applies it if it is in any ruby box.
307 /// This is necessary because its parent may not itself have the flag set
308 /// (e.g. ruby or ruby containers), thus we may not inherit the flag from
310 #[cfg(feature = "gecko")]
311 fn adjust_for_text_in_ruby(&mut self) {
312 let parent_display = self.style.get_parent_box().clone_display();
313 if parent_display.is_ruby_type() ||
316 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
319 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
323 /// <https://drafts.csswg.org/css-writing-modes-3/#block-flow:>
325 /// If a box has a different writing-mode value than its containing
328 /// - If the box has a specified display of inline, its display
329 /// computes to inline-block. [CSS21]
331 /// This matches the adjustment that Gecko does, not exactly following
332 /// the spec. See also:
334 /// <https://lists.w3.org/Archives/Public/www-style/2017Mar/0045.html>
335 /// <https://github.com/servo/servo/issues/15754>
336 fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {
337 let our_writing_mode = self.style.get_inherited_box().clone_writing_mode();
338 let parent_writing_mode = layout_parent_style.get_inherited_box().clone_writing_mode();
340 if our_writing_mode != parent_writing_mode &&
341 self.style.get_box().clone_display() == Display::Inline
343 // TODO(emilio): Figure out if we can just set the adjusted display
344 // on Gecko too and unify this code path.
345 if cfg!(feature = "servo") {
348 .set_adjusted_display(Display::InlineBlock, false);
350 self.style.mutate_box().set_display(Display::InlineBlock);
355 /// When mathvariant is not "none", font-weight and font-style are
356 /// both forced to "normal".
357 #[cfg(feature = "gecko")]
358 fn adjust_for_mathvariant(&mut self) {
359 use crate::properties::longhands::_moz_math_variant::computed_value::T as MozMathVariant;
360 use crate::properties::longhands::font_weight::computed_value::T as FontWeight;
361 use crate::values::generics::font::FontStyle;
362 if self.style.get_font().clone__moz_math_variant() != MozMathVariant::None {
363 let font_style = self.style.mutate_font();
364 font_style.set_font_weight(FontWeight::normal());
365 font_style.set_font_style(FontStyle::Normal);
369 /// This implements an out-of-date spec. The new spec moves the handling of
370 /// this to layout, which Gecko implements but Servo doesn't.
372 /// See https://github.com/servo/servo/issues/15229
373 #[cfg(feature = "servo")]
374 fn adjust_for_alignment(&mut self, layout_parent_style: &ComputedValues) {
375 use crate::computed_values::align_items::T as AlignItems;
376 use crate::computed_values::align_self::T as AlignSelf;
378 if self.style.get_position().clone_align_self() == AlignSelf::Auto &&
379 !self.style.is_absolutely_positioned()
381 let self_align = match layout_parent_style.get_position().clone_align_items() {
382 AlignItems::Stretch => AlignSelf::Stretch,
383 AlignItems::Baseline => AlignSelf::Baseline,
384 AlignItems::FlexStart => AlignSelf::FlexStart,
385 AlignItems::FlexEnd => AlignSelf::FlexEnd,
386 AlignItems::Center => AlignSelf::Center,
388 self.style.mutate_position().set_align_self(self_align);
392 /// The initial value of border-*-width may be changed at computed value
395 /// This is moved to properties.rs for convenience.
396 fn adjust_for_border_width(&mut self) {
397 properties::adjust_border_width(self.style);
400 /// The initial value of outline-width may be changed at computed value time.
401 fn adjust_for_outline(&mut self) {
405 .clone_outline_style()
407 self.style.get_outline().outline_has_nonzero_width()
411 .set_outline_width(crate::Zero::zero());
415 /// CSS overflow-x and overflow-y require some fixup as well in some cases.
416 /// https://drafts.csswg.org/css-overflow-3/#overflow-properties
417 /// "Computed value: as specified, except with `visible`/`clip` computing to
418 /// `auto`/`hidden` (respectively) if one of `overflow-x` or `overflow-y` is
419 /// neither `visible` nor `clip`."
420 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; // optimization for the common case
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());
434 /// Handles the relevant sections in:
436 /// https://drafts.csswg.org/css-display/#unbox-html
438 /// And forbidding display: contents in pseudo-elements, at least for now.
439 #[cfg(feature = "gecko")]
440 fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)
444 if self.style.get_box().clone_display() != Display::Contents {
448 // FIXME(emilio): ::before and ::after should support display: contents,
450 if self.style.pseudo.is_some() {
451 self.style.mutate_box().set_display(Display::Inline);
455 let element = match element {
460 if is_effective_display_none_for_display_contents(element) {
461 self.style.mutate_box().set_display(Display::None);
465 /// <textarea>'s editor root needs to inherit the overflow value from its
466 /// parent, but we need to make sure it's still scrollable.
467 #[cfg(feature = "gecko")]
468 fn adjust_for_text_control_editing_root(&mut self) {
469 use crate::selector_parser::PseudoElement;
471 if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {
475 let box_style = self.style.get_box();
476 let overflow_x = box_style.clone_overflow_x();
477 let overflow_y = box_style.clone_overflow_y();
479 // If at least one is scrollable we'll adjust the other one in
480 // adjust_for_overflow if needed.
481 if overflow_x.is_scrollable() || overflow_y.is_scrollable() {
485 let box_style = self.style.mutate_box();
486 box_style.set_overflow_x(Overflow::Auto);
487 box_style.set_overflow_y(Overflow::Auto);
490 /// If a <fieldset> has grid/flex display type, we need to inherit
491 /// this type into its ::-moz-fieldset-content anonymous box.
493 /// NOTE(emilio): We don't need to handle the display change for this case
494 /// in matching.rs because anonymous box restyling works separately to the
495 /// normal cascading process.
496 #[cfg(feature = "gecko")]
497 fn adjust_for_fieldset_content(&mut self, layout_parent_style: &ComputedValues) {
498 use crate::selector_parser::PseudoElement;
500 if self.style.pseudo != Some(&PseudoElement::FieldsetContent) {
504 debug_assert_eq!(self.style.get_box().clone_display(), Display::Block);
505 // TODO We actually want style from parent rather than layout
506 // parent, so that this fixup doesn't happen incorrectly when
507 // when <fieldset> has "display: contents".
508 let parent_display = layout_parent_style.get_box().clone_display();
509 let new_display = match parent_display {
510 Display::Flex | Display::InlineFlex => Some(Display::Flex),
511 Display::Grid | Display::InlineGrid => Some(Display::Grid),
514 if let Some(new_display) = new_display {
515 self.style.mutate_box().set_display(new_display);
519 /// -moz-center, -moz-left and -moz-right are used for HTML's alignment.
521 /// This is covering the <div align="right"><table>...</table></div> case.
523 /// In this case, we don't want to inherit the text alignment into the
525 #[cfg(feature = "gecko")]
526 fn adjust_for_table_text_align(&mut self) {
527 use crate::properties::longhands::text_align::computed_value::T as TextAlign;
528 if self.style.get_box().clone_display() != Display::Table {
532 match self.style.get_inherited_text().clone_text_align() {
533 TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},
538 .mutate_inherited_text()
539 .set_text_align(TextAlign::Start)
542 /// Computes the used text decoration for Servo.
544 /// FIXME(emilio): This is a layout tree concept, should move away from
545 /// style, since otherwise we're going to have the same subtle bugs WebKit
546 /// and Blink have with this very same thing.
547 #[cfg(feature = "servo")]
548 fn adjust_for_text_decorations_in_effect(&mut self) {
549 use crate::values::computed::text::TextDecorationsInEffect;
551 let decorations_in_effect = TextDecorationsInEffect::from_style(&self.style);
552 if self.style.get_inherited_text().text_decorations_in_effect != decorations_in_effect {
554 .mutate_inherited_text()
555 .text_decorations_in_effect = decorations_in_effect;
559 #[cfg(feature = "gecko")]
560 fn should_suppress_linebreak<E>(
562 layout_parent_style: &ComputedValues,
568 // Line break suppression should only be propagated to in-flow children.
569 if self.style.is_floating() || self.style.is_absolutely_positioned() {
572 let parent_display = layout_parent_style.get_box().clone_display();
573 if layout_parent_style
575 .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
577 // Line break suppression is propagated to any children of
578 // line participants.
579 if parent_display.is_line_participant() {
583 match self.style.get_box().clone_display() {
584 // Ruby base and text are always non-breakable.
585 Display::RubyBase | Display::RubyText => true,
586 // Ruby base container and text container are breakable.
587 // Non-HTML elements may not form ruby base / text container because
588 // they may not respect ruby-internal display values, so we can't
589 // make them escaped from line break suppression.
590 // Note that, when certain HTML tags, e.g. form controls, have ruby
591 // level container display type, they could also escape from the
592 // line break suppression flag while they shouldn't. However, it is
593 // generally fine as far as they can't break the line inside them.
594 Display::RubyBaseContainer | Display::RubyTextContainer
595 if element.map_or(true, |e| e.is_html_element()) =>
599 // Anything else is non-breakable if and only if its layout parent
600 // has a ruby display type, because any of the ruby boxes can be
602 _ => parent_display.is_ruby_type(),
606 /// Do ruby-related style adjustments, which include:
607 /// * propagate the line break suppression flag,
608 /// * inlinify block descendants,
609 /// * suppress border and padding for ruby level containers,
610 /// * correct unicode-bidi.
611 #[cfg(feature = "gecko")]
612 fn adjust_for_ruby<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
616 use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
618 let self_display = self.style.get_box().clone_display();
619 // Check whether line break should be suppressed for this element.
620 if self.should_suppress_linebreak(layout_parent_style, element) {
622 .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
623 // Inlinify the display type if allowed.
624 if !self.skip_item_display_fixup(element) {
625 let inline_display = self_display.inlinify();
626 if self_display != inline_display {
629 .set_adjusted_display(inline_display, false);
633 // Suppress border and padding for ruby level containers.
634 // This is actually not part of the spec. It is currently unspecified
635 // how border and padding should be handled for ruby level container,
636 // and suppressing them here make it easier for layout to handle.
637 if self_display.is_ruby_level_container() {
638 self.style.reset_border_struct();
639 self.style.reset_padding_struct();
642 // Force bidi isolation on all internal ruby boxes and ruby container
643 // per spec https://drafts.csswg.org/css-ruby-1/#bidi
644 if self_display.is_ruby_type() {
645 let new_value = match self.style.get_text().clone_unicode_bidi() {
646 UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
647 UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
650 if let Some(new_value) = new_value {
651 self.style.mutate_text().set_unicode_bidi(new_value);
656 /// Computes the RELEVANT_LINK_VISITED flag based on the parent style and on
657 /// whether we're a relevant link.
659 /// NOTE(emilio): We don't do this for text styles, which is... dubious, but
660 /// Gecko doesn't seem to do it either. It's extremely easy to do if needed
663 /// FIXME(emilio): This isn't technically a style adjustment thingie, could
664 /// it move somewhere else?
665 fn adjust_for_visited<E>(&mut self, element: Option<E>)
669 if !self.style.has_visited_style() {
673 let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());
675 if !is_link_element {
679 if element.unwrap().is_visited_link() {
681 .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
683 // Need to remove to handle unvisited link inside visited.
685 .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
689 /// Resolves "justify-items: legacy" based on the inherited style if needed
692 /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>
693 #[cfg(feature = "gecko")]
694 fn adjust_for_justify_items(&mut self) {
695 use crate::values::specified::align;
696 let justify_items = self.style.get_position().clone_justify_items();
697 if justify_items.specified.0 != align::AlignFlags::LEGACY {
701 let parent_justify_items = self.style.get_parent_position().clone_justify_items();
703 if !parent_justify_items
706 .contains(align::AlignFlags::LEGACY)
711 if parent_justify_items.computed == justify_items.computed {
717 .set_computed_justify_items(parent_justify_items.computed);
720 /// If '-webkit-appearance' is 'menulist' on a <select> element then
721 /// the computed value of 'line-height' is 'normal'.
723 /// https://github.com/w3c/csswg-drafts/issues/3257
724 #[cfg(feature = "gecko")]
725 fn adjust_for_appearance<E>(&mut self, element: Option<E>)
729 use crate::properties::longhands::appearance::computed_value::T as Appearance;
730 use crate::properties::longhands::line_height::computed_value::T as LineHeight;
732 let box_ = self.style.get_box();
733 let appearance = match box_.clone_appearance() {
734 Appearance::Auto => box_.clone__moz_default_appearance(),
738 if appearance == Appearance::Menulist {
739 if self.style.get_inherited_text().clone_line_height() == LineHeight::normal() {
742 if self.style.pseudo.is_some() {
745 let is_html_select_element = element.map_or(false, |e| {
746 e.is_html_element() && e.local_name() == &*atom!("select")
748 if !is_html_select_element {
752 .mutate_inherited_text()
753 .set_line_height(LineHeight::normal());
757 /// A legacy ::marker (i.e. no 'content') without an author-specified 'font-family'
758 /// and 'list-style-type:disc|circle|square|disclosure-closed|disclosure-open'
759 /// is assigned 'font-family:-moz-bullet-font'. (This is for <ul><li> etc.)
760 /// We don't want synthesized italic/bold for this font, so turn that off too.
761 /// Likewise for 'letter/word-spacing' -- unless the author specified it then reset
762 /// them to their initial value because traditionally we never added such spacing
763 /// between a legacy bullet and the list item's content, so we keep that behavior
764 /// for web-compat reasons.
765 /// We intentionally don't check 'list-style-image' below since we want it to use
766 /// the same font as its fallback ('list-style-type') in case it fails to load.
767 #[cfg(feature = "gecko")]
768 fn adjust_for_marker_pseudo(&mut self) {
769 use crate::values::computed::counters::Content;
770 use crate::values::computed::font::{FontFamily, FontSynthesis};
771 use crate::values::computed::text::{LetterSpacing, WordSpacing};
773 let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker()) &&
774 self.style.get_list().clone_list_style_type().is_bullet() &&
775 self.style.get_counters().clone_content() == Content::Normal;
776 if !is_legacy_marker {
783 .contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY)
787 .set_font_family(FontFamily::moz_bullet().clone());
789 // FIXME(mats): We can remove this if support for font-synthesis is added to @font-face rules.
790 // Then we can add it to the @font-face rule in html.css instead.
791 // https://github.com/w3c/csswg-drafts/issues/6081
796 .contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS)
800 .set_font_synthesis(FontSynthesis::none());
807 .contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING)
810 .mutate_inherited_text()
811 .set_letter_spacing(LetterSpacing::normal());
817 .contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING)
820 .mutate_inherited_text()
821 .set_word_spacing(WordSpacing::normal());
825 /// Adjusts the style to account for various fixups that don't fit naturally
826 /// into the cascade.
828 /// When comparing to Gecko, this is similar to the work done by
829 /// `ComputedStyle::ApplyStyleFixups`, plus some parts of
830 /// `nsStyleSet::GetContext`.
831 pub fn adjust<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
835 if cfg!(debug_assertions) {
836 if element.map_or(false, |e| e.is_pseudo_element()) {
837 // It'd be nice to assert `self.style.pseudo == Some(&pseudo)`,
838 // but we do resolve ::-moz-list pseudos on ::before / ::after
840 debug_assert!(self.style.pseudo.is_some(), "Someone really messed up");
843 // FIXME(emilio): The apply_declarations callsite in Servo's
844 // animation, and the font stuff for Gecko
845 // (Stylist::compute_for_declarations) should pass an element to
846 // cascade(), then we can make this assertion hold everywhere.
848 // element.is_some() || self.style.pseudo.is_some(),
849 // "Should always have an element around for non-pseudo styles"
852 self.adjust_for_visited(element);
853 #[cfg(feature = "gecko")]
855 self.adjust_for_prohibited_display_contents(element);
856 self.adjust_for_fieldset_content(layout_parent_style);
857 // NOTE: It's important that this happens before
858 // adjust_for_overflow.
859 self.adjust_for_text_control_editing_root();
861 self.adjust_for_top_layer();
862 self.blockify_if_necessary(layout_parent_style, element);
863 self.adjust_for_position();
864 self.adjust_for_overflow();
865 #[cfg(feature = "gecko")]
867 self.adjust_for_table_text_align();
868 self.adjust_for_mathvariant();
869 self.adjust_for_justify_items();
871 #[cfg(feature = "servo")]
873 self.adjust_for_alignment(layout_parent_style);
875 self.adjust_for_border_width();
876 self.adjust_for_outline();
877 self.adjust_for_writing_mode(layout_parent_style);
878 #[cfg(feature = "gecko")]
880 self.adjust_for_ruby(layout_parent_style, element);
882 #[cfg(feature = "servo")]
884 self.adjust_for_text_decorations_in_effect();
886 #[cfg(feature = "gecko")]
888 self.adjust_for_appearance(element);
889 self.adjust_for_marker_pseudo();