Implement grid-template-areas (fixes #16079)

This commit is contained in:
Anthony Ramine 2017-05-04 17:43:51 +02:00
parent 1b9e1cc1f2
commit 3c68c28ff7
5 changed files with 226 additions and 1 deletions

View file

@ -623,6 +623,7 @@ mod bindings {
.whitelisted_function("Servo_.*")
.whitelisted_function("Gecko_.*");
let structs_types = [
"mozilla::css::GridTemplateAreasValue",
"mozilla::css::URLValue",
"mozilla::Side",
"RawGeckoAnimationPropertySegment",

View file

@ -3,6 +3,7 @@
pub use nsstring::{nsACString, nsAString, nsString};
type nsACString_internal = nsACString;
type nsAString_internal = nsAString;
use gecko_bindings::structs::mozilla::css::GridTemplateAreasValue;
use gecko_bindings::structs::mozilla::css::URLValue;
use gecko_bindings::structs::mozilla::Side;
use gecko_bindings::structs::RawGeckoAnimationPropertySegment;
@ -893,6 +894,19 @@ extern "C" {
other:
*const nsStyleGridTemplate);
}
extern "C" {
pub fn Gecko_NewGridTemplateAreasValue(areas: u32, templates: u32,
columns: u32)
-> *mut GridTemplateAreasValue;
}
extern "C" {
pub fn Gecko_AddRefGridTemplateAreasValueArbitraryThread(aPtr:
*mut GridTemplateAreasValue);
}
extern "C" {
pub fn Gecko_ReleaseGridTemplateAreasValueArbitraryThread(aPtr:
*mut GridTemplateAreasValue);
}
extern "C" {
pub fn Gecko_ClearAndResizeStyleContents(content: *mut nsStyleContent,
how_many: u32);

View file

@ -277,3 +277,6 @@ impl_threadsafe_refcount!(::gecko_bindings::structs::nsCSSValueSharedList,
impl_threadsafe_refcount!(::gecko_bindings::structs::mozilla::css::URLValue,
Gecko_AddRefCSSURLValueArbitraryThread,
Gecko_ReleaseCSSURLValueArbitraryThread);
impl_threadsafe_refcount!(::gecko_bindings::structs::mozilla::css::GridTemplateAreasValue,
Gecko_AddRefGridTemplateAreasValueArbitraryThread,
Gecko_ReleaseGridTemplateAreasValueArbitraryThread);

View file

@ -1052,7 +1052,8 @@ fn static_assert() {
<%self:impl_trait style_struct_name="Position"
skip_longhands="${skip_position_longhands} z-index box-sizing order align-content
justify-content align-self justify-self align-items
justify-items grid-auto-rows grid-auto-columns grid-auto-flow">
justify-items grid-auto-rows grid-auto-columns grid-auto-flow
grid-template-areas">
% for side in SIDES:
<% impl_split_style_coord("%s" % side.ident,
"mOffset",
@ -1228,6 +1229,42 @@ fn static_assert() {
}
${impl_simple_copy('grid_auto_flow', 'mGridAutoFlow')}
pub fn set_grid_template_areas(&mut self, v: longhands::grid_template_areas::computed_value::T) {
use gecko_bindings::bindings::Gecko_NewGridTemplateAreasValue;
use gecko_bindings::sugar::refptr::UniqueRefPtr;
let v = match v {
Either::First(areas) => areas,
Either::Second(_) => {
unsafe { self.gecko.mGridTemplateAreas.clear() }
return;
},
};
let mut refptr = unsafe {
UniqueRefPtr::from_addrefed(
Gecko_NewGridTemplateAreasValue(v.areas.len() as u32, v.strings.len() as u32, v.width))
};
for (servo, gecko) in v.areas.into_iter().zip(refptr.mNamedAreas.iter_mut()) {
gecko.mName.assign_utf8(&*servo.name);
gecko.mColumnStart = servo.columns.start;
gecko.mColumnEnd = servo.columns.end;
gecko.mRowStart = servo.rows.start;
gecko.mRowEnd = servo.rows.end;
}
for (servo, gecko) in v.strings.into_iter().zip(refptr.mTemplates.iter_mut()) {
gecko.assign_utf8(&*servo);
}
unsafe { self.gecko.mGridTemplateAreas.set_move(refptr.get()) }
}
pub fn copy_grid_template_areas_from(&mut self, other: &Self) {
unsafe { self.gecko.mGridTemplateAreas.set(&other.gecko.mGridTemplateAreas) }
}
</%self:impl_trait>
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +

View file

@ -406,3 +406,173 @@ ${helpers.predefined_type("object-position",
}
}
</%helpers:longhand>
<%helpers:longhand name="grid-template-areas"
spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-areas"
products="gecko"
animation_value_type="none"
disable_when_testing="True"
boxed="True">
use cssparser::serialize_string;
use std::collections::HashMap;
use std::fmt;
use std::ops::Range;
use str::HTML_SPACE_CHARACTERS;
use style_traits::ToCss;
use style_traits::values::Css;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
pub mod computed_value {
pub use super::SpecifiedValue as T;
}
pub type SpecifiedValue = Either<TemplateAreas, None_>;
#[inline]
pub fn get_initial_value() -> computed_value::T {
Either::Second(None_)
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
SpecifiedValue::parse(context, input)
}
#[derive(Clone, PartialEq)]
pub struct TemplateAreas {
pub areas: Box<[NamedArea]>,
pub strings: Box<[Box<str>]>,
pub width: u32,
}
#[derive(Clone, PartialEq)]
pub struct NamedArea {
pub name: Box<str>,
pub rows: Range<u32>,
pub columns: Range<u32>,
}
no_viewport_percentage!(TemplateAreas);
impl ComputedValueAsSpecified for TemplateAreas {}
impl Parse for TemplateAreas {
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
let mut strings = vec![];
while let Ok(string) = input.try(Parser::expect_string) {
strings.push(string.into_owned().into_boxed_str());
}
if strings.is_empty() {
return Err(());
}
let mut areas: Vec<NamedArea> = vec![];
let mut width = 0;
{
let mut row = 0u32;
let mut area_indices = HashMap::<(&str), usize>::new();
for string in &strings {
let mut current_area_index: Option<usize> = None;
row += 1;
let mut column = 0u32;
for token in Tokenizer(string) {
column += 1;
let token = if let Some(token) = token? {
token
} else {
if let Some(index) = current_area_index.take() {
if areas[index].columns.end != column {
return Err(());
}
}
continue;
};
if let Some(index) = current_area_index {
if &*areas[index].name == token {
if areas[index].rows.start == row {
areas[index].columns.end += 1;
}
continue;
}
if areas[index].columns.end != column {
return Err(());
}
}
if let Some(index) = area_indices.get(token).cloned() {
if areas[index].columns.start != column || areas[index].rows.end != row {
return Err(());
}
areas[index].rows.end += 1;
current_area_index = Some(index);
continue;
}
let index = areas.len();
areas.push(NamedArea {
name: token.to_owned().into_boxed_str(),
columns: column..(column + 1),
rows: row..(row + 1),
});
assert!(area_indices.insert(token, index).is_none());
current_area_index = Some(index);
}
if let Some(index) = current_area_index {
if areas[index].columns.end != column + 1 {
assert!(areas[index].rows.start != row);
return Err(());
}
}
if row == 1 {
width = column;
} else if width != column {
return Err(());
}
}
}
Ok(TemplateAreas {
areas: areas.into_boxed_slice(),
strings: strings.into_boxed_slice(),
width: width,
})
}
}
impl ToCss for TemplateAreas {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
for (i, string) in self.strings.iter().enumerate() {
if i != 0 {
dest.write_str(" ")?;
}
serialize_string(string, dest)?;
}
Ok(())
}
}
struct Tokenizer<'a>(&'a str);
impl<'a> Iterator for Tokenizer<'a> {
type Item = Result<Option<(&'a str)>, ()>;
fn next(&mut self) -> Option<Self::Item> {
let rest = self.0.trim_left_matches(HTML_SPACE_CHARACTERS);
if rest.is_empty() {
return None;
}
if rest.starts_with('.') {
self.0 = &rest[rest.find(|c| c != '.').unwrap_or(rest.len())..];
return Some(Ok(None));
}
if !rest.starts_with(is_name_code_point) {
return Some(Err(()));
}
let token_len = rest.find(|c| !is_name_code_point(c)).unwrap_or(rest.len());
let token = &rest[..token_len];
self.0 = &rest[token_len..];
Some(Ok(Some(token)))
}
}
fn is_name_code_point(c: char) -> bool {
c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' ||
c >= '\u{80}' || c == '_' ||
c >= '0' && c <= '9' || c == '-'
}
</%helpers:longhand>