diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index 4f5693e3b9f..ab9e1f064e1 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -2663,7 +2663,6 @@ extern "C" { pub fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null: ServoComputedValuesBorrowedOrNull, pseudo_tag: *mut nsIAtom, - skip_display_fixup: bool, set: RawServoStyleSetBorrowed) -> ServoComputedValuesStrong; @@ -2680,6 +2679,11 @@ extern "C" { ServoComputedValuesBorrowed) -> ServoComputedValuesStrong; } +extern "C" { + pub fn Servo_ComputedValues_GetStyleBits(values: + ServoComputedValuesBorrowed) + -> u64; +} extern "C" { pub fn Servo_ComputedValues_GetStyleRuleList(values: ServoComputedValuesBorrowed, diff --git a/components/style/gecko/regen_atoms.py b/components/style/gecko/regen_atoms.py index 9c8545bf8db..bdc59748b0c 100755 --- a/components/style/gecko/regen_atoms.py +++ b/components/style/gecko/regen_atoms.py @@ -55,7 +55,7 @@ class CSSPseudoElementsAtomSource: class CSSAnonBoxesAtomSource: - PATTERN = re.compile('^(?:CSS_ANON_BOX|CSS_NON_INHERITING_ANON_BOX)\((.+),\s*"(.*)"(\,|\))') + PATTERN = re.compile('^(?:CSS_ANON_BOX|CSS_NON_INHERITING_ANON_BOX)\((.+),\s*"(.*)"\)') FILE = "include/nsCSSAnonBoxList.h" CLASS = "nsCSSAnonBoxes" TYPE = "nsICSSAnonBoxPseudo" diff --git a/components/style/matching.rs b/components/style/matching.rs index c8b6b26d6ae..b720c5de04a 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -301,7 +301,6 @@ trait PrivateMatchMethods: TElement { new_values: &Arc, pseudo: Option<&PseudoElement>) -> ChildCascadeRequirement { - use properties::computed_value_flags::*; // Don't accumulate damage if we're in a restyle for reconstruction. if shared_context.traversal_flags.for_reconstruct() { return ChildCascadeRequirement::MustCascadeChildren; @@ -327,10 +326,8 @@ trait PrivateMatchMethods: TElement { match difference.change { StyleChange::Unchanged => { // We need to cascade the children in order to ensure the - // correct propagation of text-decoration-line, which is a reset - // property. - if old_values.flags.contains(HAS_TEXT_DECORATION_LINE) != - new_values.flags.contains(HAS_TEXT_DECORATION_LINE) { + // correct propagation of computed value flags. + if old_values.flags != new_values.flags { return ChildCascadeRequirement::MustCascadeChildren; } ChildCascadeRequirement::CanSkipCascade diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index 8e3dc090b77..50ad887417a 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -4,42 +4,29 @@ //! Misc information about a given computed style. -use properties::{ComputedValues, StyleBuilder}; - bitflags! { /// Misc information about a given computed style. + /// + /// All flags are currently inherited for text, pseudo elements, and + /// anonymous boxes, see StyleBuilder::for_inheritance and its callsites. + /// If we ever want to add some flags that shouldn't inherit for them, + /// we might want to add a function to handle this. pub flags ComputedValueFlags: u8 { - /// Whether the style or any of the ancestors has a text-decoration + /// Whether the style or any of the ancestors has a text-decoration-line /// property that should get propagated to descendants. /// - /// text-decoration is a reset property, but gets propagated in the + /// text-decoration-line is a reset property, but gets propagated in the /// frame/box tree. - const HAS_TEXT_DECORATION_LINE = 1 << 0, - } -} - -impl ComputedValueFlags { - /// Get the computed value flags for the initial style. - pub fn initial() -> Self { - Self::empty() - } - - /// Compute the flags for this style, given the parent style. - pub fn compute( - this_style: &StyleBuilder, - parent_style: &ComputedValues, - ) -> Self { - let mut ret = Self::empty(); - - // FIXME(emilio): This feels like it wants to look at the - // layout_parent_style, but the way it works in Gecko means it's not - // needed (we'd recascade a bit more when it changes, but that's fine), - // and this way it simplifies the code for text styles and similar. - if parent_style.flags.contains(HAS_TEXT_DECORATION_LINE) || - !this_style.get_text().clone_text_decoration_line().is_empty() { - ret.insert(HAS_TEXT_DECORATION_LINE); - } - - ret + const HAS_TEXT_DECORATION_LINES = 1 << 0, + + /// Whether line break inside should be suppressed. + /// + /// If this flag is set, the line should not be broken inside, + /// which means inlines act as if nowrap is set,
element is + /// suppressed, and blocks are inlinized. + /// + /// This bit is propagated to all children of line participants. + /// It is currently used by ruby to make its content unbreakable. + const SHOULD_SUPPRESS_LINEBREAK = 1 << 1, } } diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 53986e486cb..938cac51c84 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -125,7 +125,7 @@ impl ComputedValues { custom_properties: None, writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious font_computation_data: FontComputationData::default_values(), - flags: ComputedValueFlags::initial(), + flags: ComputedValueFlags::empty(), rules: None, visited_style: None, % for style_struct in data.style_structs: diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index 02122a2fc70..47d45d824cd 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -53,6 +53,34 @@ ) } + /// Returns whether an element with this display type is a line + /// participant, which means it may lay its children on the same + /// line as itself. + pub fn is_line_participant(&self) -> bool { + matches!(*self, + T::inline + % if product == "gecko": + | T::contents + | T::ruby + | T::ruby_base_container + % endif + ) + } + + /// Returns whether this "display" value is one of the types for + /// ruby. + #[cfg(feature = "gecko")] + pub fn is_ruby_type(&self) -> bool { + matches!(*self, T::ruby | T::ruby_base | T::ruby_text | + T::ruby_base_container | T::ruby_text_container) + } + + /// Returns whether this "display" value is a ruby level container. + #[cfg(feature = "gecko")] + pub fn is_ruby_level_container(&self) -> bool { + matches!(*self, T::ruby_base_container | T::ruby_text_container) + } + /// Convert this display into an equivalent block display. /// /// Also used for style adjustments. @@ -82,6 +110,25 @@ // Everything else becomes block. _ => T::block, } + + } + + /// Convert this display into an inline-outside display. + /// + /// Ideally it should implement spec: https://drafts.csswg.org/css-display/#inlinify + /// but the spec isn't stable enough, so we copy what Gecko does for now. + #[cfg(feature = "gecko")] + pub fn inlinify(&self) -> Self { + match *self { + T::block | T::flow_root => T::inline_block, + T::table => T::inline_table, + T::flex => T::inline_flex, + T::grid => T::inline_grid, + T::_moz_box => T::_moz_inline_box, + T::_moz_stack => T::_moz_inline_stack, + T::_webkit_box => T::_webkit_inline_box, + other => other, + } } } } diff --git a/components/style/properties/longhand/text.mako.rs b/components/style/properties/longhand/text.mako.rs index a7b30aafa5e..3be23f1681e 100644 --- a/components/style/properties/longhand/text.mako.rs +++ b/components/style/properties/longhand/text.mako.rs @@ -135,6 +135,7 @@ ${helpers.single_keyword("unicode-bidi", "normal embed isolate bidi-override isolate-override plaintext", animation_value_type="discrete", + need_clone="True", spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi")} <%helpers:longhand name="text-decoration-line" diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 25c6de05f11..9d710c1dcb3 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2384,8 +2384,6 @@ impl<'a, T: 'a> Deref for StyleStructRef<'a, T> { /// actually cloning them, until we either build the style, or mutate the /// inherited value. pub struct StyleBuilder<'a> { - /// The style we're inheriting from. - inherited_style: &'a ComputedValues, /// The rule node representing the ordered list of rules matched for this /// node. rules: Option, @@ -2396,6 +2394,8 @@ pub struct StyleBuilder<'a> { pub writing_mode: WritingMode, /// The keyword behind the current font-size property, if any. pub font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>, + /// Flags for the computed value. + pub flags: ComputedValueFlags, /// The element's style if visited, only computed if there's a relevant link /// for this element. A element's "relevant link" is the element being /// matched if it is a link or the nearest ancestor link. @@ -2414,14 +2414,15 @@ impl<'a> StyleBuilder<'a> { custom_properties: Option>, writing_mode: WritingMode, font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>, + flags: ComputedValueFlags, visited_style: Option>, ) -> Self { StyleBuilder { - inherited_style, rules, custom_properties, writing_mode, font_size_keyword, + flags, visited_style, % for style_struct in data.active_style_structs(): % if style_struct.inherited: @@ -2452,6 +2453,7 @@ impl<'a> StyleBuilder<'a> { parent.custom_properties(), parent.writing_mode, parent.font_computation_data.font_size_keyword, + parent.flags, parent.clone_visited_style() ) } @@ -2484,6 +2486,12 @@ impl<'a> StyleBuilder<'a> { -> Option<<&mut style_structs::${style_struct.name}> { self.${style_struct.ident}.get_if_mutated() } + + /// Reset the current `${style_struct.name}` style to its default value. + pub fn reset_${style_struct.name_lower}(&mut self, default: &'a ComputedValues) { + self.${style_struct.ident} = + StyleStructRef::Borrowed(default.${style_struct.name_lower}_arc()); + } % endfor /// Returns whether this computed style represents a floated object. @@ -2515,11 +2523,10 @@ impl<'a> StyleBuilder<'a> { /// Turns this `StyleBuilder` into a proper `ComputedValues` instance. pub fn build(self) -> ComputedValues { - let flags = ComputedValueFlags::compute(&self, &self.inherited_style); ComputedValues::new(self.custom_properties, self.writing_mode, self.font_size_keyword, - flags, + self.flags, self.rules, self.visited_style, % for style_struct in data.active_style_structs(): @@ -2564,7 +2571,7 @@ mod lazy_static_module { % endfor custom_properties: None, writing_mode: WritingMode::empty(), - flags: ComputedValueFlags::initial(), + flags: ComputedValueFlags::empty(), font_computation_data: FontComputationData::default_values(), rules: None, visited_style: None, @@ -2596,8 +2603,8 @@ bitflags! { /// present, non-inherited styles are reset to their initial values. const INHERIT_ALL = 0x01, - /// Whether to skip any root element and flex/grid item display style - /// fixup. + /// Whether to skip any display style fixup for root element, flex/grid + /// item, and ruby descendants. const SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP = 0x02, /// Whether to only cascade properties that are visited dependent. @@ -2747,6 +2754,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device, custom_properties, WritingMode::empty(), inherited_style.font_computation_data.font_size_keyword, + ComputedValueFlags::empty(), visited_style, ), font_metrics_provider: font_metrics_provider, @@ -2983,7 +2991,8 @@ pub fn apply_declarations<'a, F, I>(device: &Device, { StyleAdjuster::new(&mut style) - .adjust(context.layout_parent_style, flags); + .adjust(context.layout_parent_style, + context.device.default_computed_values(), flags); } % if product == "gecko": diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index a2c692455af..5b60231b2c4 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -//! A struct to encapsulate all the style fixups a computed style needs in order -//! for it to adhere to the CSS spec. +//! A struct to encapsulate all the style fixups and flags propagations +//! a computed style needs in order for it to adhere to the CSS spec. use app_units::Au; use properties::{self, CascadeFlags, ComputedValues}; @@ -12,6 +12,8 @@ use properties::longhands::display::computed_value::T as display; use properties::longhands::float::computed_value::T as float; use properties::longhands::overflow_x::computed_value::T as overflow; use properties::longhands::position::computed_value::T as position; +#[cfg(feature = "gecko")] +use properties::longhands::unicode_bidi::computed_value::T as unicode_bidi; /// An unsized struct that implements all the adjustment methods. @@ -312,6 +314,92 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { self.style.mutate_inheritedtext().set_text_align(text_align::start); } + /// Set the HAS_TEXT_DECORATION_LINES flag based on parent style. + fn adjust_for_text_decoration_lines(&mut self, layout_parent_style: &ComputedValues) { + use properties::computed_value_flags::HAS_TEXT_DECORATION_LINES; + if layout_parent_style.flags.contains(HAS_TEXT_DECORATION_LINES) || + !self.style.get_text().clone_text_decoration_line().is_empty() { + self.style.flags.insert(HAS_TEXT_DECORATION_LINES); + } + } + + #[cfg(feature = "gecko")] + fn should_suppress_linebreak(&self, layout_parent_style: &ComputedValues) -> bool { + use properties::computed_value_flags::SHOULD_SUPPRESS_LINEBREAK; + // Line break suppression should only be propagated to in-flow children. + if self.style.floated() || self.style.out_of_flow_positioned() { + return false; + } + let parent_display = layout_parent_style.get_box().clone_display(); + if layout_parent_style.flags.contains(SHOULD_SUPPRESS_LINEBREAK) { + // Line break suppression is propagated to any children of + // line participants. + if parent_display.is_line_participant() { + return true; + } + } + match self.style.get_box().clone_display() { + // Ruby base and text are always non-breakable. + display::ruby_base | display::ruby_text => true, + // Ruby base container and text container are breakable. + // Note that, when certain HTML tags, e.g. form controls, have ruby + // level container display type, they could also escape from the + // line break suppression flag while they shouldn't. However, it is + // generally fine since they themselves are non-breakable. + display::ruby_base_container | display::ruby_text_container => false, + // Anything else is non-breakable if and only if its layout parent + // has a ruby display type, because any of the ruby boxes can be + // anonymous. + _ => parent_display.is_ruby_type(), + } + } + + /// Do ruby-related style adjustments, which include: + /// * propagate the line break suppression flag, + /// * inlinify block descendants, + /// * suppress border and padding for ruby level containers, + /// * correct unicode-bidi. + #[cfg(feature = "gecko")] + fn adjust_for_ruby(&mut self, + layout_parent_style: &ComputedValues, + default_computed_values: &'b ComputedValues, + flags: CascadeFlags) { + use properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP; + use properties::computed_value_flags::SHOULD_SUPPRESS_LINEBREAK; + let self_display = self.style.get_box().clone_display(); + // Check whether line break should be suppressed for this element. + if self.should_suppress_linebreak(layout_parent_style) { + self.style.flags.insert(SHOULD_SUPPRESS_LINEBREAK); + // Inlinify the display type if allowed. + if !flags.contains(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP) { + let inline_display = self_display.inlinify(); + if self_display != inline_display { + self.style.mutate_box().set_display(inline_display); + } + } + } + // Suppress border and padding for ruby level containers. + // This is actually not part of the spec. It is currently unspecified + // how border and padding should be handled for ruby level container, + // and suppressing them here make it easier for layout to handle. + if self_display.is_ruby_level_container() { + self.style.reset_border(default_computed_values); + self.style.reset_padding(default_computed_values); + } + // Force bidi isolation on all internal ruby boxes and ruby container + // per spec https://drafts.csswg.org/css-ruby-1/#bidi + if self_display.is_ruby_type() { + let new_value = match self.style.get_text().clone_unicode_bidi() { + unicode_bidi::normal | unicode_bidi::embed => Some(unicode_bidi::isolate), + unicode_bidi::bidi_override => Some(unicode_bidi::isolate_override), + _ => None, + }; + if let Some(new_value) = new_value { + self.style.mutate_text().set_unicode_bidi(new_value); + } + } + } + /// Adjusts the style to account for various fixups that don't fit naturally /// into the cascade. /// @@ -319,6 +407,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { /// `nsStyleContext::ApplyStyleFixups`. pub fn adjust(&mut self, layout_parent_style: &ComputedValues, + _default_computed_values: &'b ComputedValues, flags: CascadeFlags) { #[cfg(feature = "gecko")] { @@ -341,5 +430,11 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { self.adjust_for_border_width(); self.adjust_for_outline(); self.adjust_for_writing_mode(layout_parent_style); + self.adjust_for_text_decoration_lines(layout_parent_style); + #[cfg(feature = "gecko")] + { + self.adjust_for_ruby(layout_parent_style, + _default_computed_values, flags); + } } } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index b18b03f25d3..18c396d724e 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -90,7 +90,7 @@ use style::invalidation::element::restyle_hints::{self, RestyleHint}; use style::media_queries::{MediaList, parse_media_query_list}; use style::parallel; use style::parser::ParserContext; -use style::properties::{CascadeFlags, ComputedValues, Importance, SourcePropertyDeclaration}; +use style::properties::{ComputedValues, Importance, SourcePropertyDeclaration}; use style::properties::{LonghandIdSet, PropertyDeclaration, PropertyDeclarationBlock, PropertyId, StyleBuilder}; use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP; use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue}; @@ -1502,7 +1502,6 @@ pub extern "C" fn Servo_DocumentRule_GetConditionText(rule: RawServoDocumentRule #[no_mangle] pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null: ServoComputedValuesBorrowedOrNull, pseudo_tag: *mut nsIAtom, - skip_display_fixup: bool, raw_data: RawServoStyleSetBorrowed) -> ServoComputedValuesStrong { let global_style_data = &*GLOBAL_STYLE_DATA; @@ -1515,10 +1514,7 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null: let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null); - let mut cascade_flags = CascadeFlags::empty(); - if skip_display_fixup { - cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP); - } + let cascade_flags = SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP; let metrics = get_metrics_provider_for_product(); data.stylist.precomputed_values_for_pseudo(&guards, &pseudo, maybe_parent, cascade_flags, &metrics) @@ -1723,6 +1719,20 @@ pub extern "C" fn Servo_ComputedValues_GetVisitedStyle(values: ServoComputedValu } } +#[no_mangle] +pub extern "C" fn Servo_ComputedValues_GetStyleBits(values: ServoComputedValuesBorrowed) -> u64 { + use style::properties::computed_value_flags::*; + let flags = ComputedValues::as_arc(&values).flags; + let mut result = 0; + if flags.contains(HAS_TEXT_DECORATION_LINES) { + result |= structs::NS_STYLE_HAS_TEXT_DECORATION_LINES as u64; + } + if flags.contains(SHOULD_SUPPRESS_LINEBREAK) { + result |= structs::NS_STYLE_SUPPRESS_LINEBREAK as u64; + } + result +} + #[no_mangle] pub extern "C" fn Servo_ComputedValues_SpecifiesAnimationsOrTransitions(values: ServoComputedValuesBorrowed) -> bool {