mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Implement grid-template-areas (fixes #16079)
This commit is contained in:
parent
1b9e1cc1f2
commit
3c68c28ff7
5 changed files with 226 additions and 1 deletions
|
@ -623,6 +623,7 @@ mod bindings {
|
|||
.whitelisted_function("Servo_.*")
|
||||
.whitelisted_function("Gecko_.*");
|
||||
let structs_types = [
|
||||
"mozilla::css::GridTemplateAreasValue",
|
||||
"mozilla::css::URLValue",
|
||||
"mozilla::Side",
|
||||
"RawGeckoAnimationPropertySegment",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() +
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue