mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
style: Refactor grid types to preserve repeat() at computed value time and use cbindgen.
I'm _really_ sorry for the size of the patch. I tried to do this in two steps but it was a lot of work and pretty ugly. This patch makes us use cbindgen for grid-template-{rows,columns}, in order to: * Make us preserve repeat() at computed-value time. This is per spec since interpolation needs to know about repeat(). Except for subgrid, which did the repeat expansion at parse-time and was a bit more annoying (plus it doesn't really animate yet so we don't need it to comply with the spec). * Tweaks the WPT tests for interpolation to adopt the resolution at: https://github.com/w3c/csswg-drafts/issues/3503. Trade-off here, as this patch stands, is that this change makes us use less long-living memory, since we expand repeat() during layout, but at the cost of a bit of CPU time during layout (conditional on the property applying though, which wasn't the case before). It should be very easy to store a cached version of the template, should this be too hot (I expect it isn't), or to change the representation in other ways to optimize grid layout code if it's worth it. Another trade-off: I've used SmallPointerArray to handle line-name merging, pointing to the individual arrays in the style data, rather than actually heap-allocating the merged lists. This would also be pretty easy to change should we measure and see that it's not worth it. This patch also opens the gate to potentially improving memory usage in some other ways, by reference-counting line-name lists for example, though I don't have data that suggests it is worth it. In general, this patch makes much easier to tweak the internal representation of the grid style data structures. Overall, I think it's a win, the amount of magic going on in that mako code was a bit huge; it took a bit to wrap my head around it. This patch comments out the style struct size assertions. They will be uncommented in a follow-up patch which contains some improvements for this type, which are worth getting reviewed separately. Also, this patch doesn't remove as much code as I would've hoped for because of I tried not to change most of the dom/grid code for inspector, but I think a fair bit of the nsGridContainerFrame.cpp code that collects information for it can be simplified / de-copy-pasted to some extent. But that was a pre-existing problem and this patch is already quite massive. Differential Revision: https://phabricator.services.mozilla.com/D36598
This commit is contained in:
parent
0e8b1853a7
commit
3e39998068
8 changed files with 142 additions and 505 deletions
|
@ -976,8 +976,7 @@ fn static_assert() {
|
|||
skip_longhands="${skip_position_longhands} order
|
||||
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
|
||||
|
@ -1025,216 +1024,6 @@ fn static_assert() {
|
|||
|
||||
${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>
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -407,13 +408,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 +427,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,7 +492,7 @@
|
|||
>
|
||||
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::generics::grid::GridTemplateComponent;
|
||||
use crate::values::specified::{GenericGridTemplateComponent, TrackSize};
|
||||
use crate::values::specified::position::{AutoFlow, GridAutoFlow, GridTemplateAreas};
|
||||
|
||||
|
@ -597,7 +591,7 @@
|
|||
|
||||
// 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(());
|
||||
}
|
||||
}
|
||||
|
@ -621,7 +615,7 @@
|
|||
|
||||
// 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(());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -467,6 +467,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);
|
||||
|
|
|
@ -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.
|
||||
|
@ -366,6 +366,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 +381,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 +409,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 +418,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 +458,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 +474,54 @@ 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 +529,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 +536,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 +562,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 +575,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 +603,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 +631,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 +642,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 +659,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 +686,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 +700,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,7 +710,7 @@ pub enum GridTemplateComponent<L, I> {
|
|||
#[compute(field_bound)]
|
||||
#[resolve(field_bound)]
|
||||
#[shmem(field_bound)]
|
||||
TrackList<L, I>,
|
||||
GenericTrackList<L, I>,
|
||||
),
|
||||
/// A `subgrid <line-name-list>?`
|
||||
/// TODO: Support animations for this after subgrid is addressed in [grid-2] spec.
|
||||
|
@ -758,6 +718,8 @@ pub enum GridTemplateComponent<L, I> {
|
|||
Subgrid(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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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::{TrackList, TrackListValue};
|
||||
use crate::values::specified::{Integer, LengthPercentage};
|
||||
use crate::values::{CSSFloat, CustomIdent};
|
||||
use cssparser::{ParseError as CssParseError, Parser, Token};
|
||||
|
@ -101,7 +100,7 @@ impl Parse for TrackSize<LengthPercentage> {
|
|||
/// <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 +111,7 @@ pub fn parse_line_names<'i, 't>(
|
|||
values.push(ident);
|
||||
}
|
||||
|
||||
Ok(values.into_boxed_slice())
|
||||
Ok(values.into())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -155,9 +154,8 @@ 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 {
|
||||
|
@ -183,7 +181,7 @@ impl TrackRepeat<LengthPercentage, Integer> {
|
|||
names.push(
|
||||
input
|
||||
.try(parse_line_names)
|
||||
.unwrap_or(vec![].into_boxed_slice()),
|
||||
.unwrap_or_default()
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
@ -201,9 +199,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 +219,35 @@ 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()),
|
||||
.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,132 +255,38 @@ 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 {
|
||||
|
@ -409,7 +301,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>,
|
||||
|
|
|
@ -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 {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue