Add style adjustments for ruby.

This commit is contained in:
Xidorn Quan 2017-07-14 10:34:08 +10:00
parent f08c432b57
commit 6a12de1c1a
7 changed files with 158 additions and 8 deletions

View file

@ -301,7 +301,6 @@ trait PrivateMatchMethods: TElement {
new_values: &Arc<ComputedValues>,
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_LINES) !=
new_values.flags.contains(HAS_TEXT_DECORATION_LINES) {
// correct propagation of computed value flags.
if old_values.flags != new_values.flags {
return ChildCascadeRequirement::MustCascadeChildren;
}
ChildCascadeRequirement::CanSkipCascade

View file

@ -18,5 +18,15 @@ bitflags! {
/// text-decoration-line is a reset property, but gets propagated in the
/// frame/box tree.
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, <br> 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,
}
}

View file

@ -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,
}
}
}
}

View file

@ -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"

View file

@ -2486,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.
@ -2597,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.
@ -2985,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":

View file

@ -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.
@ -321,6 +323,83 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
}
}
#[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.
///
@ -328,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")]
{
@ -351,5 +431,10 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
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);
}
}
}