mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01: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("Servo_.*")
|
||||||
.whitelisted_function("Gecko_.*");
|
.whitelisted_function("Gecko_.*");
|
||||||
let structs_types = [
|
let structs_types = [
|
||||||
|
"mozilla::css::GridTemplateAreasValue",
|
||||||
"mozilla::css::URLValue",
|
"mozilla::css::URLValue",
|
||||||
"mozilla::Side",
|
"mozilla::Side",
|
||||||
"RawGeckoAnimationPropertySegment",
|
"RawGeckoAnimationPropertySegment",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
pub use nsstring::{nsACString, nsAString, nsString};
|
pub use nsstring::{nsACString, nsAString, nsString};
|
||||||
type nsACString_internal = nsACString;
|
type nsACString_internal = nsACString;
|
||||||
type nsAString_internal = nsAString;
|
type nsAString_internal = nsAString;
|
||||||
|
use gecko_bindings::structs::mozilla::css::GridTemplateAreasValue;
|
||||||
use gecko_bindings::structs::mozilla::css::URLValue;
|
use gecko_bindings::structs::mozilla::css::URLValue;
|
||||||
use gecko_bindings::structs::mozilla::Side;
|
use gecko_bindings::structs::mozilla::Side;
|
||||||
use gecko_bindings::structs::RawGeckoAnimationPropertySegment;
|
use gecko_bindings::structs::RawGeckoAnimationPropertySegment;
|
||||||
|
@ -893,6 +894,19 @@ extern "C" {
|
||||||
other:
|
other:
|
||||||
*const nsStyleGridTemplate);
|
*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" {
|
extern "C" {
|
||||||
pub fn Gecko_ClearAndResizeStyleContents(content: *mut nsStyleContent,
|
pub fn Gecko_ClearAndResizeStyleContents(content: *mut nsStyleContent,
|
||||||
how_many: u32);
|
how_many: u32);
|
||||||
|
|
|
@ -277,3 +277,6 @@ impl_threadsafe_refcount!(::gecko_bindings::structs::nsCSSValueSharedList,
|
||||||
impl_threadsafe_refcount!(::gecko_bindings::structs::mozilla::css::URLValue,
|
impl_threadsafe_refcount!(::gecko_bindings::structs::mozilla::css::URLValue,
|
||||||
Gecko_AddRefCSSURLValueArbitraryThread,
|
Gecko_AddRefCSSURLValueArbitraryThread,
|
||||||
Gecko_ReleaseCSSURLValueArbitraryThread);
|
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"
|
<%self:impl_trait style_struct_name="Position"
|
||||||
skip_longhands="${skip_position_longhands} z-index box-sizing order align-content
|
skip_longhands="${skip_position_longhands} z-index box-sizing order align-content
|
||||||
justify-content align-self justify-self align-items
|
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:
|
% for side in SIDES:
|
||||||
<% impl_split_style_coord("%s" % side.ident,
|
<% impl_split_style_coord("%s" % side.ident,
|
||||||
"mOffset",
|
"mOffset",
|
||||||
|
@ -1228,6 +1229,42 @@ fn static_assert() {
|
||||||
}
|
}
|
||||||
|
|
||||||
${impl_simple_copy('grid_auto_flow', 'mGridAutoFlow')}
|
${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>
|
</%self:impl_trait>
|
||||||
|
|
||||||
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +
|
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +
|
||||||
|
|
|
@ -406,3 +406,173 @@ ${helpers.predefined_type("object-position",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%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