Auto merge of #23973 - emilio:gecko-sync, r=emilio,jdm

style: Sync changes from mozilla-central.

See each individual commit for details.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23973)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-08-16 06:44:52 -04:00 committed by GitHub
commit aa60077563
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 1040 additions and 1081 deletions

View file

@ -1847,6 +1847,10 @@ where
node.type_id()
);
// FIXME(emilio): This should look at display-outside and
// display-inside, but there's so much stuff that goes through the
// generic "block" codepath (wrongly).
//
// Switch on display and floatedness.
match (display, float, positioning) {
// `display: none` contributes no flow construction result.
@ -1871,12 +1875,6 @@ where
self.set_flow_construction_result(node, construction_result)
},
// List items contribute their own special flows.
(Display::ListItem, float_value, _) => {
let construction_result = self.build_flow_for_list_item(node, float_value);
self.set_flow_construction_result(node, construction_result)
},
// Inline items that are absolutely-positioned contribute inline fragment construction
// results with a hypothetical fragment.
(Display::Inline, _, Position::Absolute) |
@ -1958,7 +1956,12 @@ where
// properties separately.
(_, float_value, _) => {
let float_kind = FloatKind::from_property(float_value);
let construction_result = self.build_flow_for_block(node, float_kind);
// List items contribute their own special flows.
let construction_result = if display.is_list_item() {
self.build_flow_for_list_item(node, float_value)
} else {
self.build_flow_for_block(node, float_kind)
};
self.set_flow_construction_result(node, construction_result)
},
}

View file

@ -19,7 +19,6 @@ use crate::traversal::InorderFlowTraversal;
use script_layout_interface::wrapper_traits::PseudoElementType;
use smallvec::SmallVec;
use std::collections::{HashMap, LinkedList};
use style::computed_values::display::T as Display;
use style::computed_values::list_style_type::T as ListStyleType;
use style::properties::ComputedValues;
use style::selector_parser::RestyleDamage;
@ -175,7 +174,7 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
}
let mut list_style_type = fragment.style().get_list().list_style_type;
if fragment.style().get_box().display != Display::ListItem {
if !fragment.style().get_box().display.is_list_item() {
list_style_type = ListStyleType::None
}
@ -291,7 +290,7 @@ impl<'a, 'b> ResolveGeneratedContentFragmentMutator<'a, 'b> {
fn reset_and_increment_counters_as_necessary(&mut self, fragment: &mut Fragment) {
let mut list_style_type = fragment.style().get_list().list_style_type;
if !self.is_block || fragment.style().get_box().display != Display::ListItem {
if !self.is_block || !fragment.style().get_box().display.is_list_item() {
list_style_type = ListStyleType::None
}

View file

@ -414,16 +414,10 @@ impl Parse for Source {
macro_rules! is_descriptor_enabled {
("font-display") => {
unsafe {
use crate::gecko_bindings::structs::mozilla;
mozilla::StaticPrefs::sVarCache_layout_css_font_display_enabled
}
static_prefs::pref!("layout.css.font-display.enabled")
};
("font-variation-settings") => {
unsafe {
use crate::gecko_bindings::structs::mozilla;
mozilla::StaticPrefs::sVarCache_layout_css_font_variations_enabled != 0
}
static_prefs::pref!("layout.css.font-variations.enabled")
};
($name:tt) => {
true

View file

@ -213,12 +213,11 @@ impl Device {
None => return MediaType::screen(),
};
// Gecko allows emulating random media with mIsEmulatingMedia and
// mMediaEmulated.
let medium_to_use = if pc.mIsEmulatingMedia() != 0 {
pc.mMediaEmulated.mRawPtr
// Gecko allows emulating random media with mMediaEmulationData.mMedium.
let medium_to_use = if !pc.mMediaEmulationData.mMedium.mRawPtr.is_null() {
pc.mMediaEmulationData.mMedium.mRawPtr
} else {
pc.mMedium
pc.mMedium as *const structs::nsAtom as *mut _
};
MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
@ -253,9 +252,8 @@ impl Device {
None => return Scale::new(1.),
};
let override_dppx = pc.mOverrideDPPX;
if override_dppx > 0.0 {
return Scale::new(override_dppx);
if pc.mMediaEmulationData.mDPPX > 0.0 {
return Scale::new(pc.mMediaEmulationData.mDPPX);
}
let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32;
@ -270,8 +268,7 @@ impl Device {
if doc.mIsBeingUsedAsImage() {
return true;
}
let document_color_use =
unsafe { structs::StaticPrefs::sVarCache_browser_display_document_color_use };
let document_color_use = static_prefs::pref!("browser.display.document_color_use");
let prefs = self.pref_sheet_prefs();
match document_color_use {
1 => true,

View file

@ -187,9 +187,7 @@ impl PseudoElement {
PseudoElement::FirstLine => PropertyFlags::APPLIES_TO_FIRST_LINE,
PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,
PseudoElement::Cue => PropertyFlags::APPLIES_TO_CUE,
PseudoElement::Marker
if unsafe { structs::StaticPrefs::sVarCache_layout_css_marker_restricted } =>
{
PseudoElement::Marker if static_prefs::pref!("layout.css.marker.restricted") => {
PropertyFlags::APPLIES_TO_MARKER
},
_ => return None,

View file

@ -112,7 +112,7 @@ impl PseudoElement {
% for pseudo in PSEUDOS:
${pseudo_element_variant(pseudo)} =>
% if pseudo.is_tree_pseudo_element():
if unsafe { structs::StaticPrefs::sVarCache_layout_css_xul_tree_pseudos_content_enabled } {
if static_prefs::pref!("layout.css.xul-tree-pseudos.content.enabled") {
0
} else {
structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME
@ -209,9 +209,7 @@ impl PseudoElement {
if starts_with_ignore_ascii_case(name, "-moz-tree-") {
return PseudoElement::tree_pseudo_element(name, Box::new([]))
}
if unsafe {
structs::StaticPrefs::sVarCache_layout_css_unknown_webkit_pseudo_element
} {
if static_prefs::pref!("layout.css.unknown-webkit-pseudo-element") {
const WEBKIT_PREFIX: &str = "-webkit-";
if starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {
let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]);

View file

@ -5,7 +5,7 @@
//! Gecko-specific bits for selector-parsing.
use crate::element_state::{DocumentState, ElementState};
use crate::gecko_bindings::structs::{self, RawServoSelectorList};
use crate::gecko_bindings::structs::RawServoSelectorList;
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::invalidation::element::document_state::InvalidationMatchingData;
use crate::selector_parser::{Direction, SelectorParser};
@ -170,13 +170,10 @@ impl NonTSPseudoClass {
/// Returns whether the pseudo-class is enabled in content sheets.
fn is_enabled_in_content(&self) -> bool {
use crate::gecko_bindings::structs::mozilla;
match *self {
// For pseudo-classes with pref, the availability in content
// depends on the pref.
NonTSPseudoClass::Fullscreen => unsafe {
mozilla::StaticPrefs::sVarCache_full_screen_api_unprefix_enabled
},
NonTSPseudoClass::Fullscreen => static_prefs::pref!("full-screen-api.unprefix.enabled"),
// Otherwise, a pseudo-class is enabled in content when it
// doesn't have any enabled flag.
_ => !self
@ -354,8 +351,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
#[inline]
fn parse_part(&self) -> bool {
self.chrome_rules_enabled() ||
unsafe { structs::StaticPrefs::sVarCache_layout_css_shadow_parts_enabled }
self.chrome_rules_enabled() || static_prefs::pref!("layout.css.shadow-parts.enabled")
}
fn parse_non_ts_pseudo_class(

View file

@ -91,6 +91,8 @@ extern crate servo_config;
extern crate servo_url;
extern crate smallbitvec;
extern crate smallvec;
#[cfg(feature = "gecko")]
extern crate static_prefs;
#[cfg(feature = "servo")]
extern crate string_cache;
#[macro_use]

View file

@ -11,8 +11,6 @@ use super::Device;
use crate::context::QuirksMode;
#[cfg(feature = "gecko")]
use crate::gecko::media_features::MEDIA_FEATURES;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs;
use crate::parser::{Parse, ParserContext};
#[cfg(feature = "servo")]
use crate::servo::media_queries::MEDIA_FEATURES;
@ -301,9 +299,7 @@ impl MediaFeatureExpression {
if starts_with_ignore_ascii_case(feature_name, "-webkit-") {
feature_name = &feature_name[8..];
requirements.insert(ParsingRequirements::WEBKIT_PREFIX);
if unsafe {
structs::StaticPrefs::sVarCache_layout_css_prefixes_device_pixel_ratio_webkit
} {
if static_prefs::pref!("layout.css.prefixes.device-pixel-ratio-webkit") {
requirements.insert(
ParsingRequirements::WEBKIT_DEVICE_PIXEL_RATIO_PREF_ENABLED,
);

View file

@ -673,7 +673,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
#[inline]
#[cfg(feature = "gecko")]
fn recompute_default_font_family_type_if_needed(&mut self) {
use crate::gecko_bindings::{bindings, structs};
use crate::gecko_bindings::bindings;
use crate::values::computed::font::GenericFontFamily;
if !self.seen.contains(LonghandId::XLang) &&
@ -681,7 +681,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
return;
}
let use_document_fonts = unsafe { structs::StaticPrefs::sVarCache_browser_display_use_document_fonts != 0 };
let use_document_fonts = static_prefs::pref!("browser.display.use_document_fonts") != 0;
let builder = &mut self.context.builder;
let (default_font_type, prioritize_user_fonts) = {
let font = builder.get_font().gecko();

View file

@ -652,9 +652,6 @@ def _remove_common_first_line_and_first_letter_properties(props, engine):
props.remove("text-emphasis-position")
props.remove("text-emphasis-style")
props.remove("text-emphasis-color")
props.remove("text-decoration-skip-ink")
props.remove("text-decoration-thickness")
props.remove("text-underline-offset")
props.remove("overflow-wrap")
props.remove("text-align")

View file

@ -973,11 +973,10 @@ fn static_assert() {
<% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
<%self:impl_trait style_struct_name="Position"
skip_longhands="${skip_position_longhands} order
skip_longhands="${skip_position_longhands}
align-content justify-content align-self
justify-self align-items justify-items
grid-auto-flow grid-template-rows
grid-template-columns">
grid-auto-flow">
% for side in SIDES:
<% impl_split_style_coord(side.ident, "mOffset", side.index) %>
% endfor
@ -1015,226 +1014,6 @@ fn static_assert() {
}
}
pub fn set_order(&mut self, v: longhands::order::computed_value::T) {
self.gecko.mOrder = v;
}
pub fn clone_order(&self) -> longhands::order::computed_value::T {
self.gecko.mOrder
}
${impl_simple_copy('order', 'mOrder')}
% for kind in ["rows", "columns"]:
pub fn set_grid_template_${kind}(&mut self, v: longhands::grid_template_${kind}::computed_value::T) {
<% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %>
use crate::gecko_bindings::structs::nsTArray;
use std::usize;
use crate::values::CustomIdent;
use crate::values::generics::grid::TrackListType::Auto;
use crate::values::generics::grid::{GridTemplateComponent, RepeatCount, TrackListValue, MAX_GRID_LINE};
#[inline]
fn set_line_names(servo_names: &[CustomIdent], gecko_names: &mut nsTArray<structs::RefPtr<structs::nsAtom>>) {
unsafe {
bindings::Gecko_ResizeAtomArray(gecko_names, servo_names.len() as u32);
}
for (servo_name, gecko_name) in servo_names.iter().zip(gecko_names.iter_mut()) {
gecko_name.set_move(unsafe {
RefPtr::from_addrefed(servo_name.0.clone().into_addrefed())
});
}
}
let max_lines = MAX_GRID_LINE as usize - 1; // for accounting the final <line-names>
let result = match v {
GridTemplateComponent::None => ptr::null_mut(),
GridTemplateComponent::TrackList(track) => {
let mut num_values = track.values.len();
if let Auto(_) = track.list_type {
num_values += 1;
}
num_values = cmp::min(num_values, max_lines);
let value = unsafe {
bindings::Gecko_CreateStyleGridTemplate(num_values as u32,
(num_values + 1) as u32).as_mut().unwrap()
};
let mut auto_idx = usize::MAX;
let mut auto_track_size = None;
if let Auto(idx) = track.list_type {
auto_idx = idx as usize;
let auto_repeat = track.auto_repeat.as_ref().expect("expected <auto-track-repeat> value");
if auto_repeat.count == RepeatCount::AutoFill {
value.set_mIsAutoFill(true);
}
value.mRepeatAutoIndex = idx as i16;
// NOTE: Gecko supports only one set of values in <auto-repeat>
// i.e., it can only take repeat(auto-fill, [a] 10px [b]), and no more.
set_line_names(&auto_repeat.line_names[0], &mut value.mRepeatAutoLineNameListBefore);
set_line_names(&auto_repeat.line_names[1], &mut value.mRepeatAutoLineNameListAfter);
auto_track_size = Some(auto_repeat.track_sizes.get(0).unwrap().clone());
} else {
unsafe {
bindings::Gecko_ResizeAtomArray(
&mut value.mRepeatAutoLineNameListBefore, 0);
bindings::Gecko_ResizeAtomArray(
&mut value.mRepeatAutoLineNameListAfter, 0);
}
}
let mut line_names = track.line_names.into_iter();
let mut values_iter = track.values.into_iter();
{
for (i, track_size) in value.mTrackSizingFunctions.iter_mut().enumerate().take(max_lines) {
let name_list = line_names.next().expect("expected line-names");
set_line_names(&name_list, &mut value.mLineNameLists[i]);
*track_size = if i == auto_idx {
auto_track_size.take().expect("expected <track-size> for <auto-track-repeat>")
} else {
match values_iter.next().expect("expected <track-size> value") {
TrackListValue::TrackSize(size) => size,
// FIXME(emilio): This shouldn't be
// representable in the first place.
TrackListValue::TrackRepeat(..) => {
unreachable!("Shouldn't have track-repeats in computed track lists")
}
}
};
}
}
let final_names = line_names.next().unwrap();
set_line_names(&final_names, value.mLineNameLists.last_mut().unwrap());
value
},
GridTemplateComponent::Subgrid(list) => {
let names_length = match list.fill_idx {
Some(_) => list.names.len() - 1,
None => list.names.len(),
};
let num_values = cmp::min(names_length, max_lines + 1);
let value = unsafe {
bindings::Gecko_CreateStyleGridTemplate(0, num_values as u32).as_mut().unwrap()
};
value.set_mIsSubgrid(true);
let mut names = list.names.into_vec();
if let Some(idx) = list.fill_idx {
value.set_mIsAutoFill(true);
value.mRepeatAutoIndex = idx as i16;
set_line_names(&names.swap_remove(idx as usize),
&mut value.mRepeatAutoLineNameListBefore);
}
for (servo_names, gecko_names) in names.iter().zip(value.mLineNameLists.iter_mut()) {
set_line_names(servo_names, gecko_names);
}
value
},
};
unsafe { bindings::Gecko_SetStyleGridTemplate(&mut ${self_grid}, result); }
}
pub fn copy_grid_template_${kind}_from(&mut self, other: &Self) {
unsafe {
bindings::Gecko_CopyStyleGridTemplateValues(&mut ${self_grid},
other.gecko.mGridTemplate${kind.title()}.mPtr);
}
}
pub fn reset_grid_template_${kind}(&mut self, other: &Self) {
self.copy_grid_template_${kind}_from(other)
}
pub fn clone_grid_template_${kind}(&self) -> longhands::grid_template_${kind}::computed_value::T {
<% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %>
use crate::gecko_bindings::structs::nsTArray;
use crate::values::CustomIdent;
use crate::values::generics::grid::{GridTemplateComponent, LineNameList, RepeatCount};
use crate::values::generics::grid::{TrackList, TrackListType, TrackListValue, TrackRepeat};
let value = match unsafe { ${self_grid}.mPtr.as_ref() } {
None => return GridTemplateComponent::None,
Some(value) => value,
};
#[inline]
fn to_boxed_customident_slice(gecko_names: &nsTArray<structs::RefPtr<structs::nsAtom>>) -> Box<[CustomIdent]> {
let idents: Vec<CustomIdent> = gecko_names.iter().map(|gecko_name| {
CustomIdent(unsafe { Atom::from_raw(gecko_name.mRawPtr) })
}).collect();
idents.into_boxed_slice()
}
#[inline]
fn to_line_names_vec(
gecko_line_names: &nsTArray<nsTArray<structs::RefPtr<structs::nsAtom>>>,
) -> Vec<Box<[CustomIdent]>> {
gecko_line_names.iter().map(|gecko_names| {
to_boxed_customident_slice(gecko_names)
}).collect()
}
let repeat_auto_index = value.mRepeatAutoIndex as usize;
if value.mIsSubgrid() {
let mut names_vec = to_line_names_vec(&value.mLineNameLists);
let fill_idx = if value.mIsAutoFill() {
names_vec.insert(
repeat_auto_index,
to_boxed_customident_slice(&value.mRepeatAutoLineNameListBefore));
Some(repeat_auto_index as u32)
} else {
None
};
let names = names_vec.into_boxed_slice();
GridTemplateComponent::Subgrid(LineNameList{names, fill_idx})
} else {
let mut auto_repeat = None;
let mut list_type = TrackListType::Normal;
let line_names = to_line_names_vec(&value.mLineNameLists).into_boxed_slice();
let mut values = Vec::with_capacity(value.mTrackSizingFunctions.len());
for (i, track_size) in value.mTrackSizingFunctions.iter().enumerate() {
if i == repeat_auto_index {
list_type = TrackListType::Auto(repeat_auto_index as u16);
let count = if value.mIsAutoFill() {
RepeatCount::AutoFill
} else {
RepeatCount::AutoFit
};
let line_names = {
let mut vec: Vec<Box<[CustomIdent]>> = Vec::with_capacity(2);
vec.push(to_boxed_customident_slice(
&value.mRepeatAutoLineNameListBefore));
vec.push(to_boxed_customident_slice(
&value.mRepeatAutoLineNameListAfter));
vec.into_boxed_slice()
};
let track_sizes = vec!(track_size.clone());
auto_repeat = Some(TrackRepeat{count, line_names, track_sizes});
} else {
values.push(TrackListValue::TrackSize(track_size.clone()));
}
}
GridTemplateComponent::TrackList(TrackList{list_type, values, line_names, auto_repeat})
}
}
% endfor
${impl_simple_type_with_conversion("grid_auto_flow")}
</%self:impl_trait>
@ -2799,97 +2578,15 @@ fn static_assert() {
<%self:impl_trait style_struct_name="InheritedText"
skip_longhands="text-align text-emphasis-style
-webkit-text-stroke-width text-emphasis-position">
skip_longhands="text-align -webkit-text-stroke-width text-emphasis-position">
<% text_align_keyword = Keyword("text-align",
"start end left right center justify -moz-center -moz-left -moz-right char",
gecko_strip_moz_prefix=False) %>
${impl_keyword('text_align', 'mTextAlign', text_align_keyword)}
fn clear_text_emphasis_style_if_string(&mut self) {
if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
self.gecko.mTextEmphasisStyleString.truncate();
self.gecko.mTextEmphasisStyle = structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE as u8;
}
}
${impl_simple_type_with_conversion("text_emphasis_position")}
pub fn set_text_emphasis_style(&mut self, v: values::computed::TextEmphasisStyle) {
use crate::values::computed::TextEmphasisStyle;
use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
self.clear_text_emphasis_style_if_string();
let (te, s) = match v {
TextEmphasisStyle::None => (structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE, ""),
TextEmphasisStyle::Keyword(ref keyword) => {
let fill = match keyword.fill {
TextEmphasisFillMode::Filled => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED,
TextEmphasisFillMode::Open => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN,
};
let shape = match keyword.shape {
TextEmphasisShapeKeyword::Dot => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOT,
TextEmphasisShapeKeyword::Circle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE,
TextEmphasisShapeKeyword::DoubleCircle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOUBLE_CIRCLE,
TextEmphasisShapeKeyword::Triangle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_TRIANGLE,
TextEmphasisShapeKeyword::Sesame => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME,
};
(shape | fill, keyword.shape.char(keyword.fill))
},
TextEmphasisStyle::String(ref s) => {
(structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING, &**s)
},
};
self.gecko.mTextEmphasisStyleString.assign_str(s);
self.gecko.mTextEmphasisStyle = te as u8;
}
pub fn copy_text_emphasis_style_from(&mut self, other: &Self) {
self.clear_text_emphasis_style_if_string();
if other.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
self.gecko.mTextEmphasisStyleString
.assign(&*other.gecko.mTextEmphasisStyleString)
}
self.gecko.mTextEmphasisStyle = other.gecko.mTextEmphasisStyle;
}
pub fn reset_text_emphasis_style(&mut self, other: &Self) {
self.copy_text_emphasis_style_from(other)
}
pub fn clone_text_emphasis_style(&self) -> values::computed::TextEmphasisStyle {
use crate::values::computed::TextEmphasisStyle;
use crate::values::computed::text::TextEmphasisKeywordValue;
use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE as u8 {
return TextEmphasisStyle::None;
}
if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
return TextEmphasisStyle::String(self.gecko.mTextEmphasisStyleString.to_string());
}
let fill =
self.gecko.mTextEmphasisStyle & structs::NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN as u8 == 0;
let fill = if fill { TextEmphasisFillMode::Filled } else { TextEmphasisFillMode::Open };
let shape =
match self.gecko.mTextEmphasisStyle as u32 & !structs::NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN {
structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOT => TextEmphasisShapeKeyword::Dot,
structs::NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE => TextEmphasisShapeKeyword::Circle,
structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOUBLE_CIRCLE => TextEmphasisShapeKeyword::DoubleCircle,
structs::NS_STYLE_TEXT_EMPHASIS_STYLE_TRIANGLE => TextEmphasisShapeKeyword::Triangle,
structs::NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME => TextEmphasisShapeKeyword::Sesame,
_ => panic!("Unexpected value in style struct for text-emphasis-style property")
};
TextEmphasisStyle::Keyword(TextEmphasisKeywordValue { fill, shape })
}
${impl_non_negative_length('_webkit_text_stroke_width',
'mWebkitTextStrokeWidth')}

View file

@ -58,7 +58,6 @@
animation_value_type="NonNegativeLength",
logical=is_logical,
logical_group="border-width",
flags="GETCS_NEEDS_LAYOUT_FLUSH",
allow_quirks="No" if is_logical else "Yes",
servo_restyle_damage="reflow rebuild_and_reflow_inline"
)}

View file

@ -339,8 +339,7 @@ ${helpers.predefined_type(
servo_2020_pref="layout.2020.unimplemented",
extra_prefixes=transform_extra_prefixes,
animation_value_type="ComputedValue",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB \
GETCS_NEEDS_LAYOUT_FLUSH CAN_ANIMATE_ON_COMPOSITOR",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR",
spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
servo_restyle_damage="reflow_out_of_flow",
)}
@ -421,6 +420,19 @@ ${helpers.predefined_type(
servo_restyle_damage="reflow_out_of_flow"
)}
// Motion Path Module Level 1
${helpers.predefined_type(
"offset-anchor",
"PositionOrAuto",
"computed::PositionOrAuto::auto()",
engines="gecko",
animation_value_type="ComputedValue",
gecko_pref="layout.css.motion-path.enabled",
spec="https://drafts.fxtf.org/motion-1/#offset-anchor-property",
servo_restyle_damage="reflow_out_of_flow",
boxed=True
)}
// CSSOM View Module
// https://www.w3.org/TR/cssom-view-1/
${helpers.single_keyword(
@ -539,7 +551,6 @@ ${helpers.predefined_type(
boxed=True,
extra_prefixes=transform_extra_prefixes,
spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
flags="GETCS_NEEDS_LAYOUT_FLUSH",
animation_value_type="ComputedValue",
servo_restyle_damage="reflow_out_of_flow"
)}
@ -587,7 +598,6 @@ ${helpers.predefined_type(
extra_prefixes=transform_extra_prefixes,
gecko_ffi_name="mTransformOrigin",
boxed=True,
flags="GETCS_NEEDS_LAYOUT_FLUSH",
spec="https://drafts.csswg.org/css-transforms/#transform-origin-property",
servo_restyle_damage="reflow_out_of_flow",
)}

View file

@ -23,7 +23,6 @@ ${helpers.predefined_type(
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
animation_value_type="LineHeight",
flags="GETCS_NEEDS_LAYOUT_FLUSH",
spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
servo_restyle_damage="reflow"
)}
@ -250,7 +249,6 @@ ${helpers.predefined_type(
None,
engines="gecko",
initial_specified_value="SpecifiedValue::None",
boxed=True,
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style",
)}
@ -384,8 +382,8 @@ ${helpers.single_keyword(
// text underline offset
${helpers.predefined_type(
"text-underline-offset",
"LengthOrAuto",
"computed::LengthOrAuto::auto()",
"TextDecorationLength",
"generics::text::GenericTextDecorationLength::Auto",
engines="gecko",
animation_value_type="ComputedValue",
gecko_pref="layout.css.text-underline-offset.enabled",

View file

@ -24,7 +24,6 @@
logical=side[1],
logical_group="margin",
spec=spec,
flags="GETCS_NEEDS_LAYOUT_FLUSH",
allowed_in_page_rule=True,
servo_restyle_damage="reflow"
)}

View file

@ -23,7 +23,6 @@
logical=side[1],
logical_group="padding",
spec=spec,
flags="GETCS_NEEDS_LAYOUT_FLUSH",
allow_quirks="No" if side[1] else "Yes",
servo_restyle_damage="reflow rebuild_and_reflow_inline"
)}

View file

@ -17,7 +17,6 @@
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
flags="GETCS_NEEDS_LAYOUT_FLUSH",
animation_value_type="ComputedValue",
allow_quirks="Yes",
servo_restyle_damage="reflow_out_of_flow",
@ -33,7 +32,6 @@
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side,
flags="GETCS_NEEDS_LAYOUT_FLUSH",
alias="offset-%s:layout.css.offset-logical-properties.enabled" % side,
animation_value_type="ComputedValue",
logical=True,
@ -285,7 +283,6 @@ ${helpers.predefined_type(
allow_quirks="No" if logical else "Yes",
spec=spec % size,
animation_value_type="Size",
flags="GETCS_NEEDS_LAYOUT_FLUSH",
servo_restyle_damage="reflow",
)}
// min-width, min-height, min-block-size, min-inline-size
@ -360,16 +357,13 @@ ${helpers.predefined_type(
)}
% endfor
// NOTE: According to the spec, this should handle multiple values of `<track-size>`,
// but gecko supports only a single value
${helpers.predefined_type(
"grid-auto-%ss" % kind,
"TrackSize",
"ImplicitGridTracks",
"Default::default()",
engines="gecko",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
boxed=True,
)}
${helpers.predefined_type(
@ -378,8 +372,6 @@ ${helpers.predefined_type(
"specified::GenericGridTemplateComponent::None",
engines="gecko",
spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind,
boxed=True,
flags="GETCS_NEEDS_LAYOUT_FLUSH",
animation_value_type="ComputedValue",
)}

View file

@ -71,9 +71,10 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"text-decoration-thickness",
"LengthOrAuto",
"computed::LengthOrAuto::auto()",
"TextDecorationLength",
"generics::text::GenericTextDecorationLength::Auto",
engines="gecko",
initial_specified_value="generics::text::GenericTextDecorationLength::Auto",
animation_value_type="ComputedValue",
gecko_pref="layout.css.text-decoration-thickness.enabled",
spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property"

View file

@ -80,7 +80,6 @@ ${helpers.predefined_type(
"Transform",
"generics::transform::Transform::none()",
engines="gecko",
flags="GETCS_NEEDS_LAYOUT_FLUSH",
animation_value_type="ComputedValue",
spec="None (Nonstandard internal property)",
enabled_in="chrome",
@ -94,7 +93,6 @@ ${helpers.predefined_type(
animation_value_type="ComputedValue",
gecko_ffi_name="mWindowTransformOrigin",
boxed=True,
flags="GETCS_NEEDS_LAYOUT_FLUSH",
spec="None (Nonstandard internal property)",
enabled_in="chrome",
)}

View file

@ -981,13 +981,10 @@ bitflags! {
const APPLIES_TO_CUE = 1 << 6;
/// This longhand property applies to ::marker.
const APPLIES_TO_MARKER = 1 << 7;
/// This property's getComputedStyle implementation requires layout
/// to be flushed.
const GETCS_NEEDS_LAYOUT_FLUSH = 1 << 8;
/// This property is a legacy shorthand.
///
/// https://drafts.csswg.org/css-cascade/#legacy-shorthand
const IS_LEGACY_SHORTHAND = 1 << 9;
const IS_LEGACY_SHORTHAND = 1 << 8;
/* The following flags are currently not used in Rust code, they
* only need to be listed in corresponding properties so that

View file

@ -268,7 +268,7 @@
>
use crate::parser::Parse;
use servo_arc::Arc;
use crate::values::generics::grid::{TrackSize, TrackList, TrackListType};
use crate::values::generics::grid::{TrackSize, TrackList};
use crate::values::generics::grid::{TrackListValue, concat_serialize_idents};
use crate::values::specified::{GridTemplateComponent, GenericGridTemplateComponent};
use crate::values::specified::grid::parse_line_names;
@ -300,26 +300,28 @@
}
% endfor
let first_line_names = input.try(parse_line_names).unwrap_or(vec![].into_boxed_slice());
let first_line_names = input.try(parse_line_names).unwrap_or_default();
if let Ok(mut string) = input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
let mut strings = vec![];
let mut values = vec![];
let mut line_names = vec![];
let mut names = first_line_names.into_vec();
let mut names = first_line_names;
loop {
line_names.push(names.into_boxed_slice());
line_names.push(names);
strings.push(string);
let size = input.try(|i| TrackSize::parse(context, i)).unwrap_or_default();
values.push(TrackListValue::TrackSize(size));
names = input.try(parse_line_names).unwrap_or(vec![].into_boxed_slice()).into_vec();
names = input.try(parse_line_names).unwrap_or_default();
if let Ok(v) = input.try(parse_line_names) {
names.extend(v.into_vec());
let mut names_vec = names.into_vec();
names_vec.extend(v.into_iter());
names = names_vec.into();
}
string = match input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
Ok(s) => s,
_ => { // only the named area determines whether we should bail out
line_names.push(names.into_boxed_slice());
line_names.push(names.into());
break
},
};
@ -327,22 +329,21 @@
if line_names.len() == values.len() {
// should be one longer than track sizes
line_names.push(vec![].into_boxed_slice());
line_names.push(Default::default());
}
let template_areas = TemplateAreas::from_vec(strings)
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
let template_rows = TrackList {
list_type: TrackListType::Normal,
values,
line_names: line_names.into_boxed_slice(),
auto_repeat: None,
values: values.into(),
line_names: line_names.into(),
auto_repeat_index: std::usize::MAX,
};
let template_cols = if input.try(|i| i.expect_delim('/')).is_ok() {
let value = GridTemplateComponent::parse_without_none(context, input)?;
if let GenericGridTemplateComponent::TrackList(ref list) = value {
if list.list_type != TrackListType::Explicit {
if !list.is_explicit() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
@ -352,8 +353,11 @@
GenericGridTemplateComponent::None
};
Ok((GenericGridTemplateComponent::TrackList(template_rows),
template_cols, GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas)))))
Ok((
GenericGridTemplateComponent::TrackList(Box::new(template_rows)),
template_cols,
GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas)))
))
} else {
let mut template_rows = GridTemplateComponent::parse(context, input)?;
if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
@ -407,13 +411,10 @@
let track_list = match *template_rows {
GenericGridTemplateComponent::TrackList(ref list) => {
// We should fail if there is a `repeat` function. `grid` and
// `grid-template` shorthands doesn't accept that. Only longhand accepts.
if list.auto_repeat.is_some() ||
list.values.iter().any(|v| match *v {
TrackListValue::TrackRepeat(_) => true,
_ => false,
}) {
// We should fail if there is a `repeat` function.
// `grid` and `grid-template` shorthands doesn't accept
// that. Only longhand accepts.
if !list.is_explicit() {
return Ok(());
}
list
@ -429,11 +430,7 @@
// We should fail if there is a `repeat` function. `grid` and
// `grid-template` shorthands doesn't accept that. Only longhand accepts that.
GenericGridTemplateComponent::TrackList(ref list) => {
if list.auto_repeat.is_some() ||
list.values.iter().any(|v| match *v {
TrackListValue::TrackRepeat(_) => true,
_ => false,
}) {
if !list.is_explicit() {
return Ok(());
}
},
@ -498,8 +495,8 @@
>
use crate::parser::Parse;
use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow};
use crate::values::generics::grid::{GridTemplateComponent, TrackListType};
use crate::values::specified::{GenericGridTemplateComponent, TrackSize};
use crate::values::generics::grid::GridTemplateComponent;
use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};
use crate::values::specified::position::{AutoFlow, GridAutoFlow, GridTemplateAreas};
pub fn parse_value<'i, 't>(
@ -509,8 +506,8 @@
let mut temp_rows = GridTemplateComponent::None;
let mut temp_cols = GridTemplateComponent::None;
let mut temp_areas = GridTemplateAreas::None;
let mut auto_rows = TrackSize::default();
let mut auto_cols = TrackSize::default();
let mut auto_rows = ImplicitGridTracks::default();
let mut auto_cols = ImplicitGridTracks::default();
let mut flow = grid_auto_flow::get_initial_value();
fn parse_auto_flow<'i, 't>(
@ -571,8 +568,8 @@
/// Returns true if other sub properties except template-{rows,columns} are initial.
fn is_grid_template(&self) -> bool {
*self.grid_template_areas == GridTemplateAreas::None &&
*self.grid_auto_rows == TrackSize::default() &&
*self.grid_auto_columns == TrackSize::default() &&
self.grid_auto_rows.is_initial() &&
self.grid_auto_columns.is_initial() &&
*self.grid_auto_flow == grid_auto_flow::get_initial_value()
}
}
@ -590,14 +587,14 @@
if self.grid_auto_flow.autoflow == AutoFlow::Column {
// It should fail to serialize if other branch of the if condition's values are set.
if *self.grid_auto_rows != TrackSize::default() ||
if !self.grid_auto_rows.is_initial() ||
*self.grid_template_columns != GridTemplateComponent::None {
return Ok(());
}
// It should fail to serialize if template-rows value is not Explicit.
if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows {
if list.list_type != TrackListType::Explicit {
if !list.is_explicit() {
return Ok(());
}
}
@ -608,20 +605,20 @@
dest.write_str(" dense")?;
}
if !self.grid_auto_columns.is_default() {
if !self.grid_auto_columns.is_initial() {
dest.write_str(" ")?;
self.grid_auto_columns.to_css(dest)?;
}
} else {
// It should fail to serialize if other branch of the if condition's values are set.
if *self.grid_auto_columns != TrackSize::default() ||
if !self.grid_auto_columns.is_initial() ||
*self.grid_template_rows != GridTemplateComponent::None {
return Ok(());
}
// It should fail to serialize if template-column value is not Explicit.
if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns {
if list.list_type != TrackListType::Explicit {
if !list.is_explicit() {
return Ok(());
}
}
@ -631,7 +628,7 @@
dest.write_str(" dense")?;
}
if !self.grid_auto_rows.is_default() {
if !self.grid_auto_rows.is_initial() {
dest.write_str(" ")?;
self.grid_auto_rows.to_css(dest)?;
}

View file

@ -8,12 +8,13 @@
engines="gecko servo-2013"
flags="SHORTHAND_IN_GETCS"
sub_properties="text-decoration-line
${' text-decoration-style text-decoration-color' if engine == 'gecko' else ''}"
${' text-decoration-style text-decoration-color text-decoration-thickness' if engine == 'gecko' else ''}"
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
% if engine == "gecko":
use crate::values::specified;
use crate::properties::longhands::{text_decoration_style, text_decoration_color};
use crate::properties::longhands::{text_decoration_style, text_decoration_color, text_decoration_thickness};
use crate::properties::{PropertyId, LonghandId};
% endif
use crate::properties::longhands::text_decoration_line;
@ -22,7 +23,10 @@
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
% if engine == "gecko":
let (mut line, mut style, mut color, mut any) = (None, None, None, false);
let text_decoration_thickness_enabled =
PropertyId::Longhand(LonghandId::TextDecorationThickness).enabled_for_all_content();
let (mut line, mut style, mut color, mut thickness, mut any) = (None, None, None, None, false);
% else:
let (mut line, mut any) = (None, false);
% endif
@ -45,6 +49,9 @@
% if engine == "gecko":
parse_component!(style, text_decoration_style);
parse_component!(color, text_decoration_color);
if text_decoration_thickness_enabled {
parse_component!(thickness, text_decoration_thickness);
}
% endif
break;
@ -60,6 +67,7 @@
% if engine == "gecko":
text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness),
% endif
})
}
@ -78,6 +86,13 @@
dest.write_str(" ")?;
self.text_decoration_color.to_css(dest)?;
}
if let Some(text_decoration_thickness) = self.text_decoration_thickness {
if !text_decoration_thickness.is_auto() {
dest.write_str(" ")?;
self.text_decoration_thickness.to_css(dest)?;
}
}
% endif
Ok(())

View file

@ -12,6 +12,7 @@ use crate::properties::longhands::float::computed_value::T as Float;
use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
use crate::properties::longhands::position::computed_value::T as Position;
use crate::properties::{self, ComputedValues, StyleBuilder};
use crate::values::specified::box_::DisplayInside;
use app_units::Au;
/// A struct that implements all the adjustment methods.
@ -175,7 +176,9 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
/// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>
/// A ::marker pseudo-element with 'list-style-position:outside' needs to
/// have its 'display' blockified.
/// have its 'display' blockified, unless the ::marker is for an inline
/// list-item (for which 'list-style-position:outside' behaves as 'inside').
/// https://drafts.csswg.org/css-lists-3/#list-style-position-property
fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
where
E: TElement,
@ -194,10 +197,8 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root());
blockify_if!(is_root);
if !self.skip_item_display_fixup(element) {
blockify_if!(layout_parent_style
.get_box()
.clone_display()
.is_item_container());
let parent_display = layout_parent_style.get_box().clone_display();
blockify_if!(parent_display.is_item_container());
}
let is_item_or_root = blockify;
@ -207,7 +208,8 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
blockify_if!(
self.style.pseudo.map_or(false, |p| p.is_marker()) &&
self.style.get_parent_list().clone_list_style_position() ==
ListStylePosition::Outside
ListStylePosition::Outside &&
layout_parent_style.get_box().clone_display().inside() != DisplayInside::Inline
);
if !blockify {

View file

@ -253,20 +253,18 @@ impl DocumentCondition {
#[cfg(feature = "gecko")]
fn allowed_in(&self, context: &ParserContext) -> bool {
use crate::gecko_bindings::structs;
use crate::stylesheets::Origin;
use static_prefs::pref;
if context.stylesheet_origin != Origin::Author {
return true;
}
if unsafe { structs::StaticPrefs::sVarCache_layout_css_moz_document_content_enabled } {
if pref!("layout.css.moz-document.content.enabled") {
return true;
}
if !unsafe {
structs::StaticPrefs::sVarCache_layout_css_moz_document_url_prefix_hack_enabled
} {
if !pref!("layout.css.moz-document.url-prefix-hack.enabled") {
return false;
}

View file

@ -323,9 +323,7 @@ impl RawSelector {
pub fn eval(&self, context: &ParserContext, namespaces: &Namespaces) -> bool {
#[cfg(feature = "gecko")]
{
if unsafe {
!crate::gecko_bindings::structs::StaticPrefs::sVarCache_layout_css_supports_selector_enabled
} {
if !static_prefs::pref!("layout.css.supports-selector.enabled") {
return false;
}
}

View file

@ -101,7 +101,7 @@ impl Animate for generics::TrackRepeat<LengthPercentage, Integer> {
Ok(generics::TrackRepeat {
count,
line_names,
track_sizes,
track_sizes: track_sizes.into(),
})
}
}
@ -117,20 +117,19 @@ impl Animate for TrackList {
return Err(());
}
if self.list_type != other.list_type {
if self.is_explicit() != other.is_explicit() {
return Err(());
}
// For now, repeat(auto-fill/auto-fit, ...) is not animatable. TrackRepeat will
// return Err(()) if we use keywords. Therefore, we can early return here to avoid
// traversing |values| in <auto-track-list>. This may be updated in the future.
// For now, repeat(auto-fill/auto-fit, ...) is not animatable.
// TrackRepeat will return Err(()) if we use keywords. Therefore, we can
// early return here to avoid traversing |values| in <auto-track-list>.
// This may be updated in the future.
// https://github.com/w3c/csswg-drafts/issues/3503
if let generics::TrackListType::Auto(_) = self.list_type {
if self.has_auto_repeat() || other.has_auto_repeat() {
return Err(());
}
let list_type = self.list_type;
let auto_repeat = self.auto_repeat.animate(&other.auto_repeat, procedure)?;
let values = self
.values
.iter()
@ -142,10 +141,9 @@ impl Animate for TrackList {
let line_names = discrete(&self.line_names, &other.line_names, procedure)?;
Ok(TrackList {
list_type,
values,
values: values.into(),
line_names,
auto_repeat,
auto_repeat_index: self.auto_repeat_index,
})
}
}

View file

@ -7,6 +7,7 @@
use self::transform::DirectionVector;
use super::animated::ToAnimatedValue;
use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
use super::generics::grid::{GenericGridLine, GenericTrackBreadth};
use super::generics::grid::{GenericTrackSize, TrackList as GenericTrackList};
use super::generics::transform::IsParallelTo;
@ -68,17 +69,17 @@ pub use self::list::Quotes;
pub use self::motion::{OffsetPath, OffsetRotate};
pub use self::outline::OutlineStyle;
pub use self::percentage::{NonNegativePercentage, Percentage};
pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, ZIndex};
pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto, ZIndex};
pub use self::rect::NonNegativeLengthOrNumberRect;
pub use self::resolution::Resolution;
pub use self::svg::MozContextProperties;
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
pub use self::table::XSpan;
pub use self::text::TextDecorationSkipInk;
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight};
pub use self::text::{OverflowWrap, TextOverflow, WordBreak, WordSpacing};
pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle};
pub use self::text::{TextDecorationLength, TextDecorationSkipInk};
pub use self::time::Time;
pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
pub use self::transform::{TransformOrigin, TransformStyle, Translate};
@ -467,6 +468,7 @@ trivial_to_computed_value!(i32);
trivial_to_computed_value!(u8);
trivial_to_computed_value!(u16);
trivial_to_computed_value!(u32);
trivial_to_computed_value!(usize);
trivial_to_computed_value!(Atom);
#[cfg(feature = "servo")]
trivial_to_computed_value!(Prefix);
@ -695,6 +697,9 @@ pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
/// The computed value of a grid `<track-size>`
pub type TrackSize = GenericTrackSize<LengthPercentage>;
/// The computed value of a grid `<track-size>+`
pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
/// The computed value of a grid `<track-list>`
/// (could also be `<auto-track-list>` or `<explicit-track-list>`)
pub type TrackList = GenericTrackList<LengthPercentage, Integer>;

View file

@ -9,6 +9,7 @@
use crate::values::computed::{Integer, LengthPercentage, Percentage};
use crate::values::generics::position::Position as GenericPosition;
use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
use crate::values::generics::position::ZIndex as GenericZIndex;
pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
use crate::Zero;
@ -18,6 +19,9 @@ use style_traits::{CssWriter, ToCss};
/// The computed value of a CSS `<position>`
pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
/// The computed value of an `auto | <position>`
pub type PositionOrAuto = GenericPositionOrAuto<Position>;
/// The computed value of a CSS horizontal position.
pub type HorizontalPosition = LengthPercentage;

View file

@ -10,7 +10,7 @@ use crate::values::computed::length::{Length, LengthPercentage};
use crate::values::computed::{Context, NonNegativeLength, NonNegativeNumber, ToComputedValue};
use crate::values::generics::text::InitialLetter as GenericInitialLetter;
use crate::values::generics::text::LineHeight as GenericLineHeight;
use crate::values::generics::text::Spacing;
use crate::values::generics::text::{GenericTextDecorationLength, Spacing};
use crate::values::specified::text::{self as specified, TextOverflowSide};
use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
use crate::values::{CSSFloat, CSSInteger};
@ -26,6 +26,9 @@ pub use crate::values::specified::{TextDecorationSkipInk, TextTransform};
/// A computed value for the `initial-letter` property.
pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
/// Implements type for `text-underline-offset` and `text-decoration-thickness` properties
pub type TextDecorationLength = GenericTextDecorationLength<Length>;
/// A computed value for the `letter-spacing` property.
#[repr(transparent)]
#[derive(
@ -194,22 +197,21 @@ impl TextDecorationsInEffect {
}
}
/// computed value for the text-emphasis-style property
/// Computed value for the text-emphasis-style property
///
/// cbindgen:derive-tagged-enum-copy-constructor=true
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
#[allow(missing_docs)]
#[repr(C, u8)]
pub enum TextEmphasisStyle {
/// Keyword value for the text-emphasis-style property (`filled` `open`)
Keyword(TextEmphasisKeywordValue),
/// [ <fill> || <shape> ]
Keyword {
#[css(skip_if = "TextEmphasisFillMode::is_filled")]
fill: TextEmphasisFillMode,
shape: TextEmphasisShapeKeyword,
},
/// `none`
None,
/// String (will be used only first grapheme cluster) for the text-emphasis-style property
String(String),
}
/// Keyword value for the text-emphasis-style property
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
pub struct TextEmphasisKeywordValue {
/// fill for the text-emphasis-style property
pub fill: TextEmphasisFillMode,
/// shape for the text-emphasis-style property
pub shape: TextEmphasisShapeKeyword,
/// `<string>` (of which only the first grapheme cluster will be used).
String(crate::OwnedStr),
}

View file

@ -69,8 +69,7 @@ pub enum TimingKeyword {
#[cfg(feature = "gecko")]
fn step_position_jump_enabled(_context: &ParserContext) -> bool {
use crate::gecko_bindings::structs;
unsafe { structs::StaticPrefs::sVarCache_layout_css_step_position_jump_enabled }
static_prefs::pref!("layout.css.step-position-jump.enabled")
}
#[cfg(feature = "servo")]

View file

@ -253,9 +253,7 @@ pub enum KeywordSize {
XLarge,
#[css(keyword = "xx-large")]
XXLarge,
// This is not a real font keyword and will not parse
// HTML font-size 7 corresponds to this value
#[css(skip)]
#[css(keyword = "xxx-large")]
XXXLarge,
}

View file

@ -12,7 +12,7 @@ use crate::values::{CSSFloat, CustomIdent};
use crate::{Atom, Zero};
use cssparser::Parser;
use std::fmt::{self, Write};
use std::{cmp, mem, usize};
use std::{cmp, usize};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
/// These are the limits that we choose to clamp grid line numbers to.
@ -290,16 +290,17 @@ impl<L> TrackSize<L> {
}
}
impl<L> Default for TrackSize<L> {
fn default() -> Self {
TrackSize::Breadth(TrackBreadth::Auto)
impl<L: PartialEq> TrackSize<L> {
/// Return true if it is `auto`.
#[inline]
pub fn is_auto(&self) -> bool {
*self == TrackSize::Breadth(TrackBreadth::Auto)
}
}
impl<L: PartialEq> TrackSize<L> {
/// Returns true if current TrackSize is same as default.
pub fn is_default(&self) -> bool {
*self == TrackSize::default()
impl<L> Default for TrackSize<L> {
fn default() -> Self {
TrackSize::Breadth(TrackBreadth::Auto)
}
}
@ -334,6 +335,39 @@ impl<L: ToCss> ToCss for TrackSize<L> {
}
}
/// A `<track-size>+`.
/// We use the empty slice as `auto`, and always parse `auto` as an empty slice.
/// This means it's impossible to have a slice containing only one auto item.
#[derive(
Clone,
Debug,
Default,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(transparent)]
pub struct GenericImplicitGridTracks<T>(
#[css(if_empty = "auto", iterable)] pub crate::OwnedSlice<T>,
);
pub use self::GenericImplicitGridTracks as ImplicitGridTracks;
impl<T: fmt::Debug + Default + PartialEq> ImplicitGridTracks<T> {
/// Returns true if current value is same as its initial value (i.e. auto).
pub fn is_initial(&self) -> bool {
debug_assert_ne!(
*self,
ImplicitGridTracks(crate::OwnedSlice::from(vec![Default::default()]))
);
self.0.is_empty()
}
}
/// Helper function for serializing identifiers with a prefix and suffix, used
/// for serializing <line-names> (in grid).
pub fn concat_serialize_idents<W>(
@ -366,6 +400,7 @@ where
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
)]
#[repr(C, u8)]
pub enum RepeatCount<Integer> {
/// A positive integer. This is allowed only for `<track-repeat>` and `<fixed-repeat>`
Number(Integer),
@ -380,18 +415,15 @@ impl Parse for RepeatCount<specified::Integer> {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
// Maximum number of repeat is 10000. The greater numbers should be clamped.
const MAX_LINE: i32 = 10000;
if let Ok(mut i) = input.try(|i| specified::Integer::parse_positive(context, i)) {
if i.value() > MAX_LINE {
i = specified::Integer::new(MAX_LINE);
}
Ok(RepeatCount::Number(i))
} else {
try_match_ident_ignore_ascii_case! { input,
"auto-fill" => Ok(RepeatCount::AutoFill),
"auto-fit" => Ok(RepeatCount::AutoFit),
if i.value() > MAX_GRID_LINE {
i = specified::Integer::new(MAX_GRID_LINE);
}
return Ok(RepeatCount::Number(i));
}
try_match_ident_ignore_ascii_case! { input,
"auto-fill" => Ok(RepeatCount::AutoFill),
"auto-fit" => Ok(RepeatCount::AutoFit),
}
}
}
@ -411,7 +443,8 @@ impl Parse for RepeatCount<specified::Integer> {
ToShmem,
)]
#[css(function = "repeat")]
pub struct TrackRepeat<L, I> {
#[repr(C)]
pub struct GenericTrackRepeat<L, I> {
/// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`)
pub count: RepeatCount<I>,
/// `<line-names>` accompanying `<track_size>` values.
@ -419,11 +452,13 @@ pub struct TrackRepeat<L, I> {
/// If there's no `<line-names>`, then it's represented by an empty vector.
/// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's
/// length is always one value more than that of the `<track-size>`.
pub line_names: Box<[Box<[CustomIdent]>]>,
pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
/// `<track-size>` values.
pub track_sizes: Vec<TrackSize<L>>,
pub track_sizes: crate::OwnedSlice<GenericTrackSize<L>>,
}
pub use self::GenericTrackRepeat as TrackRepeat;
impl<L: ToCss, I: ToCss> ToCss for TrackRepeat<L, I> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
@ -457,46 +492,10 @@ impl<L: ToCss, I: ToCss> ToCss for TrackRepeat<L, I> {
Ok(())
}
}
impl<L: Clone> TrackRepeat<L, specified::Integer> {
/// If the repeat count is numeric, then expand the values and merge accordingly.
pub fn expand(&self) -> Self {
if let RepeatCount::Number(num) = self.count {
let mut line_names = vec![];
let mut track_sizes = vec![];
let mut prev_names = vec![];
for _ in 0..num.value() {
let mut names_iter = self.line_names.iter();
for (size, names) in self.track_sizes.iter().zip(&mut names_iter) {
prev_names.extend_from_slice(&names);
let vec = mem::replace(&mut prev_names, vec![]);
line_names.push(vec.into_boxed_slice());
track_sizes.push(size.clone());
}
if let Some(names) = names_iter.next() {
prev_names.extend_from_slice(&names);
}
}
line_names.push(prev_names.into_boxed_slice());
TrackRepeat {
count: self.count,
track_sizes: track_sizes,
line_names: line_names.into_boxed_slice(),
}
} else {
// if it's auto-fit/auto-fill, then it's left to the layout.
TrackRepeat {
count: self.count,
track_sizes: self.track_sizes.clone(),
line_names: self.line_names.clone(),
}
}
}
}
/// Track list values. Can be <track-size> or <track-repeat>
///
/// cbindgen:derive-tagged-enum-copy-constructor=true
#[derive(
Animate,
Clone,
@ -509,56 +508,63 @@ impl<L: Clone> TrackRepeat<L, specified::Integer> {
ToResolvedValue,
ToShmem,
)]
pub enum TrackListValue<LengthPercentage, Integer> {
#[repr(C, u8)]
pub enum GenericTrackListValue<LengthPercentage, Integer> {
/// A <track-size> value.
TrackSize(#[animation(field_bound)] TrackSize<LengthPercentage>),
TrackSize(#[animation(field_bound)] GenericTrackSize<LengthPercentage>),
/// A <track-repeat> value.
TrackRepeat(#[animation(field_bound)] TrackRepeat<LengthPercentage, Integer>),
TrackRepeat(#[animation(field_bound)] GenericTrackRepeat<LengthPercentage, Integer>),
}
/// The type of a `<track-list>` as determined during parsing.
///
/// <https://drafts.csswg.org/css-grid/#typedef-track-list>
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
pub enum TrackListType {
/// [`<auto-track-list>`](https://drafts.csswg.org/css-grid/#typedef-auto-track-list)
///
/// If this type exists, then the value at the index in `line_names` field in `TrackList`
/// has the `<line-names>?` list that comes before `<auto-repeat>`. If it's a specified value,
/// then the `repeat()` function (that follows the line names list) is also at the given index
/// in `values` field. On the contrary, if it's a computed value, then the `repeat()` function
/// is in the `auto_repeat` field.
Auto(u16),
/// [`<track-list>`](https://drafts.csswg.org/css-grid/#typedef-track-list)
Normal,
/// [`<explicit-track-list>`](https://drafts.csswg.org/css-grid/#typedef-explicit-track-list)
///
/// Note that this is a subset of the normal `<track-list>`, and so it could be used in place
/// of the latter.
Explicit,
pub use self::GenericTrackListValue as TrackListValue;
impl<L, I> TrackListValue<L, I> {
fn is_repeat(&self) -> bool {
matches!(*self, TrackListValue::TrackRepeat(..))
}
}
/// A grid `<track-list>` type.
///
/// <https://drafts.csswg.org/css-grid/#typedef-track-list>
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToResolvedValue, ToShmem)]
pub struct TrackList<LengthPercentage, Integer> {
/// The type of this `<track-list>` (auto, explicit or general).
///
/// In order to avoid parsing the same value multiple times, this does a single traversal
/// and arrives at the type of value it has parsed (or bails out gracefully with an error).
#[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct GenericTrackList<LengthPercentage, Integer> {
/// The index in `values` where our `<auto-repeat>` value is, if in bounds.
#[css(skip)]
pub list_type: TrackListType,
pub auto_repeat_index: usize,
/// A vector of `<track-size> | <track-repeat>` values.
pub values: Vec<TrackListValue<LengthPercentage, Integer>>,
pub values: crate::OwnedSlice<GenericTrackListValue<LengthPercentage, Integer>>,
/// `<line-names>` accompanying `<track-size> | <track-repeat>` values.
///
/// If there's no `<line-names>`, then it's represented by an empty vector.
/// For N values, there will be N+1 `<line-names>`, and so this vector's
/// length is always one value more than that of the `<track-size>`.
pub line_names: Box<[Box<[CustomIdent]>]>,
/// `<auto-repeat>` value. There can only be one `<auto-repeat>` in a TrackList.
pub auto_repeat: Option<TrackRepeat<LengthPercentage, Integer>>,
pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
}
pub use self::GenericTrackList as TrackList;
impl<L, I> TrackList<L, I> {
/// Whether this track list is an explicit track list (that is, doesn't have
/// any repeat values).
pub fn is_explicit(&self) -> bool {
!self.values.iter().any(|v| v.is_repeat())
}
/// Whether this track list has an `<auto-repeat>` value.
pub fn has_auto_repeat(&self) -> bool {
self.auto_repeat_index < self.values.len()
}
}
impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
@ -566,11 +572,6 @@ impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
where
W: Write,
{
let auto_idx = match self.list_type {
TrackListType::Auto(i) => i as usize,
_ => usize::MAX,
};
let mut values_iter = self.values.iter().peekable();
let mut line_names_iter = self.line_names.iter().peekable();
@ -578,29 +579,20 @@ impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
let names = line_names_iter.next().unwrap(); // This should exist!
concat_serialize_idents("[", "]", names, " ", dest)?;
match self.auto_repeat {
Some(ref repeat) if idx == auto_idx => {
match values_iter.next() {
Some(value) => {
if !names.is_empty() {
dest.write_str(" ")?;
}
repeat.to_css(dest)?;
},
_ => match values_iter.next() {
Some(value) => {
if !names.is_empty() {
dest.write_str(" ")?;
}
value.to_css(dest)?;
},
None => break,
value.to_css(dest)?;
},
None => break,
}
if values_iter.peek().is_some() ||
line_names_iter.peek().map_or(false, |v| !v.is_empty()) ||
(idx + 1 == auto_idx)
(idx + 1 == self.auto_repeat_index)
{
dest.write_str(" ")?;
}
@ -613,7 +605,8 @@ impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
/// The `<line-name-list>` for subgrids.
///
/// `subgrid [ <line-names> | repeat(<positive-integer> | auto-fill, <line-names>+) ]+`
/// Old spec: https://www.w3.org/TR/2015/WD-css-grid-1-20150917/#typedef-line-name-list
///
/// https://drafts.csswg.org/css-grid-2/#typedef-line-name-list
#[derive(
Clone,
Debug,
@ -625,11 +618,12 @@ impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct LineNameList {
/// The optional `<line-name-list>`
pub names: Box<[Box<[CustomIdent]>]>,
/// Indicates the line name that requires `auto-fill`
pub fill_idx: Option<u32>,
pub names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
/// Indicates the line name that requires `auto-fill`, if in bounds.
pub fill_idx: usize,
}
impl Parse for LineNameList {
@ -652,13 +646,17 @@ impl Parse for LineNameList {
while let Ok(names) = input.try(parse_line_names) {
names_list.push(names);
}
Ok((names_list, count))
})
});
if let Ok((mut names_list, count)) = repeat_parse_result {
match count {
// FIXME(emilio): we probably shouldn't expand repeat() at
// parse time for subgrid.
//
// Also this doesn't have the merging semantics that
// non-subgrid has... But maybe that's ok?
RepeatCount::Number(num) => line_names.extend(
names_list
.iter()
@ -676,7 +674,7 @@ impl Parse for LineNameList {
let names = names_list.pop().unwrap();
line_names.push(names);
fill_idx = Some(line_names.len() as u32 - 1);
fill_idx = Some(line_names.len() - 1);
},
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
}
@ -687,9 +685,13 @@ impl Parse for LineNameList {
}
}
if line_names.len() > MAX_GRID_LINE as usize {
line_names.truncate(MAX_GRID_LINE as usize);
}
Ok(LineNameList {
names: line_names.into_boxed_slice(),
fill_idx: fill_idx,
names: line_names.into(),
fill_idx: fill_idx.unwrap_or(usize::MAX),
})
}
}
@ -700,7 +702,7 @@ impl ToCss for LineNameList {
W: Write,
{
dest.write_str("subgrid")?;
let fill_idx = self.fill_idx.map(|v| v as usize).unwrap_or(usize::MAX);
let fill_idx = self.fill_idx;
for (i, names) in self.names.iter().enumerate() {
if i == fill_idx {
dest.write_str(" repeat(auto-fill,")?;
@ -727,8 +729,8 @@ impl ToCss for LineNameList {
}
/// Variants for `<grid-template-rows> | <grid-template-columns>`
/// Subgrid deferred to Level 2 spec due to lack of implementation.
/// But it's implemented in gecko, so we have to as well.
///
/// cbindgen:derive-tagged-enum-copy-constructor=true
#[derive(
Animate,
Clone,
@ -741,7 +743,8 @@ impl ToCss for LineNameList {
ToResolvedValue,
ToShmem,
)]
pub enum GridTemplateComponent<L, I> {
#[repr(C, u8)]
pub enum GenericGridTemplateComponent<L, I> {
/// `none` value.
None,
/// The grid `<track-list>`
@ -750,14 +753,16 @@ pub enum GridTemplateComponent<L, I> {
#[compute(field_bound)]
#[resolve(field_bound)]
#[shmem(field_bound)]
TrackList<L, I>,
Box<GenericTrackList<L, I>>,
),
/// A `subgrid <line-name-list>?`
/// TODO: Support animations for this after subgrid is addressed in [grid-2] spec.
#[animation(error)]
Subgrid(LineNameList),
Subgrid(Box<LineNameList>),
}
pub use self::GenericGridTemplateComponent as GridTemplateComponent;
impl<L, I> GridTemplateComponent<L, I> {
/// Returns length of the <track-list>s <track-size>
pub fn track_list_len(&self) -> usize {

View file

@ -41,6 +41,43 @@ impl<H, V> Position<H, V> {
}
}
/// A generic type for representing an `Auto | <position>`.
/// This is used by <offset-anchor> for now.
/// https://drafts.fxtf.org/motion-1/#offset-anchor-property
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum GenericPositionOrAuto<Pos> {
/// The <position> value.
Position(Pos),
/// The keyword `auto`.
Auto,
}
pub use self::GenericPositionOrAuto as PositionOrAuto;
impl<Pos> PositionOrAuto<Pos> {
/// Return `auto`.
#[inline]
pub fn auto() -> Self {
PositionOrAuto::Auto
}
}
/// A generic value for the `z-index` property.
#[derive(
Animate,

View file

@ -72,11 +72,8 @@ impl<Value> Spacing<Value> {
#[cfg(feature = "gecko")]
fn line_height_moz_block_height_enabled(context: &ParserContext) -> bool {
use crate::gecko_bindings::structs;
context.in_ua_sheet() ||
unsafe {
structs::StaticPrefs::sVarCache_layout_css_line_height_moz_block_height_content_enabled
}
static_prefs::pref!("layout.css.line-height-moz-block-height.content.enabled")
}
/// A generic value for the `line-height` property.
@ -125,3 +122,33 @@ impl<N, L> LineHeight<N, L> {
LineHeight::Normal
}
}
/// Implements type for text-underline-offset and text-decoration-thickness
/// which take the grammar of auto | from-font | <length>
///
/// https://drafts.csswg.org/css-text-decor-4/
#[repr(C, u8)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Animate,
Clone,
Copy,
ComputeSquaredDistance,
ToAnimatedZero,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[allow(missing_docs)]
pub enum GenericTextDecorationLength<L> {
Length(L),
Auto,
FromFont,
}

View file

@ -65,6 +65,7 @@ trivial_to_resolved_value!(u8);
trivial_to_resolved_value!(i8);
trivial_to_resolved_value!(u16);
trivial_to_resolved_value!(u32);
trivial_to_resolved_value!(usize);
trivial_to_resolved_value!(String);
trivial_to_resolved_value!(Box<str>);
trivial_to_resolved_value!(cssparser::RGBA);

View file

@ -58,9 +58,7 @@ pub type Polygon = generic::GenericPolygon<LengthPercentage>;
#[cfg(feature = "gecko")]
fn is_clip_path_path_enabled(context: &ParserContext) -> bool {
use crate::gecko_bindings::structs::mozilla;
context.chrome_rules_enabled() ||
unsafe { mozilla::StaticPrefs::sVarCache_layout_css_clip_path_path_enabled }
context.chrome_rules_enabled() || static_prefs::pref!("layout.css.clip-path-path.enabled")
}
#[cfg(feature = "servo")]
fn is_clip_path_path_enabled(_: &ParserContext) -> bool {

View file

@ -16,6 +16,7 @@ use crate::values::specified::{AllowQuirks, Number};
use crate::values::{CustomIdent, KeyframesName};
use crate::Atom;
use cssparser::Parser;
use num_traits::FromPrimitive;
use selectors::parser::SelectorParseErrorKind;
use std::fmt::{self, Write};
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
@ -23,102 +24,55 @@ use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
#[cfg(feature = "gecko")]
fn moz_display_values_enabled(context: &ParserContext) -> bool {
use crate::gecko_bindings::structs;
context.in_ua_or_chrome_sheet() ||
unsafe { structs::StaticPrefs::sVarCache_layout_css_xul_display_values_content_enabled }
static_prefs::pref!("layout.css.xul-display-values.content.enabled")
}
#[cfg(feature = "gecko")]
fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
use crate::gecko_bindings::structs;
context.in_ua_or_chrome_sheet() ||
unsafe {
structs::StaticPrefs::sVarCache_layout_css_xul_box_display_values_content_enabled
}
}
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
fn parse_unimplemented_in_servo_2020(_context: &ParserContext) -> bool {
true
}
#[cfg(feature = "servo-layout-2020")]
fn parse_unimplemented_in_servo_2020(_context: &ParserContext) -> bool {
servo_config::prefs::pref_map()
.get("layout.2020.unimplemented")
.as_bool()
.unwrap_or(false)
static_prefs::pref!("layout.css.xul-box-display-values.content.enabled")
}
/// Defines an elements display type, which consists of
/// the two basic qualities of how an element generates boxes
/// <https://drafts.csswg.org/css-display/#propdef-display>
///
///
/// NOTE(emilio): Order is important in Gecko!
///
/// If you change it, make sure to take a look at the
/// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and
/// ensure it's still correct!
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
FromPrimitive,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum Display {
pub enum DisplayOutside {
None = 0,
Inline,
Block,
TableCaption,
InternalTable,
#[cfg(feature = "gecko")]
InternalRuby,
#[cfg(feature = "gecko")]
XUL,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum DisplayInside {
None = 0,
#[cfg(feature = "gecko")]
Contents,
Block,
FlowRoot,
Inline,
#[parse(condition = "parse_unimplemented_in_servo_2020")]
InlineBlock,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
ListItem,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
Table,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
InlineTable,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
TableRowGroup,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
TableColumn,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
TableColumnGroup,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
TableHeaderGroup,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
TableFooterGroup,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
TableRow,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
TableCell,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
TableCaption,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
#[parse(aliases = "-webkit-flex")]
Flex,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
#[parse(aliases = "-webkit-inline-flex")]
InlineFlex,
#[cfg(feature = "gecko")]
Grid,
#[cfg(feature = "gecko")]
InlineGrid,
Table,
TableRowGroup,
TableColumn,
TableColumnGroup,
TableHeaderGroup,
TableFooterGroup,
TableRow,
TableCell,
#[cfg(feature = "gecko")]
Ruby,
#[cfg(feature = "gecko")]
@ -130,46 +84,216 @@ pub enum Display {
#[cfg(feature = "gecko")]
RubyTextContainer,
#[cfg(feature = "gecko")]
Contents,
#[cfg(feature = "gecko")]
WebkitBox,
#[cfg(feature = "gecko")]
WebkitInlineBox,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_box_display_values_enabled")]
MozBox,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_box_display_values_enabled")]
MozInlineBox,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozGrid,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozInlineGrid,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozGridGroup,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozGridLine,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozStack,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozInlineStack,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozDeck,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozGroupbox,
#[cfg(feature = "gecko")]
#[parse(condition = "moz_display_values_enabled")]
MozPopup,
Flow, // only used for parsing, not computed value
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
FromPrimitive,
Hash,
MallocSizeOf,
PartialEq,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(transparent)]
pub struct Display(u16);
/// Gecko-only impl block for Display (shared stuff later in this file):
#[allow(missing_docs)]
#[allow(non_upper_case_globals)]
impl Display {
// Our u16 bits are used as follows: LOOOOOOOIIIIIIII
const LIST_ITEM_BIT: u16 = 0x8000; //^
const DISPLAY_OUTSIDE_BITS: u16 = 7; // ^^^^^^^
const DISPLAY_INSIDE_BITS: u16 = 8; // ^^^^^^^^
/// https://drafts.csswg.org/css-display/#the-display-properties
pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None);
#[cfg(feature = "gecko")]
pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents);
pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Inline);
pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot);
pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Block);
#[cfg(feature = "gecko")]
pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
pub const Flex: Self = Self::new(DisplayOutside::Block, DisplayInside::Flex);
pub const InlineFlex: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flex);
#[cfg(feature = "gecko")]
pub const Grid: Self = Self::new(DisplayOutside::Block, DisplayInside::Grid);
#[cfg(feature = "gecko")]
pub const InlineGrid: Self = Self::new(DisplayOutside::Inline, DisplayInside::Grid);
pub const Table: Self = Self::new(DisplayOutside::Block, DisplayInside::Table);
pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table);
pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Block);
#[cfg(feature = "gecko")]
pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby);
#[cfg(feature = "gecko")]
pub const WebkitBox: Self = Self::new(DisplayOutside::Block, DisplayInside::WebkitBox);
#[cfg(feature = "gecko")]
pub const WebkitInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::WebkitBox);
/// Internal table boxes.
pub const TableRowGroup: Self =
Self::new(DisplayOutside::InternalTable, DisplayInside::TableRowGroup);
pub const TableHeaderGroup: Self = Self::new(
DisplayOutside::InternalTable,
DisplayInside::TableHeaderGroup,
);
pub const TableFooterGroup: Self = Self::new(
DisplayOutside::InternalTable,
DisplayInside::TableFooterGroup,
);
pub const TableColumn: Self =
Self::new(DisplayOutside::InternalTable, DisplayInside::TableColumn);
pub const TableColumnGroup: Self = Self::new(
DisplayOutside::InternalTable,
DisplayInside::TableColumnGroup,
);
pub const TableRow: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableRow);
pub const TableCell: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableCell);
/// Internal ruby boxes.
#[cfg(feature = "gecko")]
pub const RubyBase: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyBase);
#[cfg(feature = "gecko")]
pub const RubyBaseContainer: Self = Self::new(
DisplayOutside::InternalRuby,
DisplayInside::RubyBaseContainer,
);
#[cfg(feature = "gecko")]
pub const RubyText: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyText);
#[cfg(feature = "gecko")]
pub const RubyTextContainer: Self = Self::new(
DisplayOutside::InternalRuby,
DisplayInside::RubyTextContainer,
);
/// XUL boxes.
#[cfg(feature = "gecko")]
pub const MozBox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozBox);
#[cfg(feature = "gecko")]
pub const MozInlineBox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozInlineBox);
#[cfg(feature = "gecko")]
pub const MozGrid: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGrid);
#[cfg(feature = "gecko")]
pub const MozInlineGrid: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozInlineGrid);
#[cfg(feature = "gecko")]
pub const MozGridGroup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridGroup);
#[cfg(feature = "gecko")]
pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine);
#[cfg(feature = "gecko")]
pub const MozStack: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozStack);
#[cfg(feature = "gecko")]
pub const MozInlineStack: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozInlineStack);
#[cfg(feature = "gecko")]
pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
#[cfg(feature = "gecko")]
pub const MozGroupbox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGroupbox);
#[cfg(feature = "gecko")]
pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);
/// Make a raw display value from <display-outside> and <display-inside> values.
#[inline]
const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
let o: u16 = ((outside as u8) as u16) << Self::DISPLAY_INSIDE_BITS;
let i: u16 = (inside as u8) as u16;
Self(o | i)
}
/// Make a display enum value from <display-outside> and <display-inside> values.
/// We store `flow` as a synthetic `block` or `inline` inside-value to simplify
/// our layout code.
#[inline]
fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
let inside = match inside {
DisplayInside::Flow => match outside {
DisplayOutside::Inline => DisplayInside::Inline,
_ => DisplayInside::Block,
},
_ => inside,
};
let v = Self::new(outside, inside);
if !list_item {
return v;
}
Self(v.0 | Self::LIST_ITEM_BIT)
}
/// Accessor for the <display-inside> value.
#[inline]
pub fn inside(&self) -> DisplayInside {
DisplayInside::from_u16(self.0 & ((1 << Self::DISPLAY_INSIDE_BITS) - 1)).unwrap()
}
/// Accessor for the <display-outside> value.
#[inline]
pub fn outside(&self) -> DisplayOutside {
DisplayOutside::from_u16(
(self.0 >> Self::DISPLAY_INSIDE_BITS) & ((1 << Self::DISPLAY_OUTSIDE_BITS) - 1),
)
.unwrap()
}
/// Returns whether this `display` value is some kind of list-item.
#[inline]
pub const fn is_list_item(&self) -> bool {
(self.0 & Self::LIST_ITEM_BIT) != 0
}
/// Returns whether this `display` value is a ruby level container.
pub fn is_ruby_level_container(&self) -> bool {
match *self {
#[cfg(feature = "gecko")]
Display::RubyBaseContainer | Display::RubyTextContainer => true,
_ => false,
}
}
/// Returns whether this `display` value is one of the types for ruby.
pub fn is_ruby_type(&self) -> bool {
match self.inside() {
#[cfg(feature = "gecko")]
DisplayInside::Ruby |
DisplayInside::RubyBase |
DisplayInside::RubyText |
DisplayInside::RubyBaseContainer |
DisplayInside::RubyTextContainer => true,
_ => false,
}
}
}
/// Shared Display impl for both Gecko and Servo.
#[allow(non_upper_case_globals)]
impl Display {
/// The initial display value.
#[inline]
@ -183,22 +307,20 @@ impl Display {
pub fn is_atomic_inline_level(&self) -> bool {
match *self {
Display::InlineBlock => true,
#[cfg(feature = "servo-layout-2013")]
Display::InlineFlex | Display::InlineTable => true,
_ => false,
}
}
/// Returns whether this "display" value is the display of a flex or
/// Returns whether this `display` value is the display of a flex or
/// grid container.
///
/// This is used to implement various style fixups.
pub fn is_item_container(&self) -> bool {
match *self {
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
Display::Flex | Display::InlineFlex => true,
match self.inside() {
DisplayInside::Flex => true,
#[cfg(feature = "gecko")]
Display::Grid | Display::InlineGrid => true,
DisplayInside::Grid => true,
_ => false,
}
}
@ -215,80 +337,50 @@ impl Display {
}
}
/// Returns whether this "display" value is one of the types for
/// ruby.
#[cfg(feature = "gecko")]
pub fn is_ruby_type(&self) -> bool {
matches!(
*self,
Display::Ruby |
Display::RubyBase |
Display::RubyText |
Display::RubyBaseContainer |
Display::RubyTextContainer
)
}
/// Returns whether this "display" value is a ruby level container.
#[cfg(feature = "gecko")]
pub fn is_ruby_level_container(&self) -> bool {
matches!(
*self,
Display::RubyBaseContainer | Display::RubyTextContainer
)
}
/// Convert this display into an equivalent block display.
///
/// Also used for style adjustments.
/// Also used for :root style adjustments.
pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
match *self {
// Values that have a corresponding block-outside version.
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
Display::InlineTable => Display::Table,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
Display::InlineFlex => Display::Flex,
#[cfg(feature = "gecko")]
{
// Special handling for `contents` and `list-item`s on the root element.
if _is_root_element && (self.is_contents() || self.is_list_item()) {
return Display::Block;
}
}
#[cfg(feature = "gecko")]
Display::InlineGrid => Display::Grid,
#[cfg(feature = "gecko")]
Display::WebkitInlineBox => Display::WebkitBox,
// Special handling for contents and list-item on the root
// element for Gecko.
#[cfg(feature = "gecko")]
Display::Contents | Display::ListItem if _is_root_element => Display::Block,
// These are not changed by blockification.
Display::None | Display::Block => *self,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
Display::Flex | Display::ListItem | Display::Table => *self,
#[cfg(feature = "gecko")]
Display::Contents | Display::FlowRoot | Display::Grid | Display::WebkitBox => *self,
// Everything else becomes block.
match self.outside() {
DisplayOutside::Inline => {
let inside = match self.inside() {
DisplayInside::Inline | DisplayInside::FlowRoot => DisplayInside::Block,
inside => inside,
};
Display::from3(DisplayOutside::Block, inside, self.is_list_item())
},
DisplayOutside::Block | DisplayOutside::None => *self,
_ => Display::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.
/// Convert this display into an equivalent inline-outside display.
/// https://drafts.csswg.org/css-display/#inlinify
#[cfg(feature = "gecko")]
pub fn inlinify(&self) -> Self {
match *self {
Display::Block | Display::FlowRoot => Display::InlineBlock,
Display::Table => Display::InlineTable,
Display::Flex => Display::InlineFlex,
Display::Grid => Display::InlineGrid,
// XXX bug 1105868 this should probably be InlineListItem:
Display::ListItem => Display::Inline,
Display::MozBox => Display::MozInlineBox,
Display::MozStack => Display::MozInlineStack,
Display::WebkitBox => Display::WebkitInlineBox,
other => other,
match self.outside() {
DisplayOutside::Block => {
let inside = match self.inside() {
DisplayInside::Block => DisplayInside::FlowRoot,
inside => inside,
};
Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
},
#[cfg(feature = "gecko")]
DisplayOutside::XUL => match self.inside() {
DisplayInside::MozBox => Display::MozInlineBox,
DisplayInside::MozStack => Display::MozInlineStack,
_ => *self,
},
_ => *self,
}
}
@ -309,6 +401,263 @@ impl Display {
}
}
impl ToCss for Display {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
debug_assert_ne!(
self.inside(),
DisplayInside::Flow,
"`flow` never appears in `display` computed value"
);
let outside = self.outside();
let inside = match self.inside() {
DisplayInside::Block | DisplayInside::Inline => DisplayInside::Flow,
inside => inside,
};
match *self {
Display::Block | Display::Inline => outside.to_css(dest),
Display::InlineBlock => dest.write_str("inline-block"),
#[cfg(feature = "gecko")]
Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
#[cfg(feature = "gecko")]
Display::MozInlineBox => dest.write_str("-moz-inline-box"),
#[cfg(feature = "gecko")]
Display::MozInlineGrid => dest.write_str("-moz-inline-grid"),
#[cfg(feature = "gecko")]
Display::MozInlineStack => dest.write_str("-moz-inline-stack"),
Display::TableCaption => dest.write_str("table-caption"),
_ => match (outside, inside) {
#[cfg(feature = "gecko")]
(DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
(DisplayOutside::Inline, DisplayInside::Flex) |
(DisplayOutside::Inline, DisplayInside::Table) => {
dest.write_str("inline-")?;
inside.to_css(dest)
},
#[cfg(feature = "gecko")]
(DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
(_, inside) => {
if self.is_list_item() {
if outside != DisplayOutside::Block {
outside.to_css(dest)?;
dest.write_str(" ")?;
}
if inside != DisplayInside::Flow {
inside.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("list-item")
} else {
inside.to_css(dest)
}
},
},
}
}
}
/// <display-inside> = flow | flow-root | table | flex | grid | ruby
/// https://drafts.csswg.org/css-display/#typedef-display-inside
fn parse_display_inside<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<DisplayInside, ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input,
"flow" => DisplayInside::Flow,
#[cfg(feature = "gecko")]
"flow-root" => DisplayInside::FlowRoot,
"table" => DisplayInside::Table,
"flex" => DisplayInside::Flex,
#[cfg(feature = "gecko")]
"grid" => DisplayInside::Grid,
#[cfg(feature = "gecko")]
"ruby" => DisplayInside::Ruby,
})
}
/// <display-outside> = block | inline | run-in
/// https://drafts.csswg.org/css-display/#typedef-display-outside
fn parse_display_outside<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<DisplayOutside, ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input,
"block" => DisplayOutside::Block,
"inline" => DisplayOutside::Inline,
// FIXME(bug 2056): not supported in layout yet:
//"run-in" => DisplayOutside::RunIn,
})
}
/// (flow | flow-root)?
fn parse_display_inside_for_list_item<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<DisplayInside, ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input,
"flow" => DisplayInside::Flow,
#[cfg(feature = "gecko")]
"flow-root" => DisplayInside::FlowRoot,
})
}
/// Test a <display-inside> Result for same values as above.
fn is_valid_inside_for_list_item<'i>(inside: &Result<DisplayInside, ParseError<'i>>) -> bool {
match inside {
Ok(DisplayInside::Flow) => true,
#[cfg(feature = "gecko")]
Ok(DisplayInside::FlowRoot) => true,
_ => false,
}
}
/// Parse `list-item`.
fn parse_list_item<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input,
"list-item" => (),
})
}
impl Parse for Display {
#[allow(unused)] // `context` isn't used for servo-2020 for now
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Display, ParseError<'i>> {
// Parse all combinations of <display-inside/outside>? and `list-item`? first.
let mut got_list_item = input.try(parse_list_item).is_ok();
let mut inside = if got_list_item {
input.try(parse_display_inside_for_list_item)
} else {
input.try(parse_display_inside)
};
// <display-listitem> = <display-outside>? && [ flow | flow-root ]? && list-item
// https://drafts.csswg.org/css-display/#typedef-display-listitem
if !got_list_item && is_valid_inside_for_list_item(&inside) {
got_list_item = input.try(parse_list_item).is_ok();
}
let outside = input.try(parse_display_outside);
if outside.is_ok() {
if !got_list_item && (inside.is_err() || is_valid_inside_for_list_item(&inside)) {
got_list_item = input.try(parse_list_item).is_ok();
}
if inside.is_err() {
inside = if got_list_item {
input.try(parse_display_inside_for_list_item)
} else {
input.try(parse_display_inside)
};
if !got_list_item && is_valid_inside_for_list_item(&inside) {
got_list_item = input.try(parse_list_item).is_ok();
}
}
}
if got_list_item || inside.is_ok() || outside.is_ok() {
let inside = inside.unwrap_or(DisplayInside::Flow);
let outside = outside.unwrap_or(match inside {
// "If <display-outside> is omitted, the elements outside display type
// defaults to block — except for ruby, which defaults to inline."
// https://drafts.csswg.org/css-display/#inside-model
#[cfg(feature = "gecko")]
DisplayInside::Ruby => DisplayOutside::Inline,
_ => DisplayOutside::Block,
});
return Ok(Display::from3(outside, inside, got_list_item));
}
// Now parse the single-keyword `display` values.
Ok(try_match_ident_ignore_ascii_case! { input,
"none" => Display::None,
#[cfg(feature = "gecko")]
"contents" => Display::Contents,
"inline-block" => Display::InlineBlock,
"inline-table" => Display::InlineTable,
"-webkit-flex" => Display::Flex,
"inline-flex" | "-webkit-inline-flex" => Display::InlineFlex,
#[cfg(feature = "gecko")]
"inline-grid" => Display::InlineGrid,
"table-caption" => Display::TableCaption,
"table-row-group" => Display::TableRowGroup,
"table-header-group" => Display::TableHeaderGroup,
"table-footer-group" => Display::TableFooterGroup,
"table-column" => Display::TableColumn,
"table-column-group" => Display::TableColumnGroup,
"table-row" => Display::TableRow,
"table-cell" => Display::TableCell,
#[cfg(feature = "gecko")]
"ruby-base" => Display::RubyBase,
#[cfg(feature = "gecko")]
"ruby-base-container" => Display::RubyBaseContainer,
#[cfg(feature = "gecko")]
"ruby-text" => Display::RubyText,
#[cfg(feature = "gecko")]
"ruby-text-container" => Display::RubyTextContainer,
#[cfg(feature = "gecko")]
"-webkit-box" => Display::WebkitBox,
#[cfg(feature = "gecko")]
"-webkit-inline-box" => Display::WebkitInlineBox,
#[cfg(feature = "gecko")]
"-moz-box" if moz_box_display_values_enabled(context) => Display::MozBox,
#[cfg(feature = "gecko")]
"-moz-inline-box" if moz_box_display_values_enabled(context) => Display::MozInlineBox,
#[cfg(feature = "gecko")]
"-moz-grid" if moz_display_values_enabled(context) => Display::MozGrid,
#[cfg(feature = "gecko")]
"-moz-inline-grid" if moz_display_values_enabled(context) => Display::MozInlineGrid,
#[cfg(feature = "gecko")]
"-moz-grid-group" if moz_display_values_enabled(context) => Display::MozGridGroup,
#[cfg(feature = "gecko")]
"-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine,
#[cfg(feature = "gecko")]
"-moz-stack" if moz_display_values_enabled(context) => Display::MozStack,
#[cfg(feature = "gecko")]
"-moz-inline-stack" if moz_display_values_enabled(context) => Display::MozInlineStack,
#[cfg(feature = "gecko")]
"-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
#[cfg(feature = "gecko")]
"-moz-groupbox" if moz_display_values_enabled(context) => Display::MozGroupbox,
#[cfg(feature = "gecko")]
"-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
})
}
}
impl SpecifiedValueInfo for Display {
fn collect_completion_keywords(f: KeywordsCollectFn) {
f(&[
"block",
"contents",
"flex",
"flow-root",
"grid",
"inline",
"inline-block",
"inline-flex",
"inline-grid",
"inline-table",
"inline list-item",
"inline flow-root list-item",
"list-item",
"none",
"block ruby",
"ruby",
"ruby-base",
"ruby-base-container",
"ruby-text",
"ruby-text-container",
"table",
"table-caption",
"table-cell",
"table-column",
"table-column-group",
"table-footer-group",
"table-header-group",
"table-row",
"table-row-group",
"-webkit-box",
"-webkit-inline-box",
]);
}
}
/// A specified value for the `vertical-align` property.
pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;

View file

@ -6,10 +6,9 @@
//! [grids](https://drafts.csswg.org/css-grid/)
use crate::parser::{Parse, ParserContext};
use crate::values::computed::{self, Context, ToComputedValue};
use crate::values::generics::grid::{GridTemplateComponent, RepeatCount, TrackBreadth};
use crate::values::generics::grid::{LineNameList, TrackRepeat, TrackSize};
use crate::values::generics::grid::{TrackList, TrackListType, TrackListValue};
use crate::values::generics::grid::{GridTemplateComponent, ImplicitGridTracks, RepeatCount};
use crate::values::generics::grid::{LineNameList, TrackBreadth, TrackRepeat, TrackSize};
use crate::values::generics::grid::{TrackList, TrackListValue};
use crate::values::specified::{Integer, LengthPercentage};
use crate::values::{CSSFloat, CustomIdent};
use cssparser::{ParseError as CssParseError, Parser, Token};
@ -96,12 +95,27 @@ impl Parse for TrackSize<LengthPercentage> {
}
}
impl Parse for ImplicitGridTracks<TrackSize<LengthPercentage>> {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
use style_traits::{Separator, Space};
let track_sizes = Space::parse(input, |i| TrackSize::parse(context, i))?;
if track_sizes.len() == 1 && track_sizes[0].is_auto() {
//`auto`, which is the initial value, is always represented by an empty slice.
return Ok(Default::default());
}
return Ok(ImplicitGridTracks(track_sizes.into()));
}
}
/// Parse the grid line names into a vector of owned strings.
///
/// <https://drafts.csswg.org/css-grid/#typedef-line-names>
pub fn parse_line_names<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<Box<[CustomIdent]>, ParseError<'i>> {
) -> Result<crate::OwnedSlice<CustomIdent>, ParseError<'i>> {
input.expect_square_bracket_block()?;
input.parse_nested_block(|input| {
let mut values = vec![];
@ -112,7 +126,7 @@ pub fn parse_line_names<'i, 't>(
values.push(ident);
}
Ok(values.into_boxed_slice())
Ok(values.into())
})
}
@ -155,9 +169,7 @@ impl TrackRepeat<LengthPercentage, Integer> {
let mut current_names;
loop {
current_names = input
.try(parse_line_names)
.unwrap_or(vec![].into_boxed_slice());
current_names = input.try(parse_line_names).unwrap_or_default();
if let Ok(track_size) = input.try(|i| TrackSize::parse(context, i)) {
if !track_size.is_fixed() {
if is_auto {
@ -180,11 +192,7 @@ impl TrackRepeat<LengthPercentage, Integer> {
// one `TrackSize`. But in current version of the spec, this is deprecated
// but we are adding this for gecko parity. We should remove this when
// gecko implements new spec.
names.push(
input
.try(parse_line_names)
.unwrap_or(vec![].into_boxed_slice()),
);
names.push(input.try(parse_line_names).unwrap_or_default());
break;
}
} else {
@ -201,9 +209,9 @@ impl TrackRepeat<LengthPercentage, Integer> {
}
let repeat = TrackRepeat {
count: count,
track_sizes: values,
line_names: names.into_boxed_slice(),
count,
track_sizes: values.into(),
line_names: names.into(),
};
Ok((repeat, repeat_type))
@ -221,47 +229,31 @@ impl Parse for TrackList<LengthPercentage, Integer> {
let mut names = vec![];
let mut values = vec![];
// assume it's the simplest case.
let mut list_type = TrackListType::Explicit;
// holds <auto-repeat> value. It can only be only one in a TrackList.
let mut auto_repeat = None;
// if there is any <auto-repeat> the list will be of type TrackListType::Auto(idx)
// where idx points to the position of the <auto-repeat> in the track list. If there
// is any repeat before <auto-repeat>, we need to take the number of repetitions into
// account to set the position of <auto-repeat> so it remains the same while computing
// values.
let mut auto_offset = 0;
// Whether we've parsed an `<auto-repeat>` value.
let mut auto_repeat_index = None;
// assume that everything is <fixed-size>. This flag is useful when we encounter <auto-repeat>
let mut atleast_one_not_fixed = false;
let mut at_least_one_not_fixed = false;
loop {
current_names.extend_from_slice(
&mut input
.try(parse_line_names)
.unwrap_or(vec![].into_boxed_slice()),
);
current_names.extend_from_slice(&mut input.try(parse_line_names).unwrap_or_default());
if let Ok(track_size) = input.try(|i| TrackSize::parse(context, i)) {
if !track_size.is_fixed() {
atleast_one_not_fixed = true;
if auto_repeat.is_some() {
at_least_one_not_fixed = true;
if auto_repeat_index.is_some() {
// <auto-track-list> only accepts <fixed-size> and <fixed-repeat>
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
}
let vec = mem::replace(&mut current_names, vec![]);
names.push(vec.into_boxed_slice());
names.push(vec.into());
values.push(TrackListValue::TrackSize(track_size));
} else if let Ok((repeat, type_)) =
input.try(|i| TrackRepeat::parse_with_repeat_type(context, i))
{
if list_type == TrackListType::Explicit {
list_type = TrackListType::Normal; // <explicit-track-list> doesn't contain repeat()
}
match type_ {
RepeatType::Normal => {
atleast_one_not_fixed = true;
if auto_repeat.is_some() {
at_least_one_not_fixed = true;
if auto_repeat_index.is_some() {
// only <fixed-repeat>
return Err(
input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
@ -269,137 +261,42 @@ impl Parse for TrackList<LengthPercentage, Integer> {
}
},
RepeatType::Auto => {
if auto_repeat.is_some() || atleast_one_not_fixed {
if auto_repeat_index.is_some() || at_least_one_not_fixed {
// We've either seen <auto-repeat> earlier, or there's at least one non-fixed value
return Err(
input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
);
}
list_type = TrackListType::Auto(values.len() as u16 + auto_offset);
auto_repeat = Some(repeat);
let vec = mem::replace(&mut current_names, vec![]);
names.push(vec.into_boxed_slice());
continue;
auto_repeat_index = Some(values.len());
},
RepeatType::Fixed => (),
RepeatType::Fixed => {},
}
let vec = mem::replace(&mut current_names, vec![]);
names.push(vec.into_boxed_slice());
if let RepeatCount::Number(num) = repeat.count {
auto_offset += (num.value() - 1) as u16;
}
names.push(vec.into());
values.push(TrackListValue::TrackRepeat(repeat));
} else {
if values.is_empty() && auto_repeat.is_none() {
if values.is_empty() && auto_repeat_index.is_none() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
names.push(current_names.into_boxed_slice());
names.push(current_names.into());
break;
}
}
Ok(TrackList {
list_type: list_type,
values: values,
line_names: names.into_boxed_slice(),
auto_repeat: auto_repeat,
auto_repeat_index: auto_repeat_index.unwrap_or(std::usize::MAX),
values: values.into(),
line_names: names.into(),
})
}
}
impl ToComputedValue for TrackList<LengthPercentage, Integer> {
type ComputedValue = TrackList<computed::LengthPercentage, computed::Integer>;
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
// Merge the line names while computing values. The resulting values will
// all be bunch of `<track-size>` and one <auto-repeat>.
//
// For example,
// `[a b] 100px [c d] repeat(1, 30px [g]) [h]` will be merged as `[a b] 100px [c d] 30px [g h]`
// whereas, `[a b] repeat(2, [c] 50px [d]) [e f] repeat(auto-fill, [g] 12px) 10px [h]` will be merged as
// `[a b c] 50px [d c] 50px [d e f] repeat(auto-fill, [g] 12px) 10px [h]`, with the `<auto-repeat>` value
// set in the `auto_repeat` field, and the `idx` in TrackListType::Auto pointing to the values after
// `<auto-repeat>` (in this case, `10px [h]`).
let mut prev_names = vec![];
let mut line_names = Vec::with_capacity(self.line_names.len() + 1);
let mut values = Vec::with_capacity(self.values.len() + 1);
for (pos, names) in self.line_names.iter().enumerate() {
prev_names.extend_from_slice(&names);
if pos >= self.values.len() {
let vec = mem::replace(&mut prev_names, vec![]);
line_names.push(vec.into_boxed_slice());
continue;
}
match self.values[pos] {
TrackListValue::TrackSize(ref size) => {
let vec = mem::replace(&mut prev_names, vec![]);
line_names.push(vec.into_boxed_slice());
values.push(TrackListValue::TrackSize(size.to_computed_value(context)));
},
TrackListValue::TrackRepeat(ref repeat) => {
// If the repeat count is numeric, we expand and merge the values.
let mut repeat = repeat.expand();
let mut repeat_names_iter = repeat.line_names.iter();
for (size, repeat_names) in
repeat.track_sizes.drain(..).zip(&mut repeat_names_iter)
{
prev_names.extend_from_slice(&repeat_names);
let vec = mem::replace(&mut prev_names, vec![]);
line_names.push(vec.into_boxed_slice());
values.push(TrackListValue::TrackSize(size.to_computed_value(context)));
}
if let Some(names) = repeat_names_iter.next() {
prev_names.extend_from_slice(&names);
}
},
}
}
TrackList {
list_type: self.list_type.to_computed_value(context),
values: values,
line_names: line_names.into_boxed_slice(),
auto_repeat: self
.auto_repeat
.clone()
.map(|repeat| repeat.to_computed_value(context)),
}
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
let mut values = Vec::with_capacity(computed.values.len() + 1);
for value in computed
.values
.iter()
.map(ToComputedValue::from_computed_value)
{
values.push(value);
}
TrackList {
list_type: computed.list_type,
values: values,
line_names: computed.line_names.clone(),
auto_repeat: computed
.auto_repeat
.clone()
.map(|ref repeat| TrackRepeat::from_computed_value(repeat)),
}
}
}
#[cfg(feature = "gecko")]
#[inline]
fn allow_grid_template_subgrids() -> bool {
use crate::gecko_bindings::structs::mozilla;
unsafe { mozilla::StaticPrefs::sVarCache_layout_css_grid_template_subgrid_value_enabled }
static_prefs::pref!("layout.css.grid-template-subgrid-value.enabled")
}
#[cfg(feature = "servo")]
@ -409,7 +306,6 @@ fn allow_grid_template_subgrids() -> bool {
}
impl Parse for GridTemplateComponent<LengthPercentage, Integer> {
// FIXME: Derive Parse (probably with None_)
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
@ -430,10 +326,11 @@ impl GridTemplateComponent<LengthPercentage, Integer> {
) -> Result<Self, ParseError<'i>> {
if allow_grid_template_subgrids() {
if let Ok(t) = input.try(|i| LineNameList::parse(context, i)) {
return Ok(GridTemplateComponent::Subgrid(t));
return Ok(GridTemplateComponent::Subgrid(Box::new(t)));
}
}
TrackList::parse(context, input).map(GridTemplateComponent::TrackList)
let track_list = TrackList::parse(context, input)?;
Ok(GridTemplateComponent::TrackList(Box::new(track_list)))
}
}

View file

@ -8,6 +8,7 @@
use super::computed::transform::DirectionVector;
use super::computed::{Context, ToComputedValue};
use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo;
@ -69,7 +70,7 @@ pub use self::list::Quotes;
pub use self::motion::{OffsetPath, OffsetRotate};
pub use self::outline::OutlineStyle;
pub use self::percentage::Percentage;
pub use self::position::{GridAutoFlow, GridTemplateAreas, Position};
pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto};
pub use self::position::{PositionComponent, ZIndex};
pub use self::rect::NonNegativeLengthOrNumberRect;
pub use self::resolution::Resolution;
@ -81,7 +82,7 @@ pub use self::table::XSpan;
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign};
pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
pub use self::text::{TextDecorationSkipInk, TextTransform};
pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextTransform};
pub use self::time::Time;
pub use self::transform::{Rotate, Scale, Transform};
pub use self::transform::{TransformOrigin, TransformStyle, Translate};
@ -626,6 +627,9 @@ pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
/// The specified value of a grid `<track-size>`
pub type TrackSize = GenericTrackSize<LengthPercentage>;
/// The specified value of a grid `<track-size>+`
pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
/// The specified value of a grid `<track-list>`
/// (could also be `<auto-track-list>` or `<explicit-track-list>`)
pub type TrackList = GenericTrackList<LengthPercentage, Integer>;

View file

@ -13,6 +13,7 @@ use crate::str::HTML_SPACE_CHARACTERS;
use crate::values::computed::LengthPercentage as ComputedLengthPercentage;
use crate::values::computed::{Context, Percentage, ToComputedValue};
use crate::values::generics::position::Position as GenericPosition;
use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
use crate::values::generics::position::ZIndex as GenericZIndex;
use crate::values::specified::{AllowQuirks, Integer, LengthPercentage};
use crate::Atom;
@ -26,6 +27,9 @@ use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
/// The specified value of a CSS `<position>`
pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
/// The specified value of an `auto | <position>`.
pub type PositionOrAuto = GenericPositionOrAuto<Position>;
/// The specified value of a horizontal position.
pub type HorizontalPosition = PositionComponent<HorizontalPositionKeyword>;

View file

@ -32,8 +32,7 @@ pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPerce
/// Whether the `context-value` value is enabled.
#[cfg(feature = "gecko")]
pub fn is_context_value_enabled() -> bool {
use crate::gecko_bindings::structs::mozilla;
unsafe { mozilla::StaticPrefs::sVarCache_gfx_font_rendering_opentype_svg_enabled }
static_prefs::pref!("gfx.font_rendering.opentype_svg.enabled")
}
/// Whether the `context-value` value is enabled.

View file

@ -625,40 +625,26 @@ impl<'a> PathParser<'a> {
break;
}
match self.chars.next() {
Some(command) => {
let abs = if command.is_ascii_uppercase() {
IsAbsolute::Yes
} else {
IsAbsolute::No
};
macro_rules! parse_command {
( $($($p:pat)|+ => $parse_func:ident,)* ) => {
match command {
$(
$($p)|+ => {
skip_wsp(&mut self.chars);
self.$parse_func(abs)?;
},
)*
_ => return Err(()),
}
}
}
parse_command!(
b'Z' | b'z' => parse_closepath,
b'L' | b'l' => parse_lineto,
b'H' | b'h' => parse_h_lineto,
b'V' | b'v' => parse_v_lineto,
b'C' | b'c' => parse_curveto,
b'S' | b's' => parse_smooth_curveto,
b'Q' | b'q' => parse_quadratic_bezier_curveto,
b'T' | b't' => parse_smooth_quadratic_bezier_curveto,
b'A' | b'a' => parse_elliptical_arc,
);
},
_ => break, // no more commands.
}
let command = self.chars.next().unwrap();
let abs = if command.is_ascii_uppercase() {
IsAbsolute::Yes
} else {
IsAbsolute::No
};
skip_wsp(&mut self.chars);
match command {
b'Z' | b'z' => self.parse_closepath(),
b'L' | b'l' => self.parse_lineto(abs),
b'H' | b'h' => self.parse_h_lineto(abs),
b'V' | b'v' => self.parse_v_lineto(abs),
b'C' | b'c' => self.parse_curveto(abs),
b'S' | b's' => self.parse_smooth_curveto(abs),
b'Q' | b'q' => self.parse_quadratic_bezier_curveto(abs),
b'T' | b't' => self.parse_smooth_quadratic_bezier_curveto(abs),
b'A' | b'a' => self.parse_elliptical_arc(abs),
_ => return Err(()),
}?;
}
Ok(())
}
@ -692,7 +678,7 @@ impl<'a> PathParser<'a> {
}
/// Parse "closepath" command.
fn parse_closepath(&mut self, _absolute: IsAbsolute) -> Result<(), ()> {
fn parse_closepath(&mut self) -> Result<(), ()> {
self.path.push(PathCommand::ClosePath);
Ok(())
}

View file

@ -7,13 +7,12 @@
use crate::parser::{Parse, ParserContext};
use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
use crate::values::computed::text::LineHeight as ComputedLineHeight;
use crate::values::computed::text::TextEmphasisKeywordValue as ComputedTextEmphasisKeywordValue;
use crate::values::computed::text::TextEmphasisStyle as ComputedTextEmphasisStyle;
use crate::values::computed::text::TextOverflow as ComputedTextOverflow;
use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::text::InitialLetter as GenericInitialLetter;
use crate::values::generics::text::LineHeight as GenericLineHeight;
use crate::values::generics::text::Spacing;
use crate::values::generics::text::{GenericTextDecorationLength, Spacing};
use crate::values::specified::length::NonNegativeLengthPercentage;
use crate::values::specified::length::{FontRelativeLength, Length};
use crate::values::specified::length::{LengthPercentage, NoCalcLength};
@ -645,48 +644,34 @@ impl ToComputedValue for TextAlign {
}
}
fn fill_mode_is_default_and_shape_exists(
fill: &TextEmphasisFillMode,
shape: &Option<TextEmphasisShapeKeyword>,
) -> bool {
shape.is_some() && fill.is_filled()
}
/// Specified value of text-emphasis-style property.
///
/// https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
#[allow(missing_docs)]
pub enum TextEmphasisStyle {
/// <fill> <shape>
Keyword(TextEmphasisKeywordValue),
/// [ <fill> || <shape> ]
Keyword {
#[css(contextual_skip_if = "fill_mode_is_default_and_shape_exists")]
fill: TextEmphasisFillMode,
shape: Option<TextEmphasisShapeKeyword>,
},
/// `none`
None,
/// String (will be used only first grapheme cluster) for the text-emphasis-style property
String(String),
}
/// Keyword value for the text-emphasis-style property
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub enum TextEmphasisKeywordValue {
/// <fill>
Fill(TextEmphasisFillMode),
/// <shape>
Shape(TextEmphasisShapeKeyword),
/// <fill> <shape>
FillAndShape(TextEmphasisFillMode, TextEmphasisShapeKeyword),
}
impl TextEmphasisKeywordValue {
fn fill(&self) -> Option<TextEmphasisFillMode> {
match *self {
TextEmphasisKeywordValue::Fill(fill) |
TextEmphasisKeywordValue::FillAndShape(fill, _) => Some(fill),
_ => None,
}
}
fn shape(&self) -> Option<TextEmphasisShapeKeyword> {
match *self {
TextEmphasisKeywordValue::Shape(shape) |
TextEmphasisKeywordValue::FillAndShape(_, shape) => Some(shape),
_ => None,
}
}
/// `<string>` (of which only the first grapheme cluster will be used).
String(crate::OwnedStr),
}
/// Fill mode for the text-emphasis-style property
#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
#[repr(u8)]
pub enum TextEmphasisFillMode {
/// `filled`
Filled,
@ -694,10 +679,19 @@ pub enum TextEmphasisFillMode {
Open,
}
impl TextEmphasisFillMode {
/// Whether the value is `filled`.
#[inline]
pub fn is_filled(&self) -> bool {
matches!(*self, TextEmphasisFillMode::Filled)
}
}
/// Shape keyword for the text-emphasis-style property
#[derive(
Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
)]
#[repr(u8)]
pub enum TextEmphasisShapeKeyword {
/// `dot`
Dot,
@ -711,77 +705,39 @@ pub enum TextEmphasisShapeKeyword {
Sesame,
}
impl TextEmphasisShapeKeyword {
/// converts fill mode to a unicode char
pub fn char(&self, fill: TextEmphasisFillMode) -> &str {
let fill = fill == TextEmphasisFillMode::Filled;
match *self {
TextEmphasisShapeKeyword::Dot => {
if fill {
"\u{2022}"
} else {
"\u{25e6}"
}
},
TextEmphasisShapeKeyword::Circle => {
if fill {
"\u{25cf}"
} else {
"\u{25cb}"
}
},
TextEmphasisShapeKeyword::DoubleCircle => {
if fill {
"\u{25c9}"
} else {
"\u{25ce}"
}
},
TextEmphasisShapeKeyword::Triangle => {
if fill {
"\u{25b2}"
} else {
"\u{25b3}"
}
},
TextEmphasisShapeKeyword::Sesame => {
if fill {
"\u{fe45}"
} else {
"\u{fe46}"
}
},
}
}
}
impl ToComputedValue for TextEmphasisStyle {
type ComputedValue = ComputedTextEmphasisStyle;
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
TextEmphasisStyle::Keyword(ref keyword) => {
// FIXME(emilio): This should set the rule_cache_conditions
// properly.
let default_shape = if context.style().get_inherited_box().clone_writing_mode() ==
SpecifiedWritingMode::HorizontalTb
{
TextEmphasisShapeKeyword::Circle
} else {
TextEmphasisShapeKeyword::Sesame
};
ComputedTextEmphasisStyle::Keyword(ComputedTextEmphasisKeywordValue {
fill: keyword.fill().unwrap_or(TextEmphasisFillMode::Filled),
shape: keyword.shape().unwrap_or(default_shape),
})
TextEmphasisStyle::Keyword { fill, shape } => {
let shape = shape.unwrap_or_else(|| {
// FIXME(emilio, bug 1572958): This should set the
// rule_cache_conditions properly.
//
// Also should probably use WritingMode::is_vertical rather
// than the computed value of the `writing-mode` property.
if context.style().get_inherited_box().clone_writing_mode() ==
SpecifiedWritingMode::HorizontalTb
{
TextEmphasisShapeKeyword::Circle
} else {
TextEmphasisShapeKeyword::Sesame
}
});
ComputedTextEmphasisStyle::Keyword { fill, shape }
},
TextEmphasisStyle::None => ComputedTextEmphasisStyle::None,
TextEmphasisStyle::String(ref s) => {
// Passing `true` to iterate over extended grapheme clusters, following
// recommendation at http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
let string = s.graphemes(true).next().unwrap_or("").to_string();
ComputedTextEmphasisStyle::String(string)
//
// FIXME(emilio): Doing this at computed value time seems wrong.
// The spec doesn't say that this should be a computed-value
// time operation. This is observable from getComputedStyle().
let s = s.graphemes(true).next().unwrap_or("").to_string();
ComputedTextEmphasisStyle::String(s.into())
},
}
}
@ -789,9 +745,10 @@ impl ToComputedValue for TextEmphasisStyle {
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
match *computed {
ComputedTextEmphasisStyle::Keyword(ref keyword) => TextEmphasisStyle::Keyword(
TextEmphasisKeywordValue::FillAndShape(keyword.fill, keyword.shape),
),
ComputedTextEmphasisStyle::Keyword { fill, shape } => TextEmphasisStyle::Keyword {
fill,
shape: Some(shape),
},
ComputedTextEmphasisStyle::None => TextEmphasisStyle::None,
ComputedTextEmphasisStyle::String(ref string) => {
TextEmphasisStyle::String(string.clone())
@ -814,7 +771,7 @@ impl Parse for TextEmphasisStyle {
if let Ok(s) = input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned())) {
// Handle <string>
return Ok(TextEmphasisStyle::String(s));
return Ok(TextEmphasisStyle::String(s.into()));
}
// Handle a pair of keywords
@ -824,14 +781,17 @@ impl Parse for TextEmphasisStyle {
shape = input.try(TextEmphasisShapeKeyword::parse).ok();
}
// At least one of shape or fill must be handled
let keyword_value = match (fill, shape) {
(Some(fill), Some(shape)) => TextEmphasisKeywordValue::FillAndShape(fill, shape),
(Some(fill), None) => TextEmphasisKeywordValue::Fill(fill),
(None, Some(shape)) => TextEmphasisKeywordValue::Shape(shape),
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
};
Ok(TextEmphasisStyle::Keyword(keyword_value))
if shape.is_none() && fill.is_none() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
// If a shape keyword is specified but neither filled nor open is
// specified, filled is assumed.
let fill = fill.unwrap_or(TextEmphasisFillMode::Filled);
// We cannot do the same because the default `<shape>` depends on the
// computed writing-mode.
Ok(TextEmphasisStyle::Keyword { fill, shape })
}
}
@ -1079,3 +1039,20 @@ pub enum TextDecorationSkipInk {
Auto,
None,
}
/// Implements type for `text-underline-offset` and `text-decoration-thickness` properties
pub type TextDecorationLength = GenericTextDecorationLength<Length>;
impl TextDecorationLength {
/// `Auto` value.
#[inline]
pub fn auto() -> Self {
GenericTextDecorationLength::Auto
}
/// Whether this is the `Auto` value.
#[inline]
pub fn is_auto(&self) -> bool {
matches!(*self, GenericTextDecorationLength::Auto)
}
}

View file

@ -199,7 +199,9 @@ pub enum CursorKind {
Move,
NoDrop,
NotAllowed,
#[parse(aliases = "-moz-grab")]
Grab,
#[parse(aliases = "-moz-grabbing")]
Grabbing,
EResize,
NResize,
@ -216,15 +218,9 @@ pub enum CursorKind {
ColResize,
RowResize,
AllScroll,
#[parse(aliases = "-moz-zoom-in")]
ZoomIn,
#[parse(aliases = "-moz-zoom-out")]
ZoomOut,
Auto,
#[cfg(feature = "gecko")]
MozGrab,
#[cfg(feature = "gecko")]
MozGrabbing,
#[cfg(feature = "gecko")]
MozZoomIn,
#[cfg(feature = "gecko")]
MozZoomOut,
}

View file

@ -82,6 +82,7 @@ impl SpecifiedValueInfo for i32 {}
impl SpecifiedValueInfo for u8 {}
impl SpecifiedValueInfo for u16 {}
impl SpecifiedValueInfo for u32 {}
impl SpecifiedValueInfo for usize {}
impl SpecifiedValueInfo for str {}
impl SpecifiedValueInfo for String {}
impl SpecifiedValueInfo for crate::owned_str::OwnedStr {}

View file

@ -56,6 +56,7 @@ files = [
"./components/net/tests/parsable_mime/text",
# Mako does not lend itself easily to splitting long lines
"./components/style/properties/helpers/animated_properties.mako.rs",
"./components/style/properties/shorthands/text.mako.rs",
# Long regexes are long.
"./components/style/gecko/regen_atoms.py",
# Helper macro where actually a pseudo-element per line makes sense.

View file

@ -1,2 +0,0 @@
[font-weight-xxx-large.html]
expected: FAIL

View file

@ -1,4 +0,0 @@
[font-size-valid.html]
[e.style['font-size'\] = "xxx-large" should set the property value]
expected: FAIL

View file

@ -44,9 +44,6 @@
[display: table-row-group]
expected: FAIL
[display: flow]
expected: FAIL
[display: table-column-group]
expected: FAIL