position.mako.rs (36439B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 <%namespace name="helpers" file="/helpers.mako.rs" /> 6 <% from data import DEFAULT_RULES_AND_POSITION_TRY %> 7 8 <%helpers:shorthand name="flex-flow" 9 engines="gecko servo", 10 sub_properties="flex-direction flex-wrap" 11 extra_prefixes="webkit" 12 spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property"> 13 use crate::properties::longhands::{flex_direction, flex_wrap}; 14 15 pub fn parse_value<'i, 't>( 16 context: &ParserContext, 17 input: &mut Parser<'i, 't>, 18 ) -> Result<Longhands, ParseError<'i>> { 19 let mut direction = None; 20 let mut wrap = None; 21 loop { 22 if direction.is_none() { 23 if let Ok(value) = input.try_parse(|input| flex_direction::parse(context, input)) { 24 direction = Some(value); 25 continue 26 } 27 } 28 if wrap.is_none() { 29 if let Ok(value) = input.try_parse(|input| flex_wrap::parse(context, input)) { 30 wrap = Some(value); 31 continue 32 } 33 } 34 break 35 } 36 37 if direction.is_none() && wrap.is_none() { 38 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 39 } 40 Ok(expanded! { 41 flex_direction: unwrap_or_initial!(flex_direction, direction), 42 flex_wrap: unwrap_or_initial!(flex_wrap, wrap), 43 }) 44 } 45 46 impl<'a> ToCss for LonghandsToSerialize<'a> { 47 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 48 if *self.flex_direction == flex_direction::get_initial_specified_value() && 49 *self.flex_wrap != flex_wrap::get_initial_specified_value() { 50 return self.flex_wrap.to_css(dest) 51 } 52 self.flex_direction.to_css(dest)?; 53 if *self.flex_wrap != flex_wrap::get_initial_specified_value() { 54 dest.write_char(' ')?; 55 self.flex_wrap.to_css(dest)?; 56 } 57 Ok(()) 58 } 59 } 60 </%helpers:shorthand> 61 62 <%helpers:shorthand name="flex" 63 engines="gecko servo", 64 sub_properties="flex-grow flex-shrink flex-basis" 65 extra_prefixes="webkit" 66 derive_serialize="True" 67 spec="https://drafts.csswg.org/css-flexbox/#flex-property"> 68 use crate::parser::Parse; 69 use crate::values::specified::NonNegativeNumber; 70 use crate::properties::longhands::flex_basis::SpecifiedValue as FlexBasis; 71 72 fn parse_flexibility<'i, 't>( 73 context: &ParserContext, 74 input: &mut Parser<'i, 't>, 75 ) -> Result<(NonNegativeNumber, Option<NonNegativeNumber>),ParseError<'i>> { 76 let grow = NonNegativeNumber::parse(context, input)?; 77 let shrink = input.try_parse(|i| NonNegativeNumber::parse(context, i)).ok(); 78 Ok((grow, shrink)) 79 } 80 81 pub fn parse_value<'i, 't>( 82 context: &ParserContext, 83 input: &mut Parser<'i, 't>, 84 ) -> Result<Longhands, ParseError<'i>> { 85 let mut grow = None; 86 let mut shrink = None; 87 let mut basis = None; 88 89 if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { 90 return Ok(expanded! { 91 flex_grow: NonNegativeNumber::new(0.0), 92 flex_shrink: NonNegativeNumber::new(0.0), 93 flex_basis: FlexBasis::auto(), 94 }) 95 } 96 loop { 97 if grow.is_none() { 98 if let Ok((flex_grow, flex_shrink)) = input.try_parse(|i| parse_flexibility(context, i)) { 99 grow = Some(flex_grow); 100 shrink = flex_shrink; 101 continue 102 } 103 } 104 if basis.is_none() { 105 if let Ok(value) = input.try_parse(|input| FlexBasis::parse(context, input)) { 106 basis = Some(value); 107 continue 108 } 109 } 110 break 111 } 112 113 if grow.is_none() && basis.is_none() { 114 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 115 } 116 Ok(expanded! { 117 flex_grow: grow.unwrap_or(NonNegativeNumber::new(1.0)), 118 flex_shrink: shrink.unwrap_or(NonNegativeNumber::new(1.0)), 119 // Per spec, this should be SpecifiedValue::zero(), but all 120 // browsers currently agree on using `0%`. This is a spec 121 // change which hasn't been adopted by browsers: 122 // https://github.com/w3c/csswg-drafts/commit/2c446befdf0f686217905bdd7c92409f6bd3921b 123 flex_basis: basis.unwrap_or(FlexBasis::zero_percent()), 124 }) 125 } 126 </%helpers:shorthand> 127 128 <%helpers:shorthand 129 name="gap" 130 engines="gecko servo" 131 aliases="grid-gap" 132 sub_properties="row-gap column-gap" 133 spec="https://drafts.csswg.org/css-align-3/#gap-shorthand" 134 > 135 use crate::properties::longhands::{row_gap, column_gap}; 136 137 pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) 138 -> Result<Longhands, ParseError<'i>> { 139 let r_gap = row_gap::parse(context, input)?; 140 let c_gap = input.try_parse(|input| column_gap::parse(context, input)).unwrap_or(r_gap.clone()); 141 142 Ok(expanded! { 143 row_gap: r_gap, 144 column_gap: c_gap, 145 }) 146 } 147 148 impl<'a> ToCss for LonghandsToSerialize<'a> { 149 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 150 if self.row_gap == self.column_gap { 151 self.row_gap.to_css(dest) 152 } else { 153 self.row_gap.to_css(dest)?; 154 dest.write_char(' ')?; 155 self.column_gap.to_css(dest) 156 } 157 } 158 } 159 160 </%helpers:shorthand> 161 162 % for kind in ["row", "column"]: 163 <%helpers:shorthand 164 name="grid-${kind}" 165 sub_properties="grid-${kind}-start grid-${kind}-end" 166 engines="gecko servo", 167 servo_pref="layout.grid.enabled", 168 spec="https://drafts.csswg.org/css-grid/#propdef-grid-${kind}" 169 > 170 use crate::values::specified::GridLine; 171 use crate::parser::Parse; 172 use crate::Zero; 173 174 // NOTE: Since both the shorthands have the same code, we should (re-)use code from one to implement 175 // the other. This might not be a big deal for now, but we should consider looking into this in the future 176 // to limit the amount of code generated. 177 pub fn parse_value<'i, 't>( 178 context: &ParserContext, 179 input: &mut Parser<'i, 't>, 180 ) -> Result<Longhands, ParseError<'i>> { 181 let start = input.try_parse(|i| GridLine::parse(context, i))?; 182 let end = if input.try_parse(|i| i.expect_delim('/')).is_ok() { 183 GridLine::parse(context, input)? 184 } else { 185 let mut line = GridLine::auto(); 186 if start.line_num.is_zero() && !start.is_span { 187 line.ident = start.ident.clone(); // ident from start value should be taken 188 } 189 190 line 191 }; 192 193 Ok(expanded! { 194 grid_${kind}_start: start, 195 grid_${kind}_end: end, 196 }) 197 } 198 199 impl<'a> ToCss for LonghandsToSerialize<'a> { 200 // Return the shortest possible serialization of the `grid-${kind}-[start/end]` values. 201 // This function exploits the opportunities to omit the end value per this spec text: 202 // 203 // https://drafts.csswg.org/css-grid/#propdef-grid-column 204 // "When the second value is omitted, if the first value is a <custom-ident>, 205 // the grid-row-end/grid-column-end longhand is also set to that <custom-ident>; 206 // otherwise, it is set to auto." 207 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 208 self.grid_${kind}_start.to_css(dest)?; 209 if self.grid_${kind}_start.can_omit(self.grid_${kind}_end) { 210 return Ok(()); // the end value is redundant 211 } 212 dest.write_str(" / ")?; 213 self.grid_${kind}_end.to_css(dest) 214 } 215 } 216 </%helpers:shorthand> 217 % endfor 218 219 <%helpers:shorthand 220 name="grid-area" 221 engines="gecko servo" 222 servo_pref="layout.grid.enabled", 223 sub_properties="grid-row-start grid-row-end grid-column-start grid-column-end" 224 spec="https://drafts.csswg.org/css-grid/#propdef-grid-area" 225 > 226 use crate::values::specified::GridLine; 227 use crate::parser::Parse; 228 use crate::Zero; 229 230 // The code is the same as `grid-{row,column}` except that this can have four values at most. 231 pub fn parse_value<'i, 't>( 232 context: &ParserContext, 233 input: &mut Parser<'i, 't>, 234 ) -> Result<Longhands, ParseError<'i>> { 235 fn line_with_ident_from(other: &GridLine) -> GridLine { 236 let mut this = GridLine::auto(); 237 if other.line_num.is_zero() && !other.is_span { 238 this.ident = other.ident.clone(); 239 } 240 241 this 242 } 243 244 let row_start = input.try_parse(|i| GridLine::parse(context, i))?; 245 let (column_start, row_end, column_end) = if input.try_parse(|i| i.expect_delim('/')).is_ok() { 246 let column_start = GridLine::parse(context, input)?; 247 let (row_end, column_end) = if input.try_parse(|i| i.expect_delim('/')).is_ok() { 248 let row_end = GridLine::parse(context, input)?; 249 let column_end = if input.try_parse(|i| i.expect_delim('/')).is_ok() { 250 GridLine::parse(context, input)? 251 } else { // grid-column-end has not been given 252 line_with_ident_from(&column_start) 253 }; 254 255 (row_end, column_end) 256 } else { // grid-row-start and grid-column-start has been given 257 let row_end = line_with_ident_from(&row_start); 258 let column_end = line_with_ident_from(&column_start); 259 (row_end, column_end) 260 }; 261 262 (column_start, row_end, column_end) 263 } else { // only grid-row-start is given 264 let line = line_with_ident_from(&row_start); 265 (line.clone(), line.clone(), line) 266 }; 267 268 Ok(expanded! { 269 grid_row_start: row_start, 270 grid_row_end: row_end, 271 grid_column_start: column_start, 272 grid_column_end: column_end, 273 }) 274 } 275 276 impl<'a> ToCss for LonghandsToSerialize<'a> { 277 // Return the shortest possible serialization of the `grid-[column/row]-[start/end]` values. 278 // This function exploits the opportunities to omit trailing values per this spec text: 279 // 280 // https://drafts.csswg.org/css-grid/#propdef-grid-area 281 // "If four <grid-line> values are specified, grid-row-start is set to the first value, 282 // grid-column-start is set to the second value, grid-row-end is set to the third value, 283 // and grid-column-end is set to the fourth value. 284 // 285 // When grid-column-end is omitted, if grid-column-start is a <custom-ident>, 286 // grid-column-end is set to that <custom-ident>; otherwise, it is set to auto. 287 // 288 // When grid-row-end is omitted, if grid-row-start is a <custom-ident>, grid-row-end is 289 // set to that <custom-ident>; otherwise, it is set to auto. 290 // 291 // When grid-column-start is omitted, if grid-row-start is a <custom-ident>, all four 292 // longhands are set to that value. Otherwise, it is set to auto." 293 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 294 self.grid_row_start.to_css(dest)?; 295 let mut trailing_values = 3; 296 if self.grid_column_start.can_omit(self.grid_column_end) { 297 trailing_values -= 1; 298 if self.grid_row_start.can_omit(self.grid_row_end) { 299 trailing_values -= 1; 300 if self.grid_row_start.can_omit(self.grid_column_start) { 301 trailing_values -= 1; 302 } 303 } 304 } 305 let values = [&self.grid_column_start, &self.grid_row_end, &self.grid_column_end]; 306 for value in values.iter().take(trailing_values) { 307 dest.write_str(" / ")?; 308 value.to_css(dest)?; 309 } 310 Ok(()) 311 } 312 } 313 </%helpers:shorthand> 314 315 <%helpers:shorthand 316 name="grid-template" 317 engines="gecko servo" 318 servo_pref="layout.grid.enabled", 319 sub_properties="grid-template-rows grid-template-columns grid-template-areas" 320 spec="https://drafts.csswg.org/css-grid/#propdef-grid-template" 321 > 322 use crate::parser::Parse; 323 use servo_arc::Arc; 324 use crate::values::generics::grid::{TrackSize, TrackList}; 325 use crate::values::generics::grid::{TrackListValue, concat_serialize_idents}; 326 use crate::values::specified::{GridTemplateComponent, GenericGridTemplateComponent}; 327 use crate::values::specified::grid::parse_line_names; 328 use crate::values::specified::position::{GridTemplateAreas, TemplateAreasParser, TemplateAreasArc}; 329 330 /// Parsing for `<grid-template>` shorthand (also used by `grid` shorthand). 331 pub fn parse_grid_template<'i, 't>( 332 context: &ParserContext, 333 input: &mut Parser<'i, 't>, 334 ) -> Result<(GridTemplateComponent, GridTemplateComponent, GridTemplateAreas), ParseError<'i>> { 335 // Other shorthand sub properties also parse the `none` keyword and this shorthand 336 // should know after this keyword there is nothing to parse. Otherwise it gets 337 // confused and rejects the sub properties that contains `none`. 338 <% keywords = { 339 "none": "GenericGridTemplateComponent::None", 340 } 341 %> 342 % for keyword, rust_type in keywords.items(): 343 if let Ok(x) = input.try_parse(|i| { 344 if i.try_parse(|i| i.expect_ident_matching("${keyword}")).is_ok() { 345 if !i.is_exhausted() { 346 return Err(()); 347 } 348 return Ok((${rust_type}, ${rust_type}, GridTemplateAreas::None)); 349 } 350 Err(()) 351 }) { 352 return Ok(x); 353 } 354 % endfor 355 356 let first_line_names = input.try_parse(parse_line_names).unwrap_or_default(); 357 let mut areas_parser = TemplateAreasParser::default(); 358 if areas_parser.try_parse_string(input).is_ok() { 359 let mut values = vec![]; 360 let mut line_names = vec![]; 361 line_names.push(first_line_names); 362 loop { 363 let size = input.try_parse(|i| TrackSize::parse(context, i)).unwrap_or_default(); 364 values.push(TrackListValue::TrackSize(size)); 365 let mut names = input.try_parse(parse_line_names).unwrap_or_default(); 366 let more_names = input.try_parse(parse_line_names); 367 368 match areas_parser.try_parse_string(input) { 369 Ok(()) => { 370 if let Ok(v) = more_names { 371 // We got `[names] [more_names] "string"` - merge the two name lists. 372 let mut names_vec = names.into_vec(); 373 names_vec.extend(v.into_iter()); 374 names = names_vec.into(); 375 } 376 line_names.push(names); 377 }, 378 Err(e) => { 379 if more_names.is_ok() { 380 // We've parsed `"string" [names] [more_names]` but then failed to parse another `"string"`. 381 // The grammar doesn't allow two trailing `<line-names>` so this is an invalid value. 382 return Err(e); 383 } 384 // only the named area determines whether we should bail out 385 line_names.push(names); 386 break 387 }, 388 }; 389 } 390 391 if line_names.len() == values.len() { 392 // should be one longer than track sizes 393 line_names.push(Default::default()); 394 } 395 396 let template_areas = areas_parser.finish() 397 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?; 398 let template_rows = TrackList { 399 values: values.into(), 400 line_names: line_names.into(), 401 auto_repeat_index: std::usize::MAX, 402 }; 403 404 let template_cols = if input.try_parse(|i| i.expect_delim('/')).is_ok() { 405 let value = GridTemplateComponent::parse_without_none(context, input)?; 406 if let GenericGridTemplateComponent::TrackList(ref list) = value { 407 if !list.is_explicit() { 408 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 409 } 410 } 411 412 value 413 } else { 414 GridTemplateComponent::default() 415 }; 416 417 Ok(( 418 GenericGridTemplateComponent::TrackList(Box::new(template_rows)), 419 template_cols, 420 GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas))) 421 )) 422 } else { 423 let mut template_rows = GridTemplateComponent::parse(context, input)?; 424 if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows { 425 // Fist line names are parsed already and it shouldn't be parsed again. 426 // If line names are not empty, that means given property value is not acceptable 427 if list.line_names[0].is_empty() { 428 list.line_names[0] = first_line_names; // won't panic 429 } else { 430 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 431 } 432 } 433 434 input.expect_delim('/')?; 435 Ok((template_rows, GridTemplateComponent::parse(context, input)?, GridTemplateAreas::None)) 436 } 437 } 438 439 #[inline] 440 pub fn parse_value<'i, 't>( 441 context: &ParserContext, 442 input: &mut Parser<'i, 't>, 443 ) -> Result<Longhands, ParseError<'i>> { 444 let (rows, columns, areas) = parse_grid_template(context, input)?; 445 Ok(expanded! { 446 grid_template_rows: rows, 447 grid_template_columns: columns, 448 grid_template_areas: areas, 449 }) 450 } 451 452 /// Serialization for `<grid-template>` shorthand (also used by `grid` shorthand). 453 pub fn serialize_grid_template<W>( 454 template_rows: &GridTemplateComponent, 455 template_columns: &GridTemplateComponent, 456 template_areas: &GridTemplateAreas, 457 dest: &mut CssWriter<W>, 458 ) -> fmt::Result 459 where 460 W: Write { 461 match *template_areas { 462 GridTemplateAreas::None => { 463 if template_rows.is_initial() && template_columns.is_initial() { 464 return GridTemplateComponent::default().to_css(dest); 465 } 466 template_rows.to_css(dest)?; 467 dest.write_str(" / ")?; 468 template_columns.to_css(dest) 469 }, 470 GridTemplateAreas::Areas(ref areas) => { 471 // The length of template-area and template-rows values should be equal. 472 if areas.0.strings.len() != template_rows.track_list_len() { 473 return Ok(()); 474 } 475 476 let track_list = match *template_rows { 477 GenericGridTemplateComponent::TrackList(ref list) => { 478 // We should fail if there is a `repeat` function. 479 // `grid` and `grid-template` shorthands doesn't accept 480 // that. Only longhand accepts. 481 if !list.is_explicit() { 482 return Ok(()); 483 } 484 list 485 }, 486 // Others template components shouldn't exist with normal shorthand values. 487 // But if we need to serialize a group of longhand sub-properties for 488 // the shorthand, we should be able to return empty string instead of crashing. 489 _ => return Ok(()), 490 }; 491 492 // We need to check some values that longhand accepts but shorthands don't. 493 match *template_columns { 494 // We should fail if there is a `repeat` function. `grid` and 495 // `grid-template` shorthands doesn't accept that. Only longhand accepts that. 496 GenericGridTemplateComponent::TrackList(ref list) => { 497 if !list.is_explicit() { 498 return Ok(()); 499 } 500 }, 501 // Also the shorthands don't accept subgrids unlike longhand. 502 // We should fail without an error here. 503 GenericGridTemplateComponent::Subgrid(_) => { 504 return Ok(()); 505 }, 506 _ => {}, 507 } 508 509 let mut names_iter = track_list.line_names.iter(); 510 for (((i, string), names), value) in areas.0.strings.iter().enumerate() 511 .zip(&mut names_iter) 512 .zip(track_list.values.iter()) { 513 if i > 0 { 514 dest.write_char(' ')?; 515 } 516 517 if !names.is_empty() { 518 concat_serialize_idents("[", "] ", names, " ", dest)?; 519 } 520 521 string.to_css(dest)?; 522 523 // If the track size is the initial value then it's redundant here. 524 if !value.is_initial() { 525 dest.write_char(' ')?; 526 value.to_css(dest)?; 527 } 528 } 529 530 if let Some(names) = names_iter.next() { 531 concat_serialize_idents(" [", "]", names, " ", dest)?; 532 } 533 534 if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns { 535 dest.write_str(" / ")?; 536 list.to_css(dest)?; 537 } 538 539 Ok(()) 540 }, 541 } 542 } 543 544 impl<'a> ToCss for LonghandsToSerialize<'a> { 545 #[inline] 546 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 547 serialize_grid_template( 548 self.grid_template_rows, 549 self.grid_template_columns, 550 self.grid_template_areas, 551 dest 552 ) 553 } 554 } 555 </%helpers:shorthand> 556 557 <%helpers:shorthand 558 name="grid" 559 engines="gecko servo" 560 servo_pref="layout.grid.enabled", 561 sub_properties="grid-template-rows grid-template-columns grid-template-areas 562 grid-auto-rows grid-auto-columns grid-auto-flow" 563 spec="https://drafts.csswg.org/css-grid/#propdef-grid" 564 > 565 use crate::parser::Parse; 566 use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow}; 567 use crate::values::generics::grid::GridTemplateComponent; 568 use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks}; 569 use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas}; 570 571 pub fn parse_value<'i, 't>( 572 context: &ParserContext, 573 input: &mut Parser<'i, 't>, 574 ) -> Result<Longhands, ParseError<'i>> { 575 let mut temp_rows = GridTemplateComponent::default(); 576 let mut temp_cols = GridTemplateComponent::default(); 577 let mut temp_areas = GridTemplateAreas::None; 578 let mut auto_rows = ImplicitGridTracks::default(); 579 let mut auto_cols = ImplicitGridTracks::default(); 580 let mut flow = grid_auto_flow::get_initial_value(); 581 582 fn parse_auto_flow<'i, 't>( 583 input: &mut Parser<'i, 't>, 584 is_row: bool, 585 ) -> Result<GridAutoFlow, ParseError<'i>> { 586 let mut track = None; 587 let mut dense = GridAutoFlow::empty(); 588 589 for _ in 0..2 { 590 if input.try_parse(|i| i.expect_ident_matching("auto-flow")).is_ok() { 591 track = if is_row { 592 Some(GridAutoFlow::ROW) 593 } else { 594 Some(GridAutoFlow::COLUMN) 595 }; 596 } else if input.try_parse(|i| i.expect_ident_matching("dense")).is_ok() { 597 dense = GridAutoFlow::DENSE 598 } else { 599 break 600 } 601 } 602 603 if track.is_some() { 604 Ok(track.unwrap() | dense) 605 } else { 606 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 607 } 608 } 609 610 if let Ok((rows, cols, areas)) = input.try_parse(|i| super::grid_template::parse_grid_template(context, i)) { 611 temp_rows = rows; 612 temp_cols = cols; 613 temp_areas = areas; 614 } else if let Ok(rows) = input.try_parse(|i| GridTemplateComponent::parse(context, i)) { 615 temp_rows = rows; 616 input.expect_delim('/')?; 617 flow = parse_auto_flow(input, false)?; 618 auto_cols = input.try_parse(|i| grid_auto_columns::parse(context, i)).unwrap_or_default(); 619 } else { 620 flow = parse_auto_flow(input, true)?; 621 auto_rows = input.try_parse(|i| grid_auto_rows::parse(context, i)).unwrap_or_default(); 622 input.expect_delim('/')?; 623 temp_cols = GridTemplateComponent::parse(context, input)?; 624 } 625 626 Ok(expanded! { 627 grid_template_rows: temp_rows, 628 grid_template_columns: temp_cols, 629 grid_template_areas: temp_areas, 630 grid_auto_rows: auto_rows, 631 grid_auto_columns: auto_cols, 632 grid_auto_flow: flow, 633 }) 634 } 635 636 impl<'a> LonghandsToSerialize<'a> { 637 /// Returns true if other sub properties except template-{rows,columns} are initial. 638 fn is_grid_template(&self) -> bool { 639 self.grid_auto_rows.is_initial() && 640 self.grid_auto_columns.is_initial() && 641 *self.grid_auto_flow == grid_auto_flow::get_initial_value() 642 } 643 } 644 645 impl<'a> ToCss for LonghandsToSerialize<'a> { 646 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 647 if self.is_grid_template() { 648 return super::grid_template::serialize_grid_template( 649 self.grid_template_rows, 650 self.grid_template_columns, 651 self.grid_template_areas, 652 dest 653 ); 654 } 655 656 if *self.grid_template_areas != GridTemplateAreas::None { 657 // No other syntax can set the template areas, so fail to 658 // serialize. 659 return Ok(()); 660 } 661 662 if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) { 663 // It should fail to serialize if other branch of the if condition's values are set. 664 if !self.grid_auto_rows.is_initial() || 665 !self.grid_template_columns.is_initial() { 666 return Ok(()); 667 } 668 669 // It should fail to serialize if template-rows value is not Explicit. 670 if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows { 671 if !list.is_explicit() { 672 return Ok(()); 673 } 674 } 675 676 self.grid_template_rows.to_css(dest)?; 677 dest.write_str(" / auto-flow")?; 678 if self.grid_auto_flow.contains(GridAutoFlow::DENSE) { 679 dest.write_str(" dense")?; 680 } 681 682 if !self.grid_auto_columns.is_initial() { 683 dest.write_char(' ')?; 684 self.grid_auto_columns.to_css(dest)?; 685 } 686 687 return Ok(()); 688 } 689 690 // It should fail to serialize if other branch of the if condition's values are set. 691 if !self.grid_auto_columns.is_initial() || 692 !self.grid_template_rows.is_initial() { 693 return Ok(()); 694 } 695 696 // It should fail to serialize if template-column value is not Explicit. 697 if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns { 698 if !list.is_explicit() { 699 return Ok(()); 700 } 701 } 702 703 dest.write_str("auto-flow")?; 704 if self.grid_auto_flow.contains(GridAutoFlow::DENSE) { 705 dest.write_str(" dense")?; 706 } 707 708 if !self.grid_auto_rows.is_initial() { 709 dest.write_char(' ')?; 710 self.grid_auto_rows.to_css(dest)?; 711 } 712 713 dest.write_str(" / ")?; 714 self.grid_template_columns.to_css(dest)?; 715 Ok(()) 716 } 717 } 718 </%helpers:shorthand> 719 720 <%helpers:shorthand 721 name="place-content" 722 engines="gecko servo" 723 sub_properties="align-content justify-content" 724 spec="https://drafts.csswg.org/css-align/#propdef-place-content" 725 > 726 use crate::values::specified::align::ContentDistribution; 727 728 pub fn parse_value<'i, 't>( 729 context: &ParserContext, 730 input: &mut Parser<'i, 't>, 731 ) -> Result<Longhands, ParseError<'i>> { 732 let align_content = ContentDistribution::parse_block(context, input)?; 733 let justify_content = input.try_parse(|input| ContentDistribution::parse_inline(context, input)); 734 735 let justify_content = match justify_content { 736 Ok(v) => v, 737 Err(..) => { 738 // https://drafts.csswg.org/css-align-3/#place-content: 739 // 740 // The second value is assigned to justify-content; if 741 // omitted, it is copied from the first value, unless that 742 // value is a <baseline-position> in which case it is 743 // defaulted to start. 744 // 745 if !align_content.is_baseline_position() { 746 align_content 747 } else { 748 ContentDistribution::start() 749 } 750 } 751 }; 752 753 Ok(expanded! { 754 align_content: align_content, 755 justify_content: justify_content, 756 }) 757 } 758 759 impl<'a> ToCss for LonghandsToSerialize<'a> { 760 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 761 self.align_content.to_css(dest)?; 762 if self.align_content != self.justify_content { 763 dest.write_char(' ')?; 764 self.justify_content.to_css(dest)?; 765 } 766 Ok(()) 767 } 768 } 769 </%helpers:shorthand> 770 771 <%helpers:shorthand 772 name="place-self" 773 engines="gecko servo" 774 sub_properties="align-self justify-self" 775 spec="https://drafts.csswg.org/css-align/#place-self-property" 776 rule_types_allowed="Style PositionTry" 777 > 778 use crate::values::specified::align::SelfAlignment; 779 780 pub fn parse_value<'i, 't>( 781 context: &ParserContext, 782 input: &mut Parser<'i, 't>, 783 ) -> Result<Longhands, ParseError<'i>> { 784 let align = SelfAlignment::parse_block(context, input)?; 785 let justify = input.try_parse(|input| SelfAlignment::parse_inline(context, input)); 786 787 let justify = match justify { 788 Ok(v) => v, 789 Err(..) => { 790 debug_assert!(align.is_valid_on_both_axes()); 791 align 792 } 793 }; 794 795 Ok(expanded! { 796 align_self: align, 797 justify_self: justify, 798 }) 799 } 800 impl<'a> ToCss for LonghandsToSerialize<'a> { 801 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 802 self.align_self.to_css(dest)?; 803 if self.align_self != self.justify_self { 804 dest.write_char(' ')?; 805 self.justify_self.to_css(dest)?; 806 } 807 Ok(()) 808 } 809 } 810 </%helpers:shorthand> 811 812 <%helpers:shorthand 813 name="place-items" 814 engines="gecko servo" 815 sub_properties="align-items justify-items" 816 spec="https://drafts.csswg.org/css-align/#place-items-property" 817 > 818 use crate::values::specified::align::{ItemPlacement, JustifyItems}; 819 820 pub fn parse_value<'i, 't>( 821 context: &ParserContext, 822 input: &mut Parser<'i, 't>, 823 ) -> Result<Longhands, ParseError<'i>> { 824 let align = ItemPlacement::parse_block(context, input)?; 825 let justify = 826 input.try_parse(|input| ItemPlacement::parse_inline(context, input)) 827 .unwrap_or_else(|_| align.clone()); 828 829 Ok(expanded! { 830 align_items: align, 831 justify_items: JustifyItems(justify), 832 }) 833 } 834 835 impl<'a> ToCss for LonghandsToSerialize<'a> { 836 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 837 self.align_items.to_css(dest)?; 838 if *self.align_items != self.justify_items.0 { 839 dest.write_char(' ')?; 840 self.justify_items.to_css(dest)?; 841 } 842 Ok(()) 843 } 844 } 845 </%helpers:shorthand> 846 847 <%helpers:shorthand 848 name="position-try" 849 engines="gecko" 850 gecko_pref="layout.css.anchor-positioning.enabled", 851 sub_properties="position-try-order position-try-fallbacks" 852 spec="https://drafts.csswg.org/css-anchor-position-1/#position-try-prop" 853 > 854 use crate::values::specified::position::{PositionTryOrder, PositionTryFallbacks}; 855 use crate::parser::Parse; 856 857 pub fn parse_value<'i, 't>( 858 context: &ParserContext, 859 input: &mut Parser<'i, 't>, 860 ) -> Result<Longhands, ParseError<'i>> { 861 let order = if static_prefs::pref!("layout.css.anchor-positioning.position-try-order.enabled") { 862 input.try_parse(PositionTryOrder::parse).ok() 863 } else { 864 None 865 }; 866 let fallbacks = PositionTryFallbacks::parse(context, input)?; 867 Ok(expanded! { 868 position_try_order: order.unwrap_or(PositionTryOrder::normal()), 869 position_try_fallbacks: fallbacks, 870 }) 871 } 872 873 impl<'a> ToCss for LonghandsToSerialize<'a> { 874 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 875 if let Some(o) = self.position_try_order { 876 if *o != PositionTryOrder::Normal { 877 o.to_css(dest)?; 878 dest.write_char(' ')?; 879 } 880 } 881 self.position_try_fallbacks.to_css(dest) 882 } 883 } 884 </%helpers:shorthand> 885 886 // See https://github.com/w3c/csswg-drafts/issues/3525 for the quirks stuff. 887 ${helpers.four_sides_shorthand( 888 "inset", 889 "%s", 890 "specified::Inset::parse", 891 engines="gecko servo", 892 spec="https://drafts.csswg.org/css-logical/#propdef-inset", 893 rule_types_allowed=DEFAULT_RULES_AND_POSITION_TRY, 894 allow_quirks="No", 895 )} 896 897 ${helpers.two_properties_shorthand( 898 "inset-block", 899 "inset-block-start", 900 "inset-block-end", 901 "specified::Inset::parse", 902 engines="gecko servo", 903 spec="https://drafts.csswg.org/css-logical/#propdef-inset-block", 904 rule_types_allowed=DEFAULT_RULES_AND_POSITION_TRY, 905 )} 906 907 ${helpers.two_properties_shorthand( 908 "inset-inline", 909 "inset-inline-start", 910 "inset-inline-end", 911 "specified::Inset::parse", 912 engines="gecko servo", 913 spec="https://drafts.csswg.org/css-logical/#propdef-inset-inline", 914 rule_types_allowed=DEFAULT_RULES_AND_POSITION_TRY, 915 )} 916 917 ${helpers.two_properties_shorthand( 918 "contain-intrinsic-size", 919 "contain-intrinsic-width", 920 "contain-intrinsic-height", 921 engines="gecko", 922 spec="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override", 923 )}