border.mako.rs (19066B)
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 to_rust_ident, ALL_SIDES, PHYSICAL_SIDES, maybe_moz_logical_alias %> 7 8 ${helpers.four_sides_shorthand( 9 "border-color", 10 "border-%s-color", 11 "specified::Color::parse", 12 engines="gecko servo", 13 spec="https://drafts.csswg.org/css-backgrounds/#border-color", 14 allow_quirks="Yes", 15 )} 16 17 ${helpers.four_sides_shorthand( 18 "border-style", 19 "border-%s-style", 20 engines="gecko servo", 21 spec="https://drafts.csswg.org/css-backgrounds/#border-style", 22 )} 23 24 <%helpers:shorthand 25 name="border-width" 26 engines="gecko servo" 27 sub_properties="${ 28 ' '.join('border-%s-width' % side 29 for side in PHYSICAL_SIDES)}" 30 spec="https://drafts.csswg.org/css-backgrounds/#border-width"> 31 use crate::values::generics::rect::Rect; 32 use crate::values::specified::{AllowQuirks, BorderSideWidth}; 33 34 pub fn parse_value<'i, 't>( 35 context: &ParserContext, 36 input: &mut Parser<'i, 't>, 37 ) -> Result<Longhands, ParseError<'i>> { 38 let rect = Rect::parse_with(context, input, |_, i| { 39 BorderSideWidth::parse_quirky(context, i, AllowQuirks::Yes) 40 })?; 41 Ok(expanded! { 42 border_top_width: rect.0, 43 border_right_width: rect.1, 44 border_bottom_width: rect.2, 45 border_left_width: rect.3, 46 }) 47 } 48 49 impl<'a> ToCss for LonghandsToSerialize<'a> { 50 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 51 % for side in PHYSICAL_SIDES: 52 let ${side} = &self.border_${side}_width; 53 % endfor 54 Rect::new(top, right, bottom, left).to_css(dest) 55 } 56 } 57 </%helpers:shorthand> 58 59 60 pub fn parse_border<'i, 't>( 61 context: &ParserContext, 62 input: &mut Parser<'i, 't>, 63 ) -> Result<(specified::Color, specified::BorderStyle, specified::BorderSideWidth), ParseError<'i>> { 64 use crate::values::specified::{Color, BorderStyle, BorderSideWidth}; 65 let _unused = context; 66 let mut color = None; 67 let mut style = None; 68 let mut width = None; 69 let mut any = false; 70 loop { 71 if width.is_none() { 72 if let Ok(value) = input.try_parse(|i| BorderSideWidth::parse(context, i)) { 73 width = Some(value); 74 any = true; 75 } 76 } 77 if style.is_none() { 78 if let Ok(value) = input.try_parse(BorderStyle::parse) { 79 style = Some(value); 80 any = true; 81 continue 82 } 83 } 84 if color.is_none() { 85 if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) { 86 color = Some(value); 87 any = true; 88 continue 89 } 90 } 91 break 92 } 93 if !any { 94 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 95 } 96 Ok((color.unwrap_or(Color::CurrentColor), style.unwrap_or(BorderStyle::None), width.unwrap_or(BorderSideWidth::medium()))) 97 } 98 99 % for side, logical in ALL_SIDES: 100 <% 101 spec = "https://drafts.csswg.org/css-backgrounds/#border-%s" % side 102 if logical: 103 spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-%s" % side 104 %> 105 <%helpers:shorthand 106 name="border-${side}" 107 engines="gecko servo" 108 sub_properties="${' '.join( 109 'border-%s-%s' % (side, prop) 110 for prop in ['width', 'style', 'color'] 111 )}" 112 aliases="${maybe_moz_logical_alias(engine, (side, logical), '-moz-border-%s')}" 113 spec="${spec}"> 114 115 pub fn parse_value<'i, 't>( 116 context: &ParserContext, 117 input: &mut Parser<'i, 't>, 118 ) -> Result<Longhands, ParseError<'i>> { 119 let (color, style, width) = super::parse_border(context, input)?; 120 Ok(expanded! { 121 border_${to_rust_ident(side)}_color: color, 122 border_${to_rust_ident(side)}_style: style, 123 border_${to_rust_ident(side)}_width: width 124 }) 125 } 126 127 impl<'a> ToCss for LonghandsToSerialize<'a> { 128 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 129 crate::values::specified::border::serialize_directional_border( 130 dest, 131 self.border_${to_rust_ident(side)}_width, 132 self.border_${to_rust_ident(side)}_style, 133 self.border_${to_rust_ident(side)}_color 134 ) 135 } 136 } 137 138 </%helpers:shorthand> 139 % endfor 140 141 <%helpers:shorthand name="border" 142 engines="gecko servo" 143 sub_properties="${' '.join('border-%s-%s' % (side, prop) 144 for side in PHYSICAL_SIDES for prop in ['width', 'style', 'color'] 145 )} 146 ${' '.join('border-image-%s' % name 147 for name in ['outset', 'repeat', 'slice', 'source', 'width'])}" 148 derive_value_info="False" 149 spec="https://drafts.csswg.org/css-backgrounds/#border"> 150 151 pub fn parse_value<'i, 't>( 152 context: &ParserContext, 153 input: &mut Parser<'i, 't>, 154 ) -> Result<Longhands, ParseError<'i>> { 155 use crate::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice}; 156 use crate::properties::longhands::{border_image_source, border_image_width}; 157 158 let (color, style, width) = super::parse_border(context, input)?; 159 Ok(expanded! { 160 % for side in PHYSICAL_SIDES: 161 border_${side}_color: color.clone(), 162 border_${side}_style: style, 163 border_${side}_width: width.clone(), 164 % endfor 165 166 // The ‘border’ shorthand resets ‘border-image’ to its initial value. 167 // See https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands 168 % for name in "outset repeat slice source width".split(): 169 border_image_${name}: border_image_${name}::get_initial_specified_value(), 170 % endfor 171 }) 172 } 173 174 impl<'a> ToCss for LonghandsToSerialize<'a> { 175 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 176 use crate::properties::longhands; 177 178 // If any of the border-image longhands differ from their initial specified values we should not 179 // invoke serialize_directional_border(), so there is no point in continuing on to compute all_equal. 180 % for name in "outset repeat slice source width".split(): 181 if *self.border_image_${name} != longhands::border_image_${name}::get_initial_specified_value() { 182 return Ok(()); 183 } 184 % endfor 185 186 let all_equal = { 187 % for side in PHYSICAL_SIDES: 188 let border_${side}_width = self.border_${side}_width; 189 let border_${side}_style = self.border_${side}_style; 190 let border_${side}_color = self.border_${side}_color; 191 % endfor 192 193 border_top_width == border_right_width && 194 border_right_width == border_bottom_width && 195 border_bottom_width == border_left_width && 196 197 border_top_style == border_right_style && 198 border_right_style == border_bottom_style && 199 border_bottom_style == border_left_style && 200 201 border_top_color == border_right_color && 202 border_right_color == border_bottom_color && 203 border_bottom_color == border_left_color 204 }; 205 206 // If all longhands are all present, then all sides should be the same, 207 // so we can just one set of color/style/width 208 if !all_equal { 209 return Ok(()) 210 } 211 crate::values::specified::border::serialize_directional_border( 212 dest, 213 self.border_${side}_width, 214 self.border_${side}_style, 215 self.border_${side}_color 216 ) 217 } 218 } 219 220 // Just use the same as border-left. The border shorthand can't accept 221 // any value that the sub-shorthand couldn't. 222 <% 223 border_left = "<crate::properties::shorthands::border_left::Longhands as SpecifiedValueInfo>" 224 %> 225 impl SpecifiedValueInfo for Longhands { 226 const SUPPORTED_TYPES: u8 = ${border_left}::SUPPORTED_TYPES; 227 fn collect_completion_keywords(f: KeywordsCollectFn) { 228 ${border_left}::collect_completion_keywords(f); 229 } 230 } 231 </%helpers:shorthand> 232 233 <%helpers:shorthand 234 name="border-radius" 235 engines="gecko servo" 236 sub_properties="${' '.join( 237 'border-%s-radius' % (corner) 238 for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left'] 239 )}" 240 extra_prefixes="webkit" 241 spec="https://drafts.csswg.org/css-backgrounds/#border-radius" 242 > 243 use crate::values::generics::rect::Rect; 244 use crate::values::generics::border::BorderCornerRadius; 245 use crate::values::specified::border::BorderRadius; 246 use crate::parser::Parse; 247 248 pub fn parse_value<'i, 't>( 249 context: &ParserContext, 250 input: &mut Parser<'i, 't>, 251 ) -> Result<Longhands, ParseError<'i>> { 252 let radii = BorderRadius::parse(context, input)?; 253 Ok(expanded! { 254 border_top_left_radius: radii.top_left, 255 border_top_right_radius: radii.top_right, 256 border_bottom_right_radius: radii.bottom_right, 257 border_bottom_left_radius: radii.bottom_left, 258 }) 259 } 260 261 impl<'a> ToCss for LonghandsToSerialize<'a> { 262 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 263 let LonghandsToSerialize { 264 border_top_left_radius: &BorderCornerRadius(ref tl), 265 border_top_right_radius: &BorderCornerRadius(ref tr), 266 border_bottom_right_radius: &BorderCornerRadius(ref br), 267 border_bottom_left_radius: &BorderCornerRadius(ref bl), 268 } = *self; 269 270 271 let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width()); 272 let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height()); 273 274 BorderRadius::serialize_rects(widths, heights, dest) 275 } 276 } 277 </%helpers:shorthand> 278 279 <%helpers:shorthand 280 name="border-image" 281 engines="gecko servo" 282 sub_properties="border-image-outset 283 border-image-repeat border-image-slice border-image-source border-image-width" 284 extra_prefixes="moz:layout.css.prefixes.border-image webkit" 285 spec="https://drafts.csswg.org/css-backgrounds-3/#border-image" 286 > 287 use crate::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice}; 288 use crate::properties::longhands::{border_image_source, border_image_width}; 289 290 pub fn parse_value<'i, 't>( 291 context: &ParserContext, 292 input: &mut Parser<'i, 't>, 293 ) -> Result<Longhands, ParseError<'i>> { 294 % for name in "outset repeat slice source width".split(): 295 let mut ${name} = border_image_${name}::get_initial_specified_value(); 296 % endfor 297 let mut any = false; 298 let mut parsed_slice = false; 299 let mut parsed_source = false; 300 let mut parsed_repeat = false; 301 loop { 302 if !parsed_slice { 303 if let Ok(value) = input.try_parse(|input| border_image_slice::parse(context, input)) { 304 parsed_slice = true; 305 any = true; 306 slice = value; 307 // Parse border image width and outset, if applicable. 308 let maybe_width_outset: Result<_, ParseError> = input.try_parse(|input| { 309 input.expect_delim('/')?; 310 311 // Parse border image width, if applicable. 312 let w = input.try_parse(|input| border_image_width::parse(context, input)).ok(); 313 314 // Parse border image outset if applicable. 315 let o = input.try_parse(|input| { 316 input.expect_delim('/')?; 317 border_image_outset::parse(context, input) 318 }).ok(); 319 if w.is_none() && o.is_none() { 320 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 321 } 322 Ok((w, o)) 323 }); 324 if let Ok((w, o)) = maybe_width_outset { 325 if let Some(w) = w { 326 width = w; 327 } 328 if let Some(o) = o { 329 outset = o; 330 } 331 } 332 continue; 333 } 334 } 335 % for name in "source repeat".split(): 336 if !parsed_${name} { 337 if let Ok(value) = input.try_parse(|input| border_image_${name}::parse(context, input)) { 338 ${name} = value; 339 parsed_${name} = true; 340 any = true; 341 continue 342 } 343 } 344 % endfor 345 break 346 } 347 if !any { 348 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 349 } 350 Ok(expanded! { 351 % for name in "outset repeat slice source width".split(): 352 border_image_${name}: ${name}, 353 % endfor 354 }) 355 } 356 357 impl<'a> ToCss for LonghandsToSerialize<'a> { 358 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 359 let mut has_any = false; 360 % for name in "source slice outset width repeat".split(): 361 let has_${name} = *self.border_image_${name} != border_image_${name}::get_initial_specified_value(); 362 has_any = has_any || has_${name}; 363 % endfor 364 if has_source || !has_any { 365 self.border_image_source.to_css(dest)?; 366 if !has_any { 367 return Ok(()); 368 } 369 } 370 let needs_slice = has_slice || has_width || has_outset; 371 if needs_slice { 372 if has_source { 373 dest.write_char(' ')?; 374 } 375 self.border_image_slice.to_css(dest)?; 376 if has_width || has_outset { 377 dest.write_str(" /")?; 378 if has_width { 379 dest.write_char(' ')?; 380 self.border_image_width.to_css(dest)?; 381 } 382 if has_outset { 383 dest.write_str(" / ")?; 384 self.border_image_outset.to_css(dest)?; 385 } 386 } 387 } 388 if has_repeat { 389 if has_source || needs_slice { 390 dest.write_char(' ')?; 391 } 392 self.border_image_repeat.to_css(dest)?; 393 } 394 Ok(()) 395 } 396 } 397 </%helpers:shorthand> 398 399 % for axis in ["block", "inline"]: 400 % for prop in ["width", "style", "color"]: 401 <% 402 spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s-%s" % (axis, prop) 403 %> 404 <%helpers:shorthand 405 engines="gecko servo" 406 name="border-${axis}-${prop}" 407 sub_properties="${' '.join( 408 'border-%s-%s-%s' % (axis, side, prop) 409 for side in ['start', 'end'] 410 )}" 411 spec="${spec}"> 412 413 use crate::properties::longhands::border_${axis}_start_${prop}; 414 pub fn parse_value<'i, 't>( 415 context: &ParserContext, 416 input: &mut Parser<'i, 't>, 417 ) -> Result<Longhands, ParseError<'i>> { 418 let start_value = border_${axis}_start_${prop}::parse(context, input)?; 419 let end_value = 420 input.try_parse(|input| border_${axis}_start_${prop}::parse(context, input)) 421 .unwrap_or_else(|_| start_value.clone()); 422 423 Ok(expanded! { 424 border_${axis}_start_${prop}: start_value, 425 border_${axis}_end_${prop}: end_value, 426 }) 427 } 428 429 impl<'a> ToCss for LonghandsToSerialize<'a> { 430 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 431 self.border_${axis}_start_${prop}.to_css(dest)?; 432 433 if self.border_${axis}_end_${prop} != self.border_${axis}_start_${prop} { 434 dest.write_char(' ')?; 435 self.border_${axis}_end_${prop}.to_css(dest)?; 436 } 437 438 Ok(()) 439 } 440 } 441 </%helpers:shorthand> 442 % endfor 443 % endfor 444 445 % for axis in ["block", "inline"]: 446 <% 447 spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s" % (axis) 448 %> 449 <%helpers:shorthand 450 name="border-${axis}" 451 engines="gecko servo" 452 sub_properties="${' '.join( 453 'border-%s-%s-width' % (axis, side) 454 for side in ['start', 'end'] 455 )} ${' '.join( 456 'border-%s-%s-style' % (axis, side) 457 for side in ['start', 'end'] 458 )} ${' '.join( 459 'border-%s-%s-color' % (axis, side) 460 for side in ['start', 'end'] 461 )}" 462 spec="${spec}"> 463 464 use crate::properties::shorthands::border_${axis}_start; 465 pub fn parse_value<'i, 't>( 466 context: &ParserContext, 467 input: &mut Parser<'i, 't>, 468 ) -> Result<Longhands, ParseError<'i>> { 469 let start_value = border_${axis}_start::parse_value(context, input)?; 470 Ok(expanded! { 471 border_${axis}_start_width: start_value.border_${axis}_start_width.clone(), 472 border_${axis}_end_width: start_value.border_${axis}_start_width, 473 border_${axis}_start_style: start_value.border_${axis}_start_style.clone(), 474 border_${axis}_end_style: start_value.border_${axis}_start_style, 475 border_${axis}_start_color: start_value.border_${axis}_start_color.clone(), 476 border_${axis}_end_color: start_value.border_${axis}_start_color, 477 }) 478 } 479 480 impl<'a> ToCss for LonghandsToSerialize<'a> { 481 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 482 crate::values::specified::border::serialize_directional_border( 483 dest, 484 self.border_${axis}_start_width, 485 self.border_${axis}_start_style, 486 self.border_${axis}_start_color 487 ) 488 } 489 } 490 </%helpers:shorthand> 491 % endfor