Bug 1359603 - Port text-combine-upright writing mode fixup to Servo. r=heycam
Ports the Gecko fixup for text-combine-upright writing mode to Servo. In
addition, this passes the current pseudo element (if any) down to the cascade
for use during the fixup process.
MozReview-Commit-ID: BkHd4AvSsOt
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -436,16 +436,17 @@ fn compute_style_for_animation_step(cont
let iter = || {
guard.declarations().iter().rev().map(|&(ref decl, _importance)| decl)
};
let computed =
properties::apply_declarations(&context.stylist.device,
/* is_root = */ false,
+ /* pseudo = */ None,
iter,
previous_style,
previous_style,
/* cascade_info = */ None,
&*context.error_reporter,
font_metrics_provider,
CascadeFlags::empty());
computed
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -381,28 +381,28 @@ trait PrivateMatchMethods: TElement {
}
}
fn cascade_with_rules(&self,
shared_context: &SharedStyleContext,
font_metrics_provider: &FontMetricsProvider,
rule_node: &StrongRuleNode,
primary_style: &ComputedStyle,
- is_pseudo: bool)
+ pseudo: Option<&PseudoElement>)
-> Arc<ComputedValues> {
let mut cascade_info = CascadeInfo::new();
let mut cascade_flags = CascadeFlags::empty();
if self.skip_root_and_item_based_display_fixup() {
cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
}
// Grab the inherited values.
let parent_el;
let parent_data;
- let style_to_inherit_from = if !is_pseudo {
+ let style_to_inherit_from = if pseudo.is_none() {
parent_el = self.parent_element();
parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
let parent_values = parent_data.as_ref().map(|d| {
// Sometimes Gecko eagerly styles things without processing
// pending restyles first. In general we'd like to avoid this,
// but there can be good reasons (for example, needing to
// construct a frame for some small piece of newly-added
// content in order to do something specific with that frame,
@@ -429,54 +429,56 @@ trait PrivateMatchMethods: TElement {
let style_to_inherit_from = style_to_inherit_from.map(|x| &**x);
let layout_parent_style = layout_parent_style.map(|x| &**x);
// Propagate the "can be fragmented" bit. It would be nice to
// encapsulate this better.
//
// Note that this is not needed for pseudos since we already do that
// when we resolve the non-pseudo style.
- if !is_pseudo {
+ if pseudo.is_none() {
if let Some(ref p) = layout_parent_style {
let can_be_fragmented =
p.is_multicol() ||
layout_parent_el.as_ref().unwrap().as_node().can_be_fragmented();
unsafe { self.as_node().set_can_be_fragmented(can_be_fragmented); }
}
}
// Invoke the cascade algorithm.
let values =
Arc::new(cascade(&shared_context.stylist.device,
rule_node,
+ pseudo,
&shared_context.guards,
style_to_inherit_from,
layout_parent_style,
Some(&mut cascade_info),
&*shared_context.error_reporter,
font_metrics_provider,
cascade_flags));
cascade_info.finish(&self.as_node());
values
}
fn cascade_internal(&self,
context: &StyleContext<Self>,
primary_style: &ComputedStyle,
+ pseudo: Option<&PseudoElement>,
pseudo_style: Option<&ComputedStyle>)
-> Arc<ComputedValues> {
// Grab the rule node.
let rule_node = &pseudo_style.unwrap_or(primary_style).rules;
self.cascade_with_rules(context.shared,
&context.thread_local.font_metrics_provider,
rule_node,
primary_style,
- pseudo_style.is_some())
+ pseudo)
}
/// Computes values and damage for the primary or pseudo style of an element,
/// setting them on the ElementData.
fn cascade_primary_or_pseudo(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
pseudo: Option<&PseudoElement>,
@@ -497,16 +499,17 @@ trait PrivateMatchMethods: TElement {
let mut old_values = match pseudo_style {
Some(ref mut s) => s.values.take(),
None => primary_style.values.take(),
};
// Compute the new values.
let mut new_values = self.cascade_internal(context,
primary_style,
+ pseudo,
pseudo_style.as_ref().map(|s| &**s));
// Handle animations.
if animate && !context.shared.traversal_flags.for_animation_only() {
self.process_animations(context,
&mut old_values,
&mut new_values,
primary_style,
@@ -529,33 +532,34 @@ trait PrivateMatchMethods: TElement {
}
/// get_after_change_style removes the transition rules from the ComputedValues.
/// If there is no transition rule in the ComputedValues, it returns None.
#[cfg(feature = "gecko")]
fn get_after_change_style(&self,
context: &mut StyleContext<Self>,
primary_style: &ComputedStyle,
+ pseudo: Option<&PseudoElement>,
pseudo_style: Option<&ComputedStyle>)
-> Option<Arc<ComputedValues>> {
let relevant_style = pseudo_style.unwrap_or(primary_style);
let rule_node = &relevant_style.rules;
let without_transition_rules =
context.shared.stylist.rule_tree.remove_transition_rule_if_applicable(rule_node);
if without_transition_rules == *rule_node {
// We don't have transition rule in this case, so return None to let the caller
// use the original ComputedValues.
return None;
}
Some(self.cascade_with_rules(context.shared,
&context.thread_local.font_metrics_provider,
&without_transition_rules,
primary_style,
- pseudo_style.is_some()))
+ pseudo))
}
#[cfg(feature = "gecko")]
fn needs_animations_update(&self,
old_values: &Option<Arc<ComputedValues>>,
new_values: &Arc<ComputedValues>,
pseudo: Option<&PseudoElement>) -> bool {
let ref new_box_style = new_values.get_box();
@@ -595,17 +599,17 @@ trait PrivateMatchMethods: TElement {
if self.needs_animations_update(old_values, new_values, pseudo) {
tasks.insert(CSS_ANIMATIONS);
}
let before_change_style = if self.might_need_transitions_update(&old_values.as_ref(),
new_values,
pseudo) {
let after_change_style = if self.has_css_transitions(pseudo) {
- self.get_after_change_style(context, primary_style, pseudo_style)
+ self.get_after_change_style(context, primary_style, pseudo, pseudo_style)
} else {
None
};
// In order to avoid creating a SequentialTask for transitions which may not be updated,
// we check it per property to make sure Gecko side will really update transition.
let needs_transitions_update = {
// We borrow new_values here, so need to add a scope to make sure we release it
@@ -1320,30 +1324,31 @@ pub trait MatchMethods : TElement {
}
}
/// Returns computed values without animation and transition rules.
fn get_base_style(&self,
shared_context: &SharedStyleContext,
font_metrics_provider: &FontMetricsProvider,
primary_style: &ComputedStyle,
+ pseudo: Option<&PseudoElement>,
pseudo_style: Option<&ComputedStyle>)
-> Arc<ComputedValues> {
let relevant_style = pseudo_style.unwrap_or(primary_style);
let rule_node = &relevant_style.rules;
let without_animation_rules =
shared_context.stylist.rule_tree.remove_animation_and_transition_rules(rule_node);
if without_animation_rules == *rule_node {
// Note that unwrapping here is fine, because the style is
// only incomplete during the styling process.
return relevant_style.values.as_ref().unwrap().clone();
}
self.cascade_with_rules(shared_context,
font_metrics_provider,
&without_animation_rules,
primary_style,
- pseudo_style.is_some())
+ pseudo)
}
}
impl<E: TElement> MatchMethods for E {}
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -1252,17 +1252,17 @@
${helpers.single_keyword("ruby-position", "over under",
products="gecko", animation_value_type="none",
spec="https://drafts.csswg.org/css-ruby/#ruby-position-property")}
// CSS Writing Modes Module Level 3
// https://drafts.csswg.org/css-writing-modes-3/
${helpers.single_keyword("text-combine-upright", "none all",
- products="gecko", animation_value_type="none",
+ products="gecko", animation_value_type="none", need_clone=True,
spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright")}
// SVG 1.1: Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("text-rendering",
"auto optimizespeed optimizelegibility geometricprecision",
animation_value_type="none",
spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty")}
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -24,16 +24,17 @@ use computed_values;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")] use gecko_bindings::bindings;
#[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
#[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide};
use logical_geometry::WritingMode;
use media_queries::Device;
use parser::{LengthParsingMode, Parse, ParserContext};
use properties::animated_properties::TransitionProperty;
+use selector_parser::PseudoElement;
#[cfg(feature = "servo")] use servo_config::prefs::PREFS;
use shared_lock::StylesheetGuards;
use style_traits::ToCss;
use stylesheets::{CssRuleType, Origin, UrlExtraData};
#[cfg(feature = "servo")] use values::Either;
use values::{HasViewportPercentage, computed};
use cascade_info::CascadeInfo;
use rule_tree::StrongRuleNode;
@@ -2103,16 +2104,17 @@ bitflags! {
///
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
///
/// Returns the computed values.
/// * `flags`: Various flags.
///
pub fn cascade(device: &Device,
rule_node: &StrongRuleNode,
+ pseudo: Option<<&PseudoElement>,
guards: &StylesheetGuards,
parent_style: Option<<&ComputedValues>,
layout_parent_style: Option<<&ComputedValues>,
cascade_info: Option<<&mut CascadeInfo>,
error_reporter: &ParseErrorReporter,
font_metrics_provider: &FontMetricsProvider,
flags: CascadeFlags)
-> ComputedValues {
@@ -2148,30 +2150,32 @@ pub fn cascade(device: &Device,
} else {
None
}
})
})
};
apply_declarations(device,
is_root_element,
+ pseudo,
iter_declarations,
inherited_style,
layout_parent_style,
cascade_info,
error_reporter,
font_metrics_provider,
flags)
}
/// NOTE: This function expects the declaration with more priority to appear
/// first.
#[allow(unused_mut)] // conditionally compiled code for "position"
pub fn apply_declarations<'a, F, I>(device: &Device,
is_root_element: bool,
+ pseudo: Option<<&PseudoElement>,
iter_declarations: F,
inherited_style: &ComputedValues,
layout_parent_style: &ComputedValues,
mut cascade_info: Option<<&mut CascadeInfo>,
error_reporter: &ParseErrorReporter,
font_metrics_provider: &FontMetricsProvider,
flags: CascadeFlags)
-> ComputedValues
@@ -2368,17 +2372,17 @@ pub fn apply_declarations<'a, F, I>(devi
&mut cascade_info,
error_reporter);
}
% endif
% endfor
let mut style = context.style;
- StyleAdjuster::new(&mut style, is_root_element)
+ StyleAdjuster::new(&mut style, is_root_element, pseudo)
.adjust(context.layout_parent_style,
flags.contains(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP));
% if product == "gecko":
// FIXME(emilio): This is effectively creating a new nsStyleBackground
// and nsStyleSVG per element. We should only do this when necessary
// using the `seen` bitfield!
style.mutate_background().fill_arrays();
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -6,30 +6,33 @@
//! for it to adhere to the CSS spec.
use app_units::Au;
use properties::{self, ComputedValues};
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;
-
+use selector_parser::PseudoElement;
/// An unsized struct that implements all the adjustment methods.
+#[allow(dead_code)] // `pseudo` field is currently unused by Servo
pub struct StyleAdjuster<'a> {
style: &'a mut ComputedValues,
is_root_element: bool,
+ pseudo: Option<&'a PseudoElement>,
}
impl<'a> StyleAdjuster<'a> {
/// Trivially constructs a new StyleAdjuster.
- pub fn new(style: &'a mut ComputedValues, is_root_element: bool) -> Self {
+ pub fn new(style: &'a mut ComputedValues, is_root_element: bool, pseudo: Option<&'a PseudoElement>) -> Self {
StyleAdjuster {
style: style,
is_root_element: is_root_element,
+ pseudo: pseudo,
}
}
/// https://fullscreen.spec.whatwg.org/#new-stacking-layer
///
/// Any position value other than 'absolute' and 'fixed' are
/// computed to 'absolute' if the element is in a top layer.
///
@@ -80,16 +83,36 @@ impl<'a> StyleAdjuster<'a> {
let blockified_display =
display.equivalent_block_display(self.is_root_element);
if display != blockified_display {
self.style.mutate_box().set_adjusted_display(blockified_display,
is_item_or_root);
}
}
+ /// Change writing mode of text frame for text-combine-upright.
+ /// It is safe to look at the parent's style because we are looking at
+ /// inherited properties, and ::-moz-text never matches any rules.
+ #[cfg(feature = "gecko")]
+ fn adjust_for_text_combine_upright(&mut self,
+ layout_parent_style: &ComputedValues) {
+ if let Some(p) = self.pseudo {
+ if *p.as_atom() == atom!(":-moz-text") {
+ use computed_values::text_combine_upright::T as text_combine_upright;
+ use computed_values::writing_mode::T as writing_mode;
+ let parent_writing_mode = layout_parent_style.get_inheritedbox().clone_writing_mode();
+ let parent_text_combine_upright = layout_parent_style.get_inheritedtext().clone_text_combine_upright();
+ if parent_writing_mode != writing_mode::horizontal_tb &&
+ parent_text_combine_upright == text_combine_upright::all {
+ self.style.mutate_inheritedbox().set_writing_mode(writing_mode::horizontal_tb);
+ }
+ }
+ }
+ }
+
/// https://drafts.csswg.org/css-writing-modes-3/#block-flow:
///
/// If a box has a different writing-mode value than its containing
/// block:
///
/// - If the box has a specified display of inline, its display
/// computes to inline-block. [CSS21]
///
@@ -218,20 +241,24 @@ impl<'a> StyleAdjuster<'a> {
if overflow_x != original_overflow_x ||
overflow_y != original_overflow_y {
let mut box_style = self.style.mutate_box();
box_style.set_overflow_x(overflow_x);
box_style.set_overflow_y(overflow_y);
}
}
- /// Adjusts the style to account for display fixups.
+ /// Adjusts the style to account for various fixups the don't fit naturally into the cascade.
pub fn adjust(mut self,
layout_parent_style: &ComputedValues,
skip_root_and_element_display_fixup: bool) {
+ #[cfg(feature = "gecko")]
+ {
+ self.adjust_for_text_combine_upright(layout_parent_style);
+ }
self.adjust_for_top_layer();
self.blockify_if_necessary(layout_parent_style,
skip_root_and_element_display_fixup);
self.adjust_for_writing_mode(layout_parent_style);
self.adjust_for_position();
self.adjust_for_overflow();
#[cfg(feature = "gecko")]
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -406,16 +406,17 @@ impl Stylist {
//
// In practice, I don't think any anonymous content can be a direct
// descendant of a display: contents element where display: contents is
// the actual used value, and the computed value of it would need
// blockification.
let computed =
properties::cascade(&self.device,
&rule_node,
+ Some(pseudo),
guards,
parent.map(|p| &**p),
parent.map(|p| &**p),
None,
&RustLogReporter,
font_metrics,
cascade_flags);
ComputedStyle::new(rule_node, Arc::new(computed))
@@ -524,16 +525,17 @@ impl Stylist {
// Read the comment on `precomputed_values_for_pseudo` to see why it's
// difficult to assert that display: contents nodes never arrive here
// (tl;dr: It doesn't apply for replaced elements and such, but the
// computed value is still "contents").
let computed =
properties::cascade(&self.device,
&rule_node,
+ Some(pseudo),
guards,
Some(&**parent),
Some(&**parent),
None,
&RustLogReporter,
font_metrics,
CascadeFlags::empty());
@@ -854,16 +856,17 @@ impl Stylist {
CascadeLevel::StyleAttributeNormal)
];
let rule_node =
self.rule_tree.insert_ordered_rules(v.into_iter().map(|a| (a.source, a.level)));
let metrics = get_metrics_provider_for_product();
Arc::new(properties::cascade(&self.device,
&rule_node,
+ None,
guards,
Some(parent_style),
Some(parent_style),
None,
&RustLogReporter,
&metrics,
CascadeFlags::empty()))
}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -459,17 +459,17 @@ pub extern "C" fn Servo_StyleSet_GetBase
let style = pseudos.get(p);
debug_assert!(style.is_some());
style
}
None => None,
};
let provider = get_metrics_provider_for_product();
- element.get_base_style(shared_context, &provider, &styles.primary, pseudo_style)
+ element.get_base_style(shared_context, &provider, &styles.primary, pseudo.as_ref(), pseudo_style)
.into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(computed_values: ServoComputedValuesBorrowed,
property_id: nsCSSPropertyID)
-> RawServoAnimationValueStrong
{