properties.mako.rs (112952B)
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 // This file is a Mako template: http://www.makotemplates.org/ 6 7 <%namespace name="helpers" file="/helpers.mako.rs" /> 8 9 use servo_arc::{Arc, UniqueArc}; 10 use std::{ops, ptr}; 11 use std::{fmt, mem}; 12 13 #[cfg(feature = "servo")] use euclid::SideOffsets2D; 14 #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{self, NonCustomCSSPropertyId}; 15 #[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin; 16 #[cfg(feature = "servo")] use crate::computed_values; 17 use crate::logical_geometry::WritingMode; 18 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; 19 use crate::computed_value_flags::*; 20 use cssparser::Parser; 21 use crate::media_queries::Device; 22 use crate::parser::ParserContext; 23 use crate::selector_parser::PseudoElement; 24 use crate::stylist::Stylist; 25 use style_traits::{CssStringWriter, CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, TypedValue, ToTyped}; 26 use crate::derives::*; 27 use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin}; 28 use crate::logical_geometry::{LogicalAxis, LogicalCorner, LogicalSide}; 29 use crate::use_counters::UseCounters; 30 use crate::rule_tree::StrongRuleNode; 31 use crate::values::{ 32 computed, 33 resolved, 34 specified::{font::SystemFont, length::LineHeightBase, color::ColorSchemeFlags}, 35 }; 36 use std::cell::Cell; 37 use super::{ 38 PropertyDeclarationId, PropertyId, NonCustomPropertyId, 39 NonCustomPropertyIdSet, PropertyFlags, SourcePropertyDeclaration, 40 LonghandIdSet, VariableDeclaration, CustomDeclaration, 41 WideKeywordDeclaration, NonCustomPropertyIterator, 42 }; 43 use debug_unreachable::debug_unreachable; 44 45 <%! 46 from collections import defaultdict 47 from data import Method, PropertyRestrictions, Keyword, to_rust_ident, \ 48 to_camel_case, RULE_VALUES, SYSTEM_FONT_LONGHANDS, PRIORITARY_PROPERTIES 49 import os.path 50 %> 51 52 /// Conversion with fewer impls than From/Into 53 pub trait MaybeBoxed<Out> { 54 /// Convert 55 fn maybe_boxed(self) -> Out; 56 } 57 58 impl<T> MaybeBoxed<T> for T { 59 #[inline] 60 fn maybe_boxed(self) -> T { self } 61 } 62 63 impl<T> MaybeBoxed<Box<T>> for T { 64 #[inline] 65 fn maybe_boxed(self) -> Box<T> { Box::new(self) } 66 } 67 68 macro_rules! expanded { 69 ( $( $name: ident: $value: expr ),+ ) => { 70 expanded!( $( $name: $value, )+ ) 71 }; 72 ( $( $name: ident: $value: expr, )+ ) => { 73 Longhands { 74 $( 75 $name: MaybeBoxed::maybe_boxed($value), 76 )+ 77 } 78 } 79 } 80 81 /// A module with all the code for longhand properties. 82 #[allow(missing_docs)] 83 pub mod longhands { 84 % for style_struct in data.style_structs: 85 <% data.current_style_struct = style_struct %> 86 <%include file="/longhands/${style_struct.name_lower}.mako.rs" /> 87 % endfor 88 } 89 90 91 % if engine == "gecko": 92 #[allow(unsafe_code, missing_docs)] 93 pub mod gecko { 94 <%include file="/gecko.mako.rs" /> 95 } 96 % endif 97 98 99 macro_rules! unwrap_or_initial { 100 ($prop: ident) => (unwrap_or_initial!($prop, $prop)); 101 ($prop: ident, $expr: expr) => 102 ($expr.unwrap_or_else(|| $prop::get_initial_specified_value())); 103 } 104 105 /// A module with code for all the shorthand css properties, and a few 106 /// serialization helpers. 107 #[allow(missing_docs)] 108 pub mod shorthands { 109 use cssparser::Parser; 110 use crate::parser::{Parse, ParserContext}; 111 use style_traits::{ParseError, StyleParseErrorKind}; 112 use crate::values::specified; 113 114 % for style_struct in data.style_structs: 115 <%include file="/shorthands/${style_struct.name_lower}.mako.rs" /> 116 % endfor 117 118 // We didn't define the 'all' shorthand using the regular helpers:shorthand 119 // mechanism, since it causes some very large types to be generated. 120 // 121 // Also, make sure logical properties appear before its physical 122 // counter-parts, in order to prevent bugs like: 123 // 124 // https://bugzilla.mozilla.org/show_bug.cgi?id=1410028 125 // 126 // FIXME(emilio): Adopt the resolution from: 127 // 128 // https://github.com/w3c/csswg-drafts/issues/1898 129 // 130 // when there is one, whatever that is. 131 <% 132 logical_longhands = [] 133 other_longhands = [] 134 135 for p in data.longhands: 136 if p.name in ['direction', 'unicode-bidi']: 137 continue; 138 if not p.enabled_in_content() and not p.experimental(engine): 139 continue; 140 if "Style" not in p.rule_types_allowed_names(): 141 continue; 142 if p.logical: 143 logical_longhands.append(p.name) 144 else: 145 other_longhands.append(p.name) 146 147 data.declare_shorthand( 148 "all", 149 logical_longhands + other_longhands, 150 engines="gecko servo", 151 spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand" 152 ) 153 ALL_SHORTHAND_LEN = len(logical_longhands) + len(other_longhands); 154 %> 155 } 156 157 <% 158 from itertools import groupby 159 160 # After this code, `data.longhands` is sorted in the following order: 161 # - first all keyword variants and all variants known to be Copy, 162 # - second all the other variants, such as all variants with the same field 163 # have consecutive discriminants. 164 # The variable `variants` contain the same entries as `data.longhands` in 165 # the same order, but must exist separately to the data source, because 166 # we then need to add three additional variants `WideKeywordDeclaration`, 167 # `VariableDeclaration` and `CustomDeclaration`. 168 169 variants = [] 170 for property in data.longhands: 171 variants.append({ 172 "name": property.camel_case, 173 "type": property.specified_type(), 174 "doc": "`" + property.name + "`", 175 "copy": property.specified_is_copy(), 176 }) 177 178 groups = {} 179 keyfunc = lambda x: x["type"] 180 sortkeys = {} 181 for ty, group in groupby(sorted(variants, key=keyfunc), keyfunc): 182 group = list(group) 183 groups[ty] = group 184 for v in group: 185 if len(group) == 1: 186 sortkeys[v["name"]] = (not v["copy"], 1, v["name"], "") 187 else: 188 sortkeys[v["name"]] = (not v["copy"], len(group), ty, v["name"]) 189 variants.sort(key=lambda x: sortkeys[x["name"]]) 190 191 # It is extremely important to sort the `data.longhands` array here so 192 # that it is in the same order as `variants`, for `LonghandId` and 193 # `PropertyDeclarationId` to coincide. 194 data.longhands.sort(key=lambda x: sortkeys[x.camel_case]) 195 %> 196 197 // WARNING: It is *really* important for the variants of `LonghandId` 198 // and `PropertyDeclaration` to be defined in the exact same order, 199 // with the exception of `CSSWideKeyword`, `WithVariables` and `Custom`, 200 // which don't exist in `LonghandId`. 201 202 <% 203 extra_variants = [ 204 { 205 "name": "CSSWideKeyword", 206 "type": "WideKeywordDeclaration", 207 "doc": "A CSS-wide keyword.", 208 "copy": False, 209 }, 210 { 211 "name": "WithVariables", 212 "type": "VariableDeclaration", 213 "doc": "An unparsed declaration.", 214 "copy": False, 215 }, 216 { 217 "name": "Custom", 218 "type": "CustomDeclaration", 219 "doc": "A custom property declaration.", 220 "copy": False, 221 }, 222 ] 223 for v in extra_variants: 224 variants.append(v) 225 groups[v["type"]] = [v] 226 %> 227 228 /// Servo's representation for a property declaration. 229 #[derive(ToShmem)] 230 #[repr(u16)] 231 pub enum PropertyDeclaration { 232 % for variant in variants: 233 /// ${variant["doc"]} 234 ${variant["name"]}(${variant["type"]}), 235 % endfor 236 } 237 238 // There's one of these for each parsed declaration so it better be small. 239 size_of_test!(PropertyDeclaration, 32); 240 241 #[repr(C)] 242 struct PropertyDeclarationVariantRepr<T> { 243 tag: u16, 244 value: T 245 } 246 247 impl Clone for PropertyDeclaration { 248 #[inline] 249 fn clone(&self) -> Self { 250 use self::PropertyDeclaration::*; 251 252 <% 253 [copy, others] = [list(g) for _, g in groupby(variants, key=lambda x: not x["copy"])] 254 %> 255 256 let self_tag = unsafe { 257 (*(self as *const _ as *const PropertyDeclarationVariantRepr<()>)).tag 258 }; 259 if self_tag <= LonghandId::${copy[-1]["name"]} as u16 { 260 #[derive(Clone, Copy)] 261 #[repr(u16)] 262 enum CopyVariants { 263 % for v in copy: 264 _${v["name"]}(${v["type"]}), 265 % endfor 266 } 267 268 unsafe { 269 let mut out = mem::MaybeUninit::uninit(); 270 ptr::write( 271 out.as_mut_ptr() as *mut CopyVariants, 272 *(self as *const _ as *const CopyVariants), 273 ); 274 return out.assume_init(); 275 } 276 } 277 278 // This function ensures that all properties not handled above 279 // do not have a specified value implements Copy. If you hit 280 // compile error here, you may want to add the type name into 281 // Longhand.specified_is_copy in data.py. 282 fn _static_assert_others_are_not_copy() { 283 struct Helper<T>(T); 284 trait AssertCopy { fn assert() {} } 285 trait AssertNotCopy { fn assert() {} } 286 impl<T: Copy> AssertCopy for Helper<T> {} 287 % for ty in sorted(set(x["type"] for x in others)): 288 impl AssertNotCopy for Helper<${ty}> {} 289 Helper::<${ty}>::assert(); 290 % endfor 291 } 292 293 match *self { 294 ${" |\n".join("{}(..)".format(v["name"]) for v in copy)} => { 295 unsafe { debug_unreachable!() } 296 } 297 % for ty, vs in groupby(others, key=lambda x: x["type"]): 298 <% 299 vs = list(vs) 300 %> 301 % if len(vs) == 1: 302 ${vs[0]["name"]}(ref value) => { 303 ${vs[0]["name"]}(value.clone()) 304 } 305 % else: 306 ${" |\n".join("{}(ref value)".format(v["name"]) for v in vs)} => { 307 unsafe { 308 let mut out = mem::MaybeUninit::uninit(); 309 ptr::write( 310 out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${ty}>, 311 PropertyDeclarationVariantRepr { 312 tag: *(self as *const _ as *const u16), 313 value: value.clone(), 314 }, 315 ); 316 out.assume_init() 317 } 318 } 319 % endif 320 % endfor 321 } 322 } 323 } 324 325 impl PartialEq for PropertyDeclaration { 326 #[inline] 327 fn eq(&self, other: &Self) -> bool { 328 use self::PropertyDeclaration::*; 329 330 unsafe { 331 let this_repr = 332 &*(self as *const _ as *const PropertyDeclarationVariantRepr<()>); 333 let other_repr = 334 &*(other as *const _ as *const PropertyDeclarationVariantRepr<()>); 335 if this_repr.tag != other_repr.tag { 336 return false; 337 } 338 match *self { 339 % for ty, vs in groupby(variants, key=lambda x: x["type"]): 340 ${" |\n".join("{}(ref this)".format(v["name"]) for v in vs)} => { 341 let other_repr = 342 &*(other as *const _ as *const PropertyDeclarationVariantRepr<${ty}>); 343 *this == other_repr.value 344 } 345 % endfor 346 } 347 } 348 } 349 } 350 351 impl MallocSizeOf for PropertyDeclaration { 352 #[inline] 353 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { 354 use self::PropertyDeclaration::*; 355 356 match *self { 357 % for ty, vs in groupby(variants, key=lambda x: x["type"]): 358 ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => { 359 value.size_of(ops) 360 } 361 % endfor 362 } 363 } 364 } 365 366 367 impl PropertyDeclaration { 368 /// Returns the given value for this declaration as a particular type. 369 /// It's the caller's responsibility to guarantee that the longhand id has the right specified 370 /// value representation. 371 pub(crate) unsafe fn unchecked_value_as<T>(&self) -> &T { 372 &(*(self as *const _ as *const PropertyDeclarationVariantRepr<T>)).value 373 } 374 375 /// Dumps the property declaration before crashing. 376 #[cold] 377 #[cfg(debug_assertions)] 378 pub(crate) fn debug_crash(&self, reason: &str) { 379 panic!("{}: {:?}", reason, self); 380 } 381 #[cfg(not(debug_assertions))] 382 #[inline(always)] 383 pub(crate) fn debug_crash(&self, _reason: &str) {} 384 385 /// Returns whether this is a variant of the Longhand(Value) type, rather 386 /// than one of the special variants in extra_variants. 387 fn is_longhand_value(&self) -> bool { 388 match *self { 389 % for v in extra_variants: 390 PropertyDeclaration::${v["name"]}(..) => false, 391 % endfor 392 _ => true, 393 } 394 } 395 396 /// Like the method on ToCss, but without the type parameter to avoid 397 /// accidentally monomorphizing this large function multiple times for 398 /// different writers. 399 pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result { 400 use self::PropertyDeclaration::*; 401 402 let mut dest = CssWriter::new(dest); 403 match *self { 404 % for ty, vs in groupby(variants, key=lambda x: x["type"]): 405 ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => { 406 value.to_css(&mut dest) 407 } 408 % endfor 409 } 410 } 411 412 /// Like the method on ToTyped. 413 pub fn to_typed(&self) -> Option<TypedValue> { 414 use self::PropertyDeclaration::*; 415 416 match *self { 417 % for ty, vs in groupby(variants, key=lambda x: x["type"]): 418 ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => { 419 value.to_typed() 420 } 421 % endfor 422 } 423 } 424 425 /// Returns the color value of a given property, for high-contrast-mode tweaks. 426 pub(super) fn color_value(&self) -> Option<&crate::values::specified::Color> { 427 ${static_longhand_id_set("COLOR_PROPERTIES", lambda p: p.predefined_type == "Color")} 428 <% 429 # sanity check 430 assert data.longhands_by_name["background-color"].predefined_type == "Color" 431 432 color_specified_type = data.longhands_by_name["background-color"].specified_type() 433 %> 434 let id = self.id().as_longhand()?; 435 if !COLOR_PROPERTIES.contains(id) || !self.is_longhand_value() { 436 return None; 437 } 438 let repr = self as *const _ as *const PropertyDeclarationVariantRepr<${color_specified_type}>; 439 Some(unsafe { &(*repr).value }) 440 } 441 } 442 443 /// A module with all the code related to animated properties. 444 /// 445 /// This needs to be "included" by mako at least after all longhand modules, 446 /// given they populate the global data. 447 pub mod animated_properties { 448 <%include file="/helpers/animated_properties.mako.rs" /> 449 } 450 451 /// A module to group various interesting property counts. 452 pub mod property_counts { 453 /// The number of (non-alias) longhand properties. 454 pub const LONGHANDS: usize = ${len(data.longhands)}; 455 /// The number of (non-alias) shorthand properties. 456 pub const SHORTHANDS: usize = ${len(data.shorthands)}; 457 /// The number of aliases. 458 pub const ALIASES: usize = ${len(data.all_aliases())}; 459 /// The number of counted unknown properties. 460 pub const COUNTED_UNKNOWN: usize = ${len(data.counted_unknown_properties)}; 461 /// The number of (non-alias) longhands and shorthands. 462 pub const LONGHANDS_AND_SHORTHANDS: usize = LONGHANDS + SHORTHANDS; 463 /// The number of non-custom properties. 464 pub const NON_CUSTOM: usize = LONGHANDS_AND_SHORTHANDS + ALIASES; 465 /// The number of prioritary properties that we have. 466 <% longhand_property_names = set(list(map(lambda p: p.name, data.longhands))) %> 467 <% enabled_prioritary_properties = PRIORITARY_PROPERTIES.intersection(longhand_property_names) %> 468 pub const PRIORITARY: usize = ${len(enabled_prioritary_properties)}; 469 /// The max number of longhands that a shorthand other than "all" expands to. 470 pub const MAX_SHORTHAND_EXPANDED: usize = 471 ${max(len(s.sub_properties) for s in data.shorthands_except_all())}; 472 /// The max amount of longhands that the `all` shorthand will ever contain. 473 pub const ALL_SHORTHAND_EXPANDED: usize = ${ALL_SHORTHAND_LEN}; 474 /// The number of animatable properties. 475 pub const ANIMATABLE: usize = ${sum(1 for prop in data.longhands if prop.animatable)}; 476 } 477 478 % if engine == "gecko": 479 #[allow(dead_code)] 480 unsafe fn static_assert_noncustomcsspropertyid() { 481 % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()): 482 std::mem::transmute::<[u8; ${i}], [u8; ${property.noncustomcsspropertyid()} as usize]>([0; ${i}]); // ${property.name} 483 % endfor 484 } 485 % endif 486 487 impl NonCustomPropertyId { 488 /// Get the property name. 489 #[inline] 490 pub fn name(self) -> &'static str { 491 static MAP: [&'static str; property_counts::NON_CUSTOM] = [ 492 % for property in data.longhands + data.shorthands + data.all_aliases(): 493 "${property.name}", 494 % endfor 495 ]; 496 MAP[self.0 as usize] 497 } 498 499 /// Returns whether this property is animatable. 500 #[inline] 501 pub fn is_animatable(self) -> bool { 502 ${static_non_custom_property_id_set("ANIMATABLE", lambda p: p.animatable)} 503 ANIMATABLE.contains(self) 504 } 505 506 /// Whether this property is enabled for all content right now. 507 #[inline] 508 pub(super) fn enabled_for_all_content(self) -> bool { 509 ${static_non_custom_property_id_set( 510 "EXPERIMENTAL", 511 lambda p: p.experimental(engine) 512 )} 513 514 ${static_non_custom_property_id_set( 515 "ALWAYS_ENABLED", 516 lambda p: (not p.experimental(engine)) and p.enabled_in_content() 517 )} 518 519 let passes_pref_check = || { 520 % if engine == "gecko": 521 unsafe { structs::nsCSSProps_gPropertyEnabled[self.0 as usize] } 522 % else: 523 static PREF_NAME: [Option<&str>; ${ 524 len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) 525 }] = [ 526 % for property in data.longhands + data.shorthands + data.all_aliases(): 527 <% 528 pref = getattr(property, "servo_pref") 529 %> 530 % if pref: 531 Some("${pref}"), 532 % else: 533 None, 534 % endif 535 % endfor 536 ]; 537 let pref = match PREF_NAME[self.0 as usize] { 538 None => return true, 539 Some(pref) => pref, 540 }; 541 542 style_config::get_bool(pref) 543 % endif 544 }; 545 546 if ALWAYS_ENABLED.contains(self) { 547 return true 548 } 549 550 if EXPERIMENTAL.contains(self) && passes_pref_check() { 551 return true 552 } 553 554 false 555 } 556 557 /// Returns whether a given rule allows a given property. 558 #[inline] 559 pub fn allowed_in_rule(self, rule_types: CssRuleTypes) -> bool { 560 debug_assert!( 561 rule_types.contains(CssRuleType::Keyframe) || 562 rule_types.contains(CssRuleType::Page) || 563 rule_types.contains(CssRuleType::Style) || 564 rule_types.contains(CssRuleType::Scope) || 565 rule_types.contains(CssRuleType::PositionTry), 566 "Given rule type does not allow declarations." 567 ); 568 569 static MAP: [u32; property_counts::NON_CUSTOM] = [ 570 % for property in data.longhands + data.shorthands + data.all_aliases(): 571 % for name in RULE_VALUES: 572 % if property.rule_types_allowed & RULE_VALUES[name] != 0: 573 CssRuleType::${name}.bit() | 574 % endif 575 % endfor 576 0, 577 % endfor 578 ]; 579 MAP[self.0 as usize] & rule_types.bits() != 0 580 } 581 582 pub(super) fn allowed_in(self, context: &ParserContext) -> bool { 583 if !self.allowed_in_rule(context.rule_types()) { 584 return false; 585 } 586 587 self.allowed_in_ignoring_rule_type(context) 588 } 589 590 591 pub(super) fn allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool { 592 // The semantics of these are kinda hard to reason about, what follows 593 // is a description of the different combinations that can happen with 594 // these three sets. 595 // 596 // Experimental properties are generally controlled by prefs, but an 597 // experimental property explicitly enabled in certain context (UA or 598 // chrome sheets) is always usable in the context regardless of the 599 // pref value. 600 // 601 // Non-experimental properties are either normal properties which are 602 // usable everywhere, or internal-only properties which are only usable 603 // in certain context they are explicitly enabled in. 604 if self.enabled_for_all_content() { 605 return true; 606 } 607 608 ${static_non_custom_property_id_set( 609 "ENABLED_IN_UA_SHEETS", 610 lambda p: p.explicitly_enabled_in_ua_sheets() 611 )} 612 ${static_non_custom_property_id_set( 613 "ENABLED_IN_CHROME", 614 lambda p: p.explicitly_enabled_in_chrome() 615 )} 616 617 if context.stylesheet_origin == Origin::UserAgent && 618 ENABLED_IN_UA_SHEETS.contains(self) 619 { 620 return true 621 } 622 623 if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) { 624 return true 625 } 626 627 false 628 } 629 630 /// The supported types of this property. The return value should be 631 /// style_traits::CssType when it can become a bitflags type. 632 pub(super) fn supported_types(&self) -> u8 { 633 const SUPPORTED_TYPES: [u8; ${len(data.longhands) + len(data.shorthands)}] = [ 634 % for prop in data.longhands: 635 <${prop.specified_type()} as SpecifiedValueInfo>::SUPPORTED_TYPES, 636 % endfor 637 % for prop in data.shorthands: 638 % if prop.name == "all": 639 0, // 'all' accepts no value other than CSS-wide keywords 640 % else: 641 <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES, 642 % endif 643 % endfor 644 ]; 645 SUPPORTED_TYPES[self.0 as usize] 646 } 647 648 /// See PropertyId::collect_property_completion_keywords. 649 pub(super) fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) { 650 fn do_nothing(_: KeywordsCollectFn) {} 651 const COLLECT_FUNCTIONS: [fn(KeywordsCollectFn); 652 ${len(data.longhands) + len(data.shorthands)}] = [ 653 % for prop in data.longhands: 654 <${prop.specified_type()} as SpecifiedValueInfo>::collect_completion_keywords, 655 % endfor 656 % for prop in data.shorthands: 657 % if prop.name == "all": 658 do_nothing, // 'all' accepts no value other than CSS-wide keywords 659 % else: 660 <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>:: 661 collect_completion_keywords, 662 % endif 663 % endfor 664 ]; 665 COLLECT_FUNCTIONS[self.0 as usize](f); 666 } 667 } 668 669 <%def name="static_non_custom_property_id_set(name, is_member)"> 670 static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet { 671 <% 672 storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32) 673 for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()): 674 if is_member(property): 675 storage[int(i / 32)] |= 1 << (i % 32) 676 %> 677 storage: [${", ".join("0x%x" % word for word in storage)}] 678 }; 679 </%def> 680 681 <%def name="static_longhand_id_set(name, is_member)"> 682 static ${name}: LonghandIdSet = LonghandIdSet { 683 <% 684 storage = [0] * int((len(data.longhands) - 1 + 32) / 32) 685 for i, property in enumerate(data.longhands): 686 if is_member(property): 687 storage[int(i / 32)] |= 1 << (i % 32) 688 %> 689 storage: [${", ".join("0x%x" % word for word in storage)}] 690 }; 691 </%def> 692 693 <% 694 logical_groups = defaultdict(list) 695 for prop in data.longhands: 696 if prop.logical_group: 697 logical_groups[prop.logical_group].append(prop) 698 699 for group, props in logical_groups.items(): 700 logical_count = sum(1 for p in props if p.logical) 701 if logical_count * 2 != len(props): 702 raise RuntimeError("Logical group {} has ".format(group) + 703 "unbalanced logical / physical properties") 704 705 FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data) 706 FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data) 707 MARKER_RESTRICTIONS = PropertyRestrictions.marker(data) 708 PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data) 709 CUE_RESTRICTIONS = PropertyRestrictions.cue(data) 710 711 def restriction_flags(property): 712 name = property.name 713 flags = [] 714 if name in FIRST_LINE_RESTRICTIONS: 715 flags.append("APPLIES_TO_FIRST_LINE") 716 if name in FIRST_LETTER_RESTRICTIONS: 717 flags.append("APPLIES_TO_FIRST_LETTER") 718 if name in PLACEHOLDER_RESTRICTIONS: 719 flags.append("APPLIES_TO_PLACEHOLDER") 720 if name in MARKER_RESTRICTIONS: 721 flags.append("APPLIES_TO_MARKER") 722 if name in CUE_RESTRICTIONS: 723 flags.append("APPLIES_TO_CUE") 724 return flags 725 726 %> 727 728 /// A group for properties which may override each other via logical resolution. 729 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 730 #[repr(u8)] 731 pub enum LogicalGroupId { 732 % for i, group in enumerate(logical_groups.keys()): 733 /// ${group} 734 ${to_camel_case(group)} = ${i}, 735 % endfor 736 } 737 738 impl LogicalGroupId { 739 /// Return the list of physical mapped properties for a given logical group. 740 fn physical_properties(self) -> &'static [LonghandId] { 741 static PROPS: [[LonghandId; 4]; ${len(logical_groups)}] = [ 742 % for group, props in logical_groups.items(): 743 [ 744 <% physical_props = [p for p in props if p.logical][0].all_physical_mapped_properties(data) %> 745 % for phys in physical_props: 746 LonghandId::${phys.camel_case}, 747 % endfor 748 % for i in range(len(physical_props), 4): 749 LonghandId::${physical_props[0].camel_case}, 750 % endfor 751 ], 752 % endfor 753 ]; 754 &PROPS[self as usize] 755 } 756 } 757 758 /// A set of logical groups. 759 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)] 760 pub struct LogicalGroupSet { 761 storage: [u32; (${len(logical_groups)} - 1 + 32) / 32] 762 } 763 764 impl LogicalGroupSet { 765 /// Creates an empty `NonCustomPropertyIdSet`. 766 pub fn new() -> Self { 767 Self { 768 storage: Default::default(), 769 } 770 } 771 772 /// Return whether the given group is in the set 773 #[inline] 774 pub fn contains(&self, g: LogicalGroupId) -> bool { 775 let bit = g as usize; 776 (self.storage[bit / 32] & (1 << (bit % 32))) != 0 777 } 778 779 /// Insert a group the set. 780 #[inline] 781 pub fn insert(&mut self, g: LogicalGroupId) { 782 let bit = g as usize; 783 self.storage[bit / 32] |= 1 << (bit % 32); 784 } 785 } 786 787 788 #[repr(u8)] 789 #[derive(Copy, Clone, Debug)] 790 pub(crate) enum PrioritaryPropertyId { 791 % for p in data.longhands: 792 % if p.is_prioritary(): 793 ${p.camel_case}, 794 % endif 795 % endfor 796 } 797 798 impl PrioritaryPropertyId { 799 #[inline] 800 pub fn to_longhand(self) -> LonghandId { 801 static PRIORITARY_TO_LONGHAND: [LonghandId; property_counts::PRIORITARY] = [ 802 % for p in data.longhands: 803 % if p.is_prioritary(): 804 LonghandId::${p.camel_case}, 805 % endif 806 % endfor 807 ]; 808 PRIORITARY_TO_LONGHAND[self as usize] 809 } 810 #[inline] 811 pub fn from_longhand(l: LonghandId) -> Option<Self> { 812 static LONGHAND_TO_PRIORITARY: [Option<PrioritaryPropertyId>; ${len(data.longhands)}] = [ 813 % for p in data.longhands: 814 % if p.is_prioritary(): 815 Some(PrioritaryPropertyId::${p.camel_case}), 816 % else: 817 None, 818 % endif 819 % endfor 820 ]; 821 LONGHAND_TO_PRIORITARY[l as usize] 822 } 823 } 824 825 impl LonghandIdSet { 826 /// The set of non-inherited longhands. 827 #[inline] 828 pub(super) fn reset() -> &'static Self { 829 ${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)} 830 &RESET 831 } 832 833 #[inline] 834 pub(super) fn discrete_animatable() -> &'static Self { 835 ${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_type == "discrete")} 836 &DISCRETE_ANIMATABLE 837 } 838 839 #[inline] 840 pub(super) fn logical() -> &'static Self { 841 ${static_longhand_id_set("LOGICAL", lambda p: p.logical)} 842 &LOGICAL 843 } 844 845 /// Returns the set of longhands that are ignored when document colors are 846 /// disabled. 847 #[inline] 848 pub(super) fn ignored_when_colors_disabled() -> &'static Self { 849 ${static_longhand_id_set( 850 "IGNORED_WHEN_COLORS_DISABLED", 851 lambda p: p.ignored_when_colors_disabled 852 )} 853 &IGNORED_WHEN_COLORS_DISABLED 854 } 855 856 /// Only a few properties are allowed to depend on the visited state of 857 /// links. When cascading visited styles, we can save time by only 858 /// processing these properties. 859 pub(super) fn visited_dependent() -> &'static Self { 860 ${static_longhand_id_set("VISITED_DEPENDENT", lambda p: p.is_visited_dependent())} 861 debug_assert!(Self::late_group().contains_all(&VISITED_DEPENDENT)); 862 &VISITED_DEPENDENT 863 } 864 865 #[inline] 866 pub(super) fn prioritary_properties() -> &'static Self { 867 ${static_longhand_id_set("PRIORITARY_PROPERTIES", lambda p: p.is_prioritary())} 868 &PRIORITARY_PROPERTIES 869 } 870 871 #[inline] 872 pub(super) fn late_group_only_inherited() -> &'static Self { 873 ${static_longhand_id_set("LATE_GROUP_ONLY_INHERITED", lambda p: p.style_struct.inherited and not p.is_prioritary())} 874 &LATE_GROUP_ONLY_INHERITED 875 } 876 877 #[inline] 878 pub(super) fn late_group() -> &'static Self { 879 ${static_longhand_id_set("LATE_GROUP", lambda p: not p.is_prioritary())} 880 &LATE_GROUP 881 } 882 883 /// Returns the set of properties that are declared as having no effect on 884 /// Gecko <scrollbar> elements or their descendant scrollbar parts. 885 #[cfg(debug_assertions)] 886 #[cfg(feature = "gecko")] 887 #[inline] 888 pub fn has_no_effect_on_gecko_scrollbars() -> &'static Self { 889 // data.py asserts that has_no_effect_on_gecko_scrollbars is True or 890 // False for properties that are inherited and Gecko pref controlled, 891 // and is None for all other properties. 892 ${static_longhand_id_set( 893 "HAS_NO_EFFECT_ON_SCROLLBARS", 894 lambda p: p.has_effect_on_gecko_scrollbars is False 895 )} 896 &HAS_NO_EFFECT_ON_SCROLLBARS 897 } 898 899 /// Returns the set of margin properties, for the purposes of <h1> use counters / warnings. 900 #[inline] 901 pub fn margin_properties() -> &'static Self { 902 ${static_longhand_id_set( 903 "MARGIN_PROPERTIES", 904 lambda p: p.logical_group == "margin" 905 )} 906 &MARGIN_PROPERTIES 907 } 908 909 /// Returns the set of border properties for the purpose of disabling native 910 /// appearance. 911 #[inline] 912 pub fn border_background_properties() -> &'static Self { 913 ${static_longhand_id_set( 914 "BORDER_BACKGROUND_PROPERTIES", 915 lambda p: (p.logical_group and p.logical_group.startswith("border")) or \ 916 p in data.shorthands_by_name["border"].sub_properties or \ 917 p in data.shorthands_by_name["background"].sub_properties and \ 918 p.name not in ["background-blend-mode", "background-repeat"] 919 )} 920 &BORDER_BACKGROUND_PROPERTIES 921 } 922 923 /// Returns properties that are zoom dependent (basically, that contain lengths). 924 #[inline] 925 pub fn zoom_dependent() -> &'static Self { 926 ${static_longhand_id_set("ZOOM_DEPENDENT", lambda p: p.is_zoom_dependent())} 927 &ZOOM_DEPENDENT 928 } 929 930 /// Note that it's different from zoom_dependent(), as this only includes inherited, physical 931 /// properties. 932 #[inline] 933 pub fn zoom_dependent_inherited_properties() -> &'static Self { 934 ${static_longhand_id_set("ZOOM_DEPENDENT_INHERITED", lambda p: p.is_inherited_zoom_dependent_property())} 935 &ZOOM_DEPENDENT_INHERITED 936 } 937 } 938 939 /// An identifier for a given longhand property. 940 #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] 941 #[repr(u16)] 942 pub enum LonghandId { 943 % for i, property in enumerate(data.longhands): 944 /// ${property.name} 945 ${property.camel_case} = ${i}, 946 % endfor 947 } 948 949 enum LogicalMappingKind { 950 Side(LogicalSide), 951 Corner(LogicalCorner), 952 Axis(LogicalAxis), 953 } 954 955 struct LogicalMappingData { 956 group: LogicalGroupId, 957 kind: LogicalMappingKind, 958 } 959 960 impl LogicalMappingData { 961 fn to_physical(&self, wm: WritingMode) -> LonghandId { 962 let index = match self.kind { 963 LogicalMappingKind::Side(s) => s.to_physical(wm) as usize, 964 LogicalMappingKind::Corner(c) => c.to_physical(wm) as usize, 965 LogicalMappingKind::Axis(a) => a.to_physical(wm) as usize, 966 }; 967 self.group.physical_properties()[index] 968 } 969 } 970 971 impl LonghandId { 972 /// Returns an iterator over all the shorthands that include this longhand. 973 pub fn shorthands(self) -> NonCustomPropertyIterator<ShorthandId> { 974 // first generate longhand to shorthands lookup map 975 // 976 // NOTE(emilio): This currently doesn't exclude the "all" shorthand. It 977 // could potentially do so, which would speed up serialization 978 // algorithms and what not, I guess. 979 <% 980 from functools import cmp_to_key 981 longhand_to_shorthand_map = {} 982 num_sub_properties = {} 983 for shorthand in data.shorthands: 984 num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties) 985 for sub_property in shorthand.sub_properties: 986 if sub_property.ident not in longhand_to_shorthand_map: 987 longhand_to_shorthand_map[sub_property.ident] = [] 988 989 longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case) 990 991 def cmp(a, b): 992 return (a > b) - (a < b) 993 994 def preferred_order(x, y): 995 # Since we want properties in order from most subproperties to least, 996 # reverse the arguments to cmp from the expected order. 997 result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0)) 998 if result: 999 return result 1000 # Fall back to lexicographic comparison. 1001 return cmp(x, y) 1002 1003 # Sort the lists of shorthand properties according to preferred order: 1004 # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order 1005 for shorthand_list in longhand_to_shorthand_map.values(): 1006 shorthand_list.sort(key=cmp_to_key(preferred_order)) 1007 %> 1008 1009 // based on lookup results for each longhand, create result arrays 1010 static MAP: [&'static [ShorthandId]; property_counts::LONGHANDS] = [ 1011 % for property in data.longhands: 1012 &[ 1013 % for shorthand in longhand_to_shorthand_map.get(property.ident, []): 1014 ShorthandId::${shorthand}, 1015 % endfor 1016 ], 1017 % endfor 1018 ]; 1019 1020 NonCustomPropertyIterator { 1021 filter: NonCustomPropertyId::from(self).enabled_for_all_content(), 1022 iter: MAP[self as usize].iter(), 1023 } 1024 } 1025 1026 pub(super) fn parse_value<'i, 't>( 1027 self, 1028 context: &ParserContext, 1029 input: &mut Parser<'i, 't>, 1030 ) -> Result<PropertyDeclaration, ParseError<'i>> { 1031 type ParsePropertyFn = for<'i, 't> fn( 1032 context: &ParserContext, 1033 input: &mut Parser<'i, 't>, 1034 ) -> Result<PropertyDeclaration, ParseError<'i>>; 1035 static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [ 1036 % for property in data.longhands: 1037 longhands::${property.ident}::parse_declared, 1038 % endfor 1039 ]; 1040 (PARSE_PROPERTY[self as usize])(context, input) 1041 } 1042 1043 /// Return the relevant data to map a particular logical property into physical. 1044 fn logical_mapping_data(self) -> Option<&'static LogicalMappingData> { 1045 const LOGICAL_MAPPING_DATA: [Option<LogicalMappingData>; ${len(data.longhands)}] = [ 1046 % for prop in data.longhands: 1047 % if prop.logical: 1048 Some(LogicalMappingData { 1049 group: LogicalGroupId::${to_camel_case(prop.logical_group)}, 1050 kind: ${prop.logical_mapping_kind(data)} 1051 }), 1052 % else: 1053 None, 1054 % endif 1055 % endfor 1056 ]; 1057 LOGICAL_MAPPING_DATA[self as usize].as_ref() 1058 } 1059 1060 /// If this is a logical property, return the corresponding physical one in the given 1061 /// writing mode. Otherwise, return unchanged. 1062 #[inline] 1063 pub fn to_physical(self, wm: WritingMode) -> Self { 1064 let Some(data) = self.logical_mapping_data() else { return self }; 1065 data.to_physical(wm) 1066 } 1067 1068 /// Return the logical group of this longhand property. 1069 pub fn logical_group(self) -> Option<LogicalGroupId> { 1070 const LOGICAL_GROUP_IDS: [Option<LogicalGroupId>; ${len(data.longhands)}] = [ 1071 % for prop in data.longhands: 1072 % if prop.logical_group: 1073 Some(LogicalGroupId::${to_camel_case(prop.logical_group)}), 1074 % else: 1075 None, 1076 % endif 1077 % endfor 1078 ]; 1079 LOGICAL_GROUP_IDS[self as usize] 1080 } 1081 1082 /// Returns PropertyFlags for given longhand property. 1083 #[inline(always)] 1084 pub fn flags(self) -> PropertyFlags { 1085 const FLAGS: [PropertyFlags; ${len(data.longhands)}] = [ 1086 % for property in data.longhands: 1087 PropertyFlags::empty() 1088 % for flag in property.flags + restriction_flags(property): 1089 .union(PropertyFlags::${flag}) 1090 % endfor 1091 , 1092 % endfor 1093 ]; 1094 FLAGS[self as usize] 1095 } 1096 } 1097 1098 /// An identifier for a given shorthand property. 1099 #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)] 1100 #[repr(u16)] 1101 pub enum ShorthandId { 1102 % for i, property in enumerate(data.shorthands): 1103 /// ${property.name} 1104 ${property.camel_case} = ${i}, 1105 % endfor 1106 } 1107 1108 impl ShorthandId { 1109 /// Get the longhand ids that form this shorthand. 1110 pub fn longhands(self) -> NonCustomPropertyIterator<LonghandId> { 1111 static MAP: [&'static [LonghandId]; property_counts::SHORTHANDS] = [ 1112 % for property in data.shorthands: 1113 &[ 1114 % for sub in property.sub_properties: 1115 LonghandId::${sub.camel_case}, 1116 % endfor 1117 ], 1118 % endfor 1119 ]; 1120 NonCustomPropertyIterator { 1121 filter: NonCustomPropertyId::from(self).enabled_for_all_content(), 1122 iter: MAP[self as usize].iter(), 1123 } 1124 } 1125 1126 /// Try to serialize the given declarations as this shorthand. 1127 /// 1128 /// Returns an error if writing to the stream fails, or if the declarations 1129 /// do not map to a shorthand. 1130 pub fn longhands_to_css( 1131 self, 1132 declarations: &[&PropertyDeclaration], 1133 dest: &mut CssStringWriter, 1134 ) -> fmt::Result { 1135 type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result; 1136 fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result { 1137 // No need to try to serialize the declarations as the 'all' 1138 // shorthand, since it only accepts CSS-wide keywords (and variable 1139 // references), which will be handled in 1140 // get_shorthand_appendable_value. 1141 Ok(()) 1142 } 1143 1144 static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [ 1145 % for shorthand in data.shorthands: 1146 % if shorthand.ident == "all": 1147 all_to_css, 1148 % else: 1149 shorthands::${shorthand.ident}::to_css, 1150 % endif 1151 % endfor 1152 ]; 1153 1154 LONGHANDS_TO_CSS[self as usize](declarations, dest) 1155 } 1156 1157 /// Returns PropertyFlags for the given shorthand property. 1158 #[inline] 1159 pub fn flags(self) -> PropertyFlags { 1160 const FLAGS: [u16; ${len(data.shorthands)}] = [ 1161 % for property in data.shorthands: 1162 % for flag in property.flags: 1163 PropertyFlags::${flag}.bits() | 1164 % endfor 1165 0, 1166 % endfor 1167 ]; 1168 PropertyFlags::from_bits_retain(FLAGS[self as usize]) 1169 } 1170 1171 /// Returns the order in which this property appears relative to other 1172 /// shorthands in idl-name-sorting order. 1173 #[inline] 1174 pub fn idl_name_sort_order(self) -> u32 { 1175 <% 1176 from data import to_idl_name 1177 ordered = {} 1178 sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident)) 1179 for order, shorthand in enumerate(sorted_shorthands): 1180 ordered[shorthand.ident] = order 1181 %> 1182 static IDL_NAME_SORT_ORDER: [u32; ${len(data.shorthands)}] = [ 1183 % for property in data.shorthands: 1184 ${ordered[property.ident]}, 1185 % endfor 1186 ]; 1187 IDL_NAME_SORT_ORDER[self as usize] 1188 } 1189 1190 pub(super) fn parse_into<'i, 't>( 1191 self, 1192 declarations: &mut SourcePropertyDeclaration, 1193 context: &ParserContext, 1194 input: &mut Parser<'i, 't>, 1195 ) -> Result<(), ParseError<'i>> { 1196 type ParseIntoFn = for<'i, 't> fn( 1197 declarations: &mut SourcePropertyDeclaration, 1198 context: &ParserContext, 1199 input: &mut Parser<'i, 't>, 1200 ) -> Result<(), ParseError<'i>>; 1201 1202 fn parse_all<'i, 't>( 1203 _: &mut SourcePropertyDeclaration, 1204 _: &ParserContext, 1205 input: &mut Parser<'i, 't> 1206 ) -> Result<(), ParseError<'i>> { 1207 // 'all' accepts no value other than CSS-wide keywords 1208 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 1209 } 1210 1211 static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [ 1212 % for shorthand in data.shorthands: 1213 % if shorthand.ident == "all": 1214 parse_all, 1215 % else: 1216 shorthands::${shorthand.ident}::parse_into, 1217 % endif 1218 % endfor 1219 ]; 1220 1221 (PARSE_INTO[self as usize])(declarations, context, input) 1222 } 1223 } 1224 1225 /// The counted unknown property list which is used for css use counters. 1226 /// 1227 /// FIXME: This should be just #[repr(u8)], but can't be because of ABI issues, 1228 /// see https://bugs.llvm.org/show_bug.cgi?id=44228. 1229 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)] 1230 #[repr(u32)] 1231 pub enum CountedUnknownProperty { 1232 % for prop in data.counted_unknown_properties: 1233 /// ${prop.name} 1234 ${prop.camel_case}, 1235 % endfor 1236 } 1237 1238 impl CountedUnknownProperty { 1239 /// Parse the counted unknown property, for testing purposes only. 1240 pub fn parse_for_testing(property_name: &str) -> Option<Self> { 1241 ::cssparser::ascii_case_insensitive_phf_map! { 1242 unknown_ids -> CountedUnknownProperty = { 1243 % for property in data.counted_unknown_properties: 1244 "${property.name}" => CountedUnknownProperty::${property.camel_case}, 1245 % endfor 1246 } 1247 } 1248 unknown_ids::get(property_name).cloned() 1249 } 1250 1251 /// Returns the underlying index, used for use counter. 1252 #[inline] 1253 pub fn bit(self) -> usize { 1254 self as usize 1255 } 1256 } 1257 1258 impl PropertyId { 1259 /// Returns a given property from the given name, _regardless of whether it 1260 /// is enabled or not_, or Err(()) for unknown properties. 1261 pub fn parse_unchecked( 1262 property_name: &str, 1263 use_counters: Option<&UseCounters>, 1264 ) -> Result<Self, ()> { 1265 // A special id for css use counters. ShorthandAlias is not used in the Servo build. 1266 // That's why we need to allow dead_code. 1267 pub enum StaticId { 1268 NonCustom(NonCustomPropertyId), 1269 CountedUnknown(CountedUnknownProperty), 1270 } 1271 ::cssparser::ascii_case_insensitive_phf_map! { 1272 static_ids -> StaticId = { 1273 % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()): 1274 "${property.name}" => StaticId::NonCustom(NonCustomPropertyId(${i})), 1275 % endfor 1276 % for property in data.counted_unknown_properties: 1277 "${property.name}" => { 1278 StaticId::CountedUnknown(CountedUnknownProperty::${property.camel_case}) 1279 }, 1280 % endfor 1281 } 1282 } 1283 1284 if let Some(id) = static_ids::get(property_name) { 1285 return Ok(match *id { 1286 StaticId::NonCustom(id) => PropertyId::NonCustom(id), 1287 StaticId::CountedUnknown(unknown_prop) => { 1288 if let Some(counters) = use_counters { 1289 counters.counted_unknown_properties.record(unknown_prop); 1290 } 1291 // Always return Err(()) because these aren't valid custom property names. 1292 return Err(()); 1293 } 1294 }); 1295 } 1296 1297 let name = crate::custom_properties::parse_name(property_name)?; 1298 Ok(PropertyId::Custom(crate::custom_properties::Name::from(name))) 1299 } 1300 } 1301 1302 impl PropertyDeclaration { 1303 /// Given a property declaration, return the property declaration id. 1304 #[inline] 1305 pub fn id(&self) -> PropertyDeclarationId<'_> { 1306 match *self { 1307 PropertyDeclaration::Custom(ref declaration) => { 1308 return PropertyDeclarationId::Custom(&declaration.name) 1309 } 1310 PropertyDeclaration::CSSWideKeyword(ref declaration) => { 1311 return PropertyDeclarationId::Longhand(declaration.id); 1312 } 1313 PropertyDeclaration::WithVariables(ref declaration) => { 1314 return PropertyDeclarationId::Longhand(declaration.id); 1315 } 1316 _ => {} 1317 } 1318 // This is just fine because PropertyDeclaration and LonghandId 1319 // have corresponding discriminants. 1320 let id = unsafe { *(self as *const _ as *const LonghandId) }; 1321 debug_assert_eq!(id, match *self { 1322 % for property in data.longhands: 1323 PropertyDeclaration::${property.camel_case}(..) => LonghandId::${property.camel_case}, 1324 % endfor 1325 _ => id, 1326 }); 1327 PropertyDeclarationId::Longhand(id) 1328 } 1329 1330 /// Given a declaration, convert it into a declaration for a corresponding 1331 /// physical property. 1332 #[inline] 1333 pub fn to_physical(&self, wm: WritingMode) -> Self { 1334 match *self { 1335 PropertyDeclaration::WithVariables(VariableDeclaration { 1336 id, 1337 ref value, 1338 }) => { 1339 return PropertyDeclaration::WithVariables(VariableDeclaration { 1340 id: id.to_physical(wm), 1341 value: value.clone(), 1342 }) 1343 } 1344 PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration { 1345 id, 1346 keyword, 1347 }) => { 1348 return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration { 1349 id: id.to_physical(wm), 1350 keyword, 1351 }) 1352 } 1353 PropertyDeclaration::Custom(..) => return self.clone(), 1354 % for prop in data.longhands: 1355 PropertyDeclaration::${prop.camel_case}(..) => {}, 1356 % endfor 1357 } 1358 1359 let mut ret = self.clone(); 1360 1361 % for prop in data.longhands: 1362 % for physical_property in prop.all_physical_mapped_properties(data): 1363 % if physical_property.specified_type() != prop.specified_type(): 1364 <% raise "Logical property %s should share specified value with physical property %s" % \ 1365 (prop.name, physical_property.name) %> 1366 % endif 1367 % endfor 1368 % endfor 1369 1370 unsafe { 1371 let longhand_id = *(&mut ret as *mut _ as *mut LonghandId); 1372 1373 debug_assert_eq!( 1374 PropertyDeclarationId::Longhand(longhand_id), 1375 ret.id() 1376 ); 1377 1378 // This is just fine because PropertyDeclaration and LonghandId 1379 // have corresponding discriminants. 1380 *(&mut ret as *mut _ as *mut LonghandId) = longhand_id.to_physical(wm); 1381 1382 debug_assert_eq!( 1383 PropertyDeclarationId::Longhand(longhand_id.to_physical(wm)), 1384 ret.id() 1385 ); 1386 } 1387 1388 ret 1389 } 1390 1391 /// Returns whether or not the property is set by a system font 1392 pub fn get_system(&self) -> Option<SystemFont> { 1393 match *self { 1394 % if engine == "gecko": 1395 % for prop in SYSTEM_FONT_LONGHANDS: 1396 PropertyDeclaration::${to_camel_case(prop)}(ref prop) => { 1397 prop.get_system() 1398 } 1399 % endfor 1400 % endif 1401 _ => None, 1402 } 1403 } 1404 } 1405 1406 #[cfg(feature = "gecko")] 1407 pub use super::gecko::style_structs; 1408 1409 /// The module where all the style structs are defined. 1410 #[cfg(feature = "servo")] 1411 pub mod style_structs { 1412 use rustc_hash::FxHasher; 1413 use super::longhands; 1414 use std::hash::{Hash, Hasher}; 1415 use crate::values::specified::color::ColorSchemeFlags; 1416 use crate::derives::*; 1417 1418 % for style_struct in data.active_style_structs(): 1419 % if style_struct.name == "Font": 1420 #[derive(Clone, Debug, MallocSizeOf)] 1421 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] 1422 % else: 1423 #[derive(Clone, Debug, MallocSizeOf, PartialEq)] 1424 % endif 1425 /// The ${style_struct.name} style struct. 1426 pub struct ${style_struct.name} { 1427 % for longhand in style_struct.longhands: 1428 % if not longhand.logical: 1429 /// The ${longhand.name} computed value. 1430 pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T, 1431 % endif 1432 % endfor 1433 % if style_struct.name == "Font": 1434 /// The font hash, used for font caching. 1435 pub hash: u64, 1436 % endif 1437 % if style_struct.name == "Box": 1438 /// The display value specified by the CSS stylesheets (without any style adjustments), 1439 /// which is needed for hypothetical layout boxes. 1440 pub original_display: longhands::display::computed_value::T, 1441 % endif 1442 } 1443 % if style_struct.name == "Font": 1444 impl PartialEq for Font { 1445 fn eq(&self, other: &Font) -> bool { 1446 self.hash == other.hash 1447 % for longhand in style_struct.longhands: 1448 && self.${longhand.ident} == other.${longhand.ident} 1449 % endfor 1450 } 1451 } 1452 % endif 1453 1454 impl ${style_struct.name} { 1455 % for longhand in style_struct.longhands: 1456 % if not longhand.logical: 1457 % if longhand.ident == "display": 1458 /// Set `display`. 1459 /// 1460 /// We need to keep track of the original display for hypothetical boxes, 1461 /// so we need to special-case this. 1462 #[allow(non_snake_case)] 1463 #[inline] 1464 pub fn set_display(&mut self, v: longhands::display::computed_value::T) { 1465 self.display = v; 1466 self.original_display = v; 1467 } 1468 % else: 1469 /// Set ${longhand.name}. 1470 #[allow(non_snake_case)] 1471 #[inline] 1472 pub fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) { 1473 self.${longhand.ident} = v; 1474 } 1475 % endif 1476 % if longhand.ident == "display": 1477 /// Set `display` from other struct. 1478 /// 1479 /// Same as `set_display` above. 1480 /// Thus, we need to special-case this. 1481 #[allow(non_snake_case)] 1482 #[inline] 1483 pub fn copy_display_from(&mut self, other: &Self) { 1484 self.display = other.display.clone(); 1485 self.original_display = other.display.clone(); 1486 } 1487 % else: 1488 /// Set ${longhand.name} from other struct. 1489 #[allow(non_snake_case)] 1490 #[inline] 1491 pub fn copy_${longhand.ident}_from(&mut self, other: &Self) { 1492 self.${longhand.ident} = other.${longhand.ident}.clone(); 1493 } 1494 % endif 1495 /// Reset ${longhand.name} from the initial struct. 1496 #[allow(non_snake_case)] 1497 #[inline] 1498 pub fn reset_${longhand.ident}(&mut self, other: &Self) { 1499 self.copy_${longhand.ident}_from(other) 1500 } 1501 1502 /// Get the computed value for ${longhand.name}. 1503 #[allow(non_snake_case)] 1504 #[inline] 1505 pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T { 1506 self.${longhand.ident}.clone() 1507 } 1508 % endif 1509 % if longhand.need_index: 1510 /// If this longhand is indexed, get the number of elements. 1511 #[allow(non_snake_case)] 1512 pub fn ${longhand.ident}_count(&self) -> usize { 1513 self.${longhand.ident}.0.len() 1514 } 1515 1516 /// If this longhand is indexed, get the element at given 1517 /// index. 1518 #[allow(non_snake_case)] 1519 pub fn ${longhand.ident}_at(&self, index: usize) 1520 -> longhands::${longhand.ident}::computed_value::SingleComputedValue { 1521 self.${longhand.ident}.0[index].clone() 1522 } 1523 % endif 1524 % endfor 1525 % if style_struct.name == "Border": 1526 % for side in ["top", "right", "bottom", "left"]: 1527 /// Whether the border-${side} property has nonzero width. 1528 #[allow(non_snake_case)] 1529 pub fn border_${side}_has_nonzero_width(&self) -> bool { 1530 use crate::Zero; 1531 !self.border_${side}_width.is_zero() 1532 } 1533 % endfor 1534 % elif style_struct.name == "Font": 1535 /// Computes a font hash in order to be able to cache fonts 1536 /// effectively in GFX and layout. 1537 pub fn compute_font_hash(&mut self) { 1538 let mut hasher: FxHasher = Default::default(); 1539 self.font_weight.hash(&mut hasher); 1540 self.font_stretch.hash(&mut hasher); 1541 self.font_style.hash(&mut hasher); 1542 self.font_family.hash(&mut hasher); 1543 self.hash = hasher.finish() 1544 } 1545 /// Create a new Font with the initial values of all members. 1546 pub fn initial_values() -> Self { 1547 Self { 1548 % for longhand in style_struct.longhands: 1549 % if not longhand.logical: 1550 ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(), 1551 % endif 1552 % endfor 1553 hash: 0, 1554 } 1555 } 1556 % elif style_struct.name == "InheritedUI": 1557 /// Returns the ColorSchemeFlags corresponding to the value of `color-scheme`. 1558 #[inline] 1559 pub fn color_scheme_bits(&self) -> ColorSchemeFlags { 1560 self.color_scheme.bits 1561 } 1562 % elif style_struct.name == "Outline": 1563 /// Whether the outline-width property is non-zero. 1564 #[inline] 1565 pub fn outline_has_nonzero_width(&self) -> bool { 1566 use crate::Zero; 1567 !self.outline_width.is_zero() 1568 } 1569 % elif style_struct.name == "Box": 1570 /// Sets the display property, but without touching original_display, 1571 /// except when the adjustment comes from root or item display fixups. 1572 pub fn set_adjusted_display( 1573 &mut self, 1574 dpy: longhands::display::computed_value::T, 1575 is_item_or_root: bool 1576 ) { 1577 self.display = dpy; 1578 if is_item_or_root { 1579 self.original_display = dpy; 1580 } 1581 } 1582 % endif 1583 } 1584 1585 % endfor 1586 } 1587 1588 % for style_struct in data.active_style_structs(): 1589 impl style_structs::${style_struct.name} { 1590 % for longhand in style_struct.longhands: 1591 % if longhand.need_index: 1592 /// Iterate over the values of ${longhand.name}. 1593 #[allow(non_snake_case)] 1594 #[inline] 1595 pub fn ${longhand.ident}_iter(&self) -> ${longhand.camel_case}Iter<'_> { 1596 ${longhand.camel_case}Iter { 1597 style_struct: self, 1598 current: 0, 1599 max: self.${longhand.ident}_count(), 1600 } 1601 } 1602 1603 /// Get a value mod `index` for the property ${longhand.name}. 1604 #[allow(non_snake_case)] 1605 #[inline] 1606 pub fn ${longhand.ident}_mod(&self, index: usize) 1607 -> longhands::${longhand.ident}::computed_value::SingleComputedValue { 1608 self.${longhand.ident}_at(index % self.${longhand.ident}_count()) 1609 } 1610 1611 /// Clone the computed value for the property. 1612 #[allow(non_snake_case)] 1613 #[inline] 1614 #[cfg(feature = "gecko")] 1615 pub fn clone_${longhand.ident}( 1616 &self, 1617 ) -> longhands::${longhand.ident}::computed_value::T { 1618 longhands::${longhand.ident}::computed_value::List( 1619 self.${longhand.ident}_iter().collect() 1620 ) 1621 } 1622 % endif 1623 % endfor 1624 1625 % if style_struct.name == "UI": 1626 /// Returns whether there is any animation specified with 1627 /// animation-name other than `none`. 1628 pub fn specifies_animations(&self) -> bool { 1629 self.animation_name_iter().any(|name| !name.is_none()) 1630 } 1631 1632 /// Returns whether there are any transitions specified. 1633 #[cfg(feature = "servo")] 1634 pub fn specifies_transitions(&self) -> bool { 1635 (0..self.transition_property_count()).any(|index| { 1636 let combined_duration = 1637 self.transition_duration_mod(index).seconds().max(0.) + 1638 self.transition_delay_mod(index).seconds(); 1639 combined_duration > 0. 1640 }) 1641 } 1642 1643 /// Returns whether animation-timeline is initial value. We need this information to 1644 /// resolve animation-duration. 1645 #[cfg(feature = "servo")] 1646 pub fn has_initial_animation_timeline(&self) -> bool { 1647 self.animation_timeline_count() == 1 && self.animation_timeline_at(0).is_auto() 1648 } 1649 1650 /// Returns whether there is any named progress timeline specified with 1651 /// scroll-timeline-name other than `none`. 1652 #[cfg(feature = "gecko")] 1653 pub fn specifies_scroll_timelines(&self) -> bool { 1654 self.scroll_timeline_name_iter().any(|name| !name.is_none()) 1655 } 1656 1657 /// Returns whether there is any named progress timeline specified with 1658 /// view-timeline-name other than `none`. 1659 #[cfg(feature = "gecko")] 1660 pub fn specifies_view_timelines(&self) -> bool { 1661 self.view_timeline_name_iter().any(|name| !name.is_none()) 1662 } 1663 1664 /// Returns true if animation properties are equal between styles, but without 1665 /// considering keyframe data and animation-timeline. 1666 #[cfg(feature = "servo")] 1667 pub fn animations_equals(&self, other: &Self) -> bool { 1668 self.animation_name_iter().eq(other.animation_name_iter()) && 1669 self.animation_delay_iter().eq(other.animation_delay_iter()) && 1670 self.animation_direction_iter().eq(other.animation_direction_iter()) && 1671 self.animation_duration_iter().eq(other.animation_duration_iter()) && 1672 self.animation_fill_mode_iter().eq(other.animation_fill_mode_iter()) && 1673 self.animation_iteration_count_iter().eq(other.animation_iteration_count_iter()) && 1674 self.animation_play_state_iter().eq(other.animation_play_state_iter()) && 1675 self.animation_timing_function_iter().eq(other.animation_timing_function_iter()) 1676 } 1677 1678 % elif style_struct.name == "Column": 1679 /// Whether this is a multicol style. 1680 #[cfg(feature = "servo")] 1681 pub fn is_multicol(&self) -> bool { 1682 !self.column_width.is_auto() || !self.column_count.is_auto() 1683 } 1684 % endif 1685 } 1686 1687 % for longhand in style_struct.longhands: 1688 % if longhand.need_index: 1689 /// An iterator over the values of the ${longhand.name} properties. 1690 pub struct ${longhand.camel_case}Iter<'a> { 1691 style_struct: &'a style_structs::${style_struct.name}, 1692 current: usize, 1693 max: usize, 1694 } 1695 1696 impl<'a> Iterator for ${longhand.camel_case}Iter<'a> { 1697 type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue; 1698 1699 fn next(&mut self) -> Option<Self::Item> { 1700 self.current += 1; 1701 if self.current <= self.max { 1702 Some(self.style_struct.${longhand.ident}_at(self.current - 1)) 1703 } else { 1704 None 1705 } 1706 } 1707 } 1708 % endif 1709 % endfor 1710 % endfor 1711 1712 1713 #[cfg(feature = "gecko")] 1714 pub use super::gecko::{ComputedValues, ComputedValuesInner}; 1715 1716 #[cfg(feature = "servo")] 1717 #[cfg_attr(feature = "servo", derive(Clone, Debug))] 1718 /// Actual data of ComputedValues, to match up with Gecko 1719 pub struct ComputedValuesInner { 1720 % for style_struct in data.active_style_structs(): 1721 ${style_struct.ident}: Arc<style_structs::${style_struct.name}>, 1722 % endfor 1723 custom_properties: crate::custom_properties::ComputedCustomProperties, 1724 1725 /// The effective zoom value. 1726 pub effective_zoom: computed::Zoom, 1727 1728 /// A set of flags we use to store misc information regarding this style. 1729 pub flags: ComputedValueFlags, 1730 1731 /// The writing mode of this computed values struct. 1732 pub writing_mode: WritingMode, 1733 1734 /// The rule node representing the ordered list of rules matched for this 1735 /// node. Can be None for default values and text nodes. This is 1736 /// essentially an optimization to avoid referencing the root rule node. 1737 pub rules: Option<StrongRuleNode>, 1738 1739 /// The element's computed values if visited, only computed if there's a 1740 /// relevant link for this element. A element's "relevant link" is the 1741 /// element being matched if it is a link or the nearest ancestor link. 1742 visited_style: Option<Arc<ComputedValues>>, 1743 } 1744 1745 /// The struct that Servo uses to represent computed values. 1746 /// 1747 /// This struct contains an immutable atomically-reference-counted pointer to 1748 /// every kind of style struct. 1749 /// 1750 /// When needed, the structs may be copied in order to get mutated. 1751 #[cfg(feature = "servo")] 1752 #[cfg_attr(feature = "servo", derive(Clone, Debug))] 1753 pub struct ComputedValues { 1754 /// The actual computed values 1755 /// 1756 /// In Gecko the outer ComputedValues is actually a ComputedStyle, whereas 1757 /// ComputedValuesInner is the core set of computed values. 1758 /// 1759 /// We maintain this distinction in servo to reduce the amount of special 1760 /// casing. 1761 inner: ComputedValuesInner, 1762 1763 /// The pseudo-element that we're using. 1764 pseudo: Option<PseudoElement>, 1765 } 1766 1767 impl ComputedValues { 1768 /// Returns the pseudo-element that this style represents. 1769 #[cfg(feature = "servo")] 1770 pub fn pseudo(&self) -> Option<&PseudoElement> { 1771 self.pseudo.as_ref() 1772 } 1773 1774 /// Returns true if this is the style for a pseudo-element. 1775 #[cfg(feature = "servo")] 1776 pub fn is_pseudo_style(&self) -> bool { 1777 self.pseudo().is_some() 1778 } 1779 1780 /// Returns whether this style's display value is equal to contents. 1781 pub fn is_display_contents(&self) -> bool { 1782 self.clone_display().is_contents() 1783 } 1784 1785 /// Gets a reference to the rule node. Panic if no rule node exists. 1786 pub fn rules(&self) -> &StrongRuleNode { 1787 self.rules.as_ref().unwrap() 1788 } 1789 1790 /// Returns the visited rules, if applicable. 1791 pub fn visited_rules(&self) -> Option<&StrongRuleNode> { 1792 self.visited_style().and_then(|s| s.rules.as_ref()) 1793 } 1794 1795 /// Gets a reference to the custom properties map (if one exists). 1796 pub fn custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties { 1797 &self.custom_properties 1798 } 1799 1800 /// Returns whether we have the same custom properties as another style. 1801 pub fn custom_properties_equal(&self, other: &Self) -> bool { 1802 self.custom_properties() == other.custom_properties() 1803 } 1804 1805 % for prop in data.longhands: 1806 % if not prop.logical: 1807 /// Gets the computed value of a given property. 1808 #[inline(always)] 1809 #[allow(non_snake_case)] 1810 pub fn clone_${prop.ident}( 1811 &self, 1812 ) -> longhands::${prop.ident}::computed_value::T { 1813 self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}() 1814 } 1815 % endif 1816 % endfor 1817 1818 /// Writes the (resolved or computed) value of the given longhand as a string in `dest`. 1819 /// 1820 /// TODO(emilio): We should move all the special resolution from 1821 /// nsComputedDOMStyle to ToResolvedValue instead. 1822 pub fn computed_or_resolved_value( 1823 &self, 1824 property_id: LonghandId, 1825 context: Option<&mut resolved::Context>, 1826 dest: &mut CssStringWriter, 1827 ) -> fmt::Result { 1828 use crate::values::resolved::ToResolvedValue; 1829 let mut dest = CssWriter::new(dest); 1830 let property_id = property_id.to_physical(self.writing_mode); 1831 match property_id { 1832 % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()): 1833 <% props = list(props) %> 1834 ${" |\n".join("LonghandId::{}".format(p.camel_case) for p in props)} => { 1835 let value = match property_id { 1836 % for prop in props: 1837 % if not prop.logical: 1838 LonghandId::${prop.camel_case} => self.clone_${prop.ident}(), 1839 % endif 1840 % endfor 1841 _ => unsafe { debug_unreachable!() }, 1842 }; 1843 if let Some(c) = context { 1844 c.current_longhand = Some(property_id); 1845 value.to_resolved_value(c).to_css(&mut dest) 1846 } else { 1847 value.to_css(&mut dest) 1848 } 1849 } 1850 % endfor 1851 } 1852 } 1853 1854 /// Returns the computed value of the given longhand as a strongly-typed 1855 /// `TypedValue`, if supported. 1856 pub fn computed_typed_value( 1857 &self, 1858 property_id: LonghandId, 1859 ) -> Option<TypedValue> { 1860 let property_id = property_id.to_physical(self.writing_mode); 1861 match property_id { 1862 % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()): 1863 <% props = list(props) %> 1864 ${" |\n".join("LonghandId::{}".format(p.camel_case) for p in props)} => { 1865 let value = match property_id { 1866 % for prop in props: 1867 % if not prop.logical: 1868 LonghandId::${prop.camel_case} => self.clone_${prop.ident}(), 1869 % endif 1870 % endfor 1871 _ => unsafe { debug_unreachable!() }, 1872 }; 1873 value.to_typed() 1874 } 1875 % endfor 1876 } 1877 } 1878 1879 /// Returns the given longhand's resolved value as a property declaration. 1880 pub fn computed_or_resolved_declaration( 1881 &self, 1882 property_id: LonghandId, 1883 context: Option<&mut resolved::Context>, 1884 ) -> PropertyDeclaration { 1885 use crate::values::resolved::ToResolvedValue; 1886 use crate::values::computed::ToComputedValue; 1887 let physical_property_id = property_id.to_physical(self.writing_mode); 1888 match physical_property_id { 1889 % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()): 1890 <% props = list(props) %> 1891 ${" |\n".join("LonghandId::{}".format(p.camel_case) for p in props)} => { 1892 let mut computed_value = match physical_property_id { 1893 % for prop in props: 1894 % if not prop.logical: 1895 LonghandId::${prop.camel_case} => self.clone_${prop.ident}(), 1896 % endif 1897 % endfor 1898 _ => unsafe { debug_unreachable!() }, 1899 }; 1900 if let Some(c) = context { 1901 c.current_longhand = Some(physical_property_id); 1902 let resolved = computed_value.to_resolved_value(c); 1903 computed_value = ToResolvedValue::from_resolved_value(resolved); 1904 } 1905 let specified = ToComputedValue::from_computed_value(&computed_value); 1906 % if props[0].boxed: 1907 let specified = Box::new(specified); 1908 % endif 1909 % if len(props) == 1: 1910 PropertyDeclaration::${props[0].camel_case}(specified) 1911 % else: 1912 unsafe { 1913 let mut out = mem::MaybeUninit::uninit(); 1914 ptr::write( 1915 out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${specified_type}>, 1916 PropertyDeclarationVariantRepr { 1917 tag: property_id as u16, 1918 value: specified, 1919 }, 1920 ); 1921 out.assume_init() 1922 } 1923 % endif 1924 } 1925 % endfor 1926 } 1927 } 1928 1929 /// Resolves the currentColor keyword. 1930 /// 1931 /// Any color value from computed values (except for the 'color' property 1932 /// itself) should go through this method. 1933 /// 1934 /// Usage example: 1935 /// let top_color = 1936 /// style.resolve_color(&style.get_border().clone_border_top_color()); 1937 #[inline] 1938 pub fn resolve_color(&self, color: &computed::Color) -> crate::color::AbsoluteColor { 1939 let current_color = self.get_inherited_text().clone_color(); 1940 color.resolve_to_absolute(¤t_color) 1941 } 1942 1943 /// Returns which longhand properties have different values in the two 1944 /// ComputedValues. 1945 #[cfg(feature = "gecko_debug")] 1946 pub fn differing_properties(&self, other: &ComputedValues) -> LonghandIdSet { 1947 let mut set = LonghandIdSet::new(); 1948 % for prop in data.longhands: 1949 % if not prop.logical: 1950 if self.clone_${prop.ident}() != other.clone_${prop.ident}() { 1951 set.insert(LonghandId::${prop.camel_case}); 1952 } 1953 % endif 1954 % endfor 1955 set 1956 } 1957 1958 /// Create a `TransitionPropertyIterator` for this styles transition properties. 1959 pub fn transition_properties<'a>( 1960 &'a self 1961 ) -> animated_properties::TransitionPropertyIterator<'a> { 1962 animated_properties::TransitionPropertyIterator::from_style(self) 1963 } 1964 } 1965 1966 #[cfg(feature = "servo")] 1967 impl ComputedValues { 1968 /// Create a new refcounted `ComputedValues` 1969 pub fn new( 1970 pseudo: Option<&PseudoElement>, 1971 custom_properties: crate::custom_properties::ComputedCustomProperties, 1972 writing_mode: WritingMode, 1973 effective_zoom: computed::Zoom, 1974 flags: ComputedValueFlags, 1975 rules: Option<StrongRuleNode>, 1976 visited_style: Option<Arc<ComputedValues>>, 1977 % for style_struct in data.active_style_structs(): 1978 ${style_struct.ident}: Arc<style_structs::${style_struct.name}>, 1979 % endfor 1980 ) -> Arc<Self> { 1981 Arc::new(Self { 1982 inner: ComputedValuesInner { 1983 custom_properties, 1984 writing_mode, 1985 rules, 1986 visited_style, 1987 effective_zoom, 1988 flags, 1989 % for style_struct in data.active_style_structs(): 1990 ${style_struct.ident}, 1991 % endfor 1992 }, 1993 pseudo: pseudo.cloned(), 1994 }) 1995 } 1996 1997 /// Get the initial computed values. 1998 pub fn initial_values_with_font_override(default_font: super::style_structs::Font) -> Arc<Self> { 1999 use crate::computed_value_flags::ComputedValueFlags; 2000 use servo_arc::Arc; 2001 use super::{ComputedValues, ComputedValuesInner, longhands, style_structs}; 2002 2003 Arc::new(ComputedValues { 2004 inner: ComputedValuesInner { 2005 % for style_struct in data.active_style_structs(): 2006 % if style_struct.name == "Font": 2007 font: Arc::new(default_font), 2008 <% continue %> 2009 % endif % 2010 2011 ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} { 2012 % for longhand in style_struct.longhands: 2013 % if not longhand.logical: 2014 ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(), 2015 % endif 2016 % endfor 2017 % if style_struct.name == "Box": 2018 original_display: longhands::display::get_initial_value(), 2019 % endif 2020 }), 2021 % endfor 2022 custom_properties: crate::custom_properties::ComputedCustomProperties::default(), 2023 writing_mode: WritingMode::empty(), 2024 rules: None, 2025 visited_style: None, 2026 effective_zoom: crate::values::computed::Zoom::ONE, 2027 flags: ComputedValueFlags::empty(), 2028 }, 2029 pseudo: None, 2030 }) 2031 } 2032 2033 /// Converts the computed values to an Arc<> from a reference. 2034 pub fn to_arc(&self) -> Arc<Self> { 2035 // SAFETY: We're guaranteed to be allocated as an Arc<> since the 2036 // functions above are the only ones that create ComputedValues 2037 // instances in Servo (and that must be the case since ComputedValues' 2038 // member is private). 2039 unsafe { Arc::from_raw_addrefed(self) } 2040 } 2041 2042 /// Serializes the computed value of this property as a string. 2043 pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String { 2044 match property { 2045 PropertyDeclarationId::Longhand(id) => { 2046 let context = resolved::Context { 2047 style: self, 2048 for_property: id.into(), 2049 current_longhand: Some(id), 2050 }; 2051 let mut s = String::new(); 2052 self.computed_or_resolved_value( 2053 id, 2054 Some(&context), 2055 &mut s 2056 ).unwrap(); 2057 s 2058 } 2059 PropertyDeclarationId::Custom(name) => { 2060 // FIXME(bug 1869476): This should use a stylist to determine 2061 // whether the name corresponds to an inherited custom property 2062 // and then choose the inherited/non_inherited map accordingly. 2063 let p = &self.custom_properties; 2064 let value = p 2065 .inherited 2066 .get(name) 2067 .or_else(|| p.non_inherited.get(name)); 2068 value.map_or(String::new(), |value| value.to_css_string()) 2069 } 2070 } 2071 } 2072 } 2073 2074 #[cfg(feature = "servo")] 2075 impl ops::Deref for ComputedValues { 2076 type Target = ComputedValuesInner; 2077 fn deref(&self) -> &ComputedValuesInner { 2078 &self.inner 2079 } 2080 } 2081 2082 #[cfg(feature = "servo")] 2083 impl ops::DerefMut for ComputedValues { 2084 fn deref_mut(&mut self) -> &mut ComputedValuesInner { 2085 &mut self.inner 2086 } 2087 } 2088 2089 #[cfg(feature = "servo")] 2090 impl ComputedValuesInner { 2091 /// Returns the visited style, if any. 2092 pub fn visited_style(&self) -> Option<&ComputedValues> { 2093 self.visited_style.as_deref() 2094 } 2095 2096 % for style_struct in data.active_style_structs(): 2097 /// Clone the ${style_struct.name} struct. 2098 #[inline] 2099 pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> { 2100 self.${style_struct.ident}.clone() 2101 } 2102 2103 /// Get a immutable reference to the ${style_struct.name} struct. 2104 #[inline] 2105 pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} { 2106 &self.${style_struct.ident} 2107 } 2108 2109 /// Get a mutable reference to the ${style_struct.name} struct. 2110 #[inline] 2111 pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} { 2112 Arc::make_mut(&mut self.${style_struct.ident}) 2113 } 2114 % endfor 2115 2116 /// Gets a reference to the rule node. Panic if no rule node exists. 2117 pub fn rules(&self) -> &StrongRuleNode { 2118 self.rules.as_ref().unwrap() 2119 } 2120 2121 #[inline] 2122 /// Returns whether the "content" property for the given style is completely 2123 /// ineffective, and would yield an empty `::before` or `::after` 2124 /// pseudo-element. 2125 pub fn ineffective_content_property(&self) -> bool { 2126 use crate::values::generics::counters::Content; 2127 match self.get_counters().content { 2128 Content::Normal | Content::None => true, 2129 Content::Items(ref items) => items.items.is_empty() 2130 } 2131 } 2132 2133 /// Whether the current style or any of its ancestors is multicolumn. 2134 #[inline] 2135 pub fn can_be_fragmented(&self) -> bool { 2136 self.flags.contains(ComputedValueFlags::CAN_BE_FRAGMENTED) 2137 } 2138 2139 /// Whether the current style is multicolumn. 2140 #[inline] 2141 pub fn is_multicol(&self) -> bool { 2142 self.get_column().is_multicol() 2143 } 2144 2145 /// Get the logical computed inline size. 2146 #[inline] 2147 pub fn content_inline_size(&self) -> &computed::Size { 2148 let position_style = self.get_position(); 2149 if self.writing_mode.is_vertical() { 2150 &position_style.height 2151 } else { 2152 &position_style.width 2153 } 2154 } 2155 2156 /// Get the logical computed block size. 2157 #[inline] 2158 pub fn content_block_size(&self) -> &computed::Size { 2159 let position_style = self.get_position(); 2160 if self.writing_mode.is_vertical() { &position_style.width } else { &position_style.height } 2161 } 2162 2163 /// Get the logical computed min inline size. 2164 #[inline] 2165 pub fn min_inline_size(&self) -> &computed::Size { 2166 let position_style = self.get_position(); 2167 if self.writing_mode.is_vertical() { &position_style.min_height } else { &position_style.min_width } 2168 } 2169 2170 /// Get the logical computed min block size. 2171 #[inline] 2172 pub fn min_block_size(&self) -> &computed::Size { 2173 let position_style = self.get_position(); 2174 if self.writing_mode.is_vertical() { &position_style.min_width } else { &position_style.min_height } 2175 } 2176 2177 /// Get the logical computed max inline size. 2178 #[inline] 2179 pub fn max_inline_size(&self) -> &computed::MaxSize { 2180 let position_style = self.get_position(); 2181 if self.writing_mode.is_vertical() { &position_style.max_height } else { &position_style.max_width } 2182 } 2183 2184 /// Get the logical computed max block size. 2185 #[inline] 2186 pub fn max_block_size(&self) -> &computed::MaxSize { 2187 let position_style = self.get_position(); 2188 if self.writing_mode.is_vertical() { &position_style.max_width } else { &position_style.max_height } 2189 } 2190 2191 /// Get the logical computed padding for this writing mode. 2192 #[inline] 2193 pub fn logical_padding(&self) -> LogicalMargin<&computed::LengthPercentage> { 2194 let padding_style = self.get_padding(); 2195 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new( 2196 &padding_style.padding_top.0, 2197 &padding_style.padding_right.0, 2198 &padding_style.padding_bottom.0, 2199 &padding_style.padding_left.0, 2200 )) 2201 } 2202 2203 /// Get the logical border width 2204 #[inline] 2205 pub fn border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au> { 2206 let border_style = self.get_border(); 2207 LogicalMargin::from_physical(writing_mode, SideOffsets2D::new( 2208 Au::from(border_style.border_top_width), 2209 Au::from(border_style.border_right_width), 2210 Au::from(border_style.border_bottom_width), 2211 Au::from(border_style.border_left_width), 2212 )) 2213 } 2214 2215 /// Gets the logical computed border widths for this style. 2216 #[inline] 2217 pub fn logical_border_width(&self) -> LogicalMargin<Au> { 2218 self.border_width_for_writing_mode(self.writing_mode) 2219 } 2220 2221 /// Gets the logical computed margin from this style. 2222 #[inline] 2223 pub fn logical_margin(&self) -> LogicalMargin<&computed::Margin> { 2224 let margin_style = self.get_margin(); 2225 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new( 2226 &margin_style.margin_top, 2227 &margin_style.margin_right, 2228 &margin_style.margin_bottom, 2229 &margin_style.margin_left, 2230 )) 2231 } 2232 2233 /// Gets the logical position from this style. 2234 #[inline] 2235 pub fn logical_position(&self) -> LogicalMargin<&computed::Inset> { 2236 // FIXME(SimonSapin): should be the writing mode of the containing block, maybe? 2237 let position_style = self.get_position(); 2238 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new( 2239 &position_style.top, 2240 &position_style.right, 2241 &position_style.bottom, 2242 &position_style.left, 2243 )) 2244 } 2245 2246 /// Return true if the effects force the transform style to be Flat 2247 pub fn overrides_transform_style(&self) -> bool { 2248 use crate::computed_values::mix_blend_mode::T as MixBlendMode; 2249 2250 let effects = self.get_effects(); 2251 // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported. 2252 effects.opacity < 1.0 || 2253 !effects.filter.0.is_empty() || 2254 !effects.clip.is_auto() || 2255 effects.mix_blend_mode != MixBlendMode::Normal 2256 } 2257 2258 /// <https://drafts.csswg.org/css-transforms/#grouping-property-values> 2259 pub fn get_used_transform_style(&self) -> computed_values::transform_style::T { 2260 use crate::computed_values::transform_style::T as TransformStyle; 2261 2262 let box_ = self.get_box(); 2263 2264 if self.overrides_transform_style() { 2265 TransformStyle::Flat 2266 } else { 2267 // Return the computed value if not overridden by the above exceptions 2268 box_.transform_style 2269 } 2270 } 2271 } 2272 2273 /// A reference to a style struct of the parent, or our own style struct. 2274 pub enum StyleStructRef<'a, T: 'static> { 2275 /// A borrowed struct from the parent, for example, for inheriting style. 2276 Borrowed(&'a T), 2277 /// An owned struct, that we've already mutated. 2278 Owned(UniqueArc<T>), 2279 /// Temporarily vacated, will panic if accessed 2280 Vacated, 2281 } 2282 2283 impl<'a, T: 'a> StyleStructRef<'a, T> 2284 where 2285 T: Clone, 2286 { 2287 /// Ensure a mutable reference of this value exists, either cloning the 2288 /// borrowed value, or returning the owned one. 2289 pub fn mutate(&mut self) -> &mut T { 2290 if let StyleStructRef::Borrowed(v) = *self { 2291 *self = StyleStructRef::Owned(UniqueArc::new(v.clone())); 2292 } 2293 2294 match *self { 2295 StyleStructRef::Owned(ref mut v) => v, 2296 StyleStructRef::Borrowed(..) => unreachable!(), 2297 StyleStructRef::Vacated => panic!("Accessed vacated style struct") 2298 } 2299 } 2300 2301 /// Whether this is pointer-equal to the struct we're going to copy the 2302 /// value from. 2303 /// 2304 /// This is used to avoid allocations when people write stuff like `font: 2305 /// inherit` or such `all: initial`. 2306 #[inline] 2307 pub fn ptr_eq(&self, struct_to_copy_from: &T) -> bool { 2308 match *self { 2309 StyleStructRef::Owned(..) => false, 2310 StyleStructRef::Borrowed(s) => { 2311 s as *const T == struct_to_copy_from as *const T 2312 } 2313 StyleStructRef::Vacated => panic!("Accessed vacated style struct") 2314 } 2315 } 2316 2317 /// Extract a unique Arc from this struct, vacating it. 2318 /// 2319 /// The vacated state is a transient one, please put the Arc back 2320 /// when done via `put()`. This function is to be used to separate 2321 /// the struct being mutated from the computed context 2322 pub fn take(&mut self) -> UniqueArc<T> { 2323 use std::mem::replace; 2324 let inner = replace(self, StyleStructRef::Vacated); 2325 2326 match inner { 2327 StyleStructRef::Owned(arc) => arc, 2328 StyleStructRef::Borrowed(s) => UniqueArc::new(s.clone()), 2329 StyleStructRef::Vacated => panic!("Accessed vacated style struct"), 2330 } 2331 } 2332 2333 /// Replace vacated ref with an arc 2334 pub fn put(&mut self, arc: UniqueArc<T>) { 2335 debug_assert!(matches!(*self, StyleStructRef::Vacated)); 2336 *self = StyleStructRef::Owned(arc); 2337 } 2338 2339 /// Get a mutable reference to the owned struct, or `None` if the struct 2340 /// hasn't been mutated. 2341 pub fn get_if_mutated(&mut self) -> Option<&mut T> { 2342 match *self { 2343 StyleStructRef::Owned(ref mut v) => Some(v), 2344 StyleStructRef::Borrowed(..) => None, 2345 StyleStructRef::Vacated => panic!("Accessed vacated style struct") 2346 } 2347 } 2348 2349 /// Returns an `Arc` to the internal struct, constructing one if 2350 /// appropriate. 2351 pub fn build(self) -> Arc<T> { 2352 match self { 2353 StyleStructRef::Owned(v) => v.shareable(), 2354 // SAFETY: We know all style structs are arc-allocated. 2355 StyleStructRef::Borrowed(v) => unsafe { Arc::from_raw_addrefed(v) }, 2356 StyleStructRef::Vacated => panic!("Accessed vacated style struct") 2357 } 2358 } 2359 } 2360 2361 impl<'a, T: 'a> ops::Deref for StyleStructRef<'a, T> { 2362 type Target = T; 2363 2364 fn deref(&self) -> &T { 2365 match *self { 2366 StyleStructRef::Owned(ref v) => &**v, 2367 StyleStructRef::Borrowed(v) => v, 2368 StyleStructRef::Vacated => panic!("Accessed vacated style struct") 2369 } 2370 } 2371 } 2372 2373 /// A type used to compute a struct with minimal overhead. 2374 /// 2375 /// This allows holding references to the parent/default computed values without 2376 /// actually cloning them, until we either build the style, or mutate the 2377 /// inherited value. 2378 pub struct StyleBuilder<'a> { 2379 /// The device we're using to compute style. 2380 /// 2381 /// This provides access to viewport unit ratios, etc. 2382 pub device: &'a Device, 2383 2384 /// The stylist we're using to compute style except for media queries. 2385 /// device is used in media queries instead. 2386 pub stylist: Option<&'a Stylist>, 2387 2388 /// The style we're inheriting from. 2389 /// 2390 /// This is effectively 2391 /// `parent_style.unwrap_or(device.default_computed_values())`. 2392 inherited_style: &'a ComputedValues, 2393 2394 /// The style we're getting reset structs from. 2395 reset_style: &'a ComputedValues, 2396 2397 /// The rule node representing the ordered list of rules matched for this 2398 /// node. 2399 pub rules: Option<StrongRuleNode>, 2400 2401 /// The computed custom properties. 2402 pub custom_properties: crate::custom_properties::ComputedCustomProperties, 2403 2404 /// Non-custom properties that are considered invalid at compute time 2405 /// due to cyclic dependencies with custom properties. 2406 /// e.g. `--foo: 1em; font-size: var(--foo)` where `--foo` is registered. 2407 pub invalid_non_custom_properties: LonghandIdSet, 2408 2409 /// The pseudo-element this style will represent. 2410 pub pseudo: Option<&'a PseudoElement>, 2411 2412 /// Whether we have mutated any reset structs since the the last time 2413 /// `clear_modified_reset` was called. This is used to tell whether the 2414 /// `StyleAdjuster` did any work. 2415 modified_reset: bool, 2416 2417 /// Whether this is the style for the root element. 2418 pub is_root_element: bool, 2419 2420 /// The writing mode flags. 2421 /// 2422 /// TODO(emilio): Make private. 2423 pub writing_mode: WritingMode, 2424 2425 /// The color-scheme bits. Needed because they may otherwise be different between visited and 2426 /// unvisited colors. 2427 pub color_scheme: ColorSchemeFlags, 2428 2429 /// The effective zoom. 2430 pub effective_zoom: computed::Zoom, 2431 2432 /// The effective zoom for inheritance (the "specified" zoom on this element). 2433 pub effective_zoom_for_inheritance: computed::Zoom, 2434 2435 /// Flags for the computed value. 2436 pub flags: Cell<ComputedValueFlags>, 2437 2438 /// The element's style if visited, only computed if there's a relevant link 2439 /// for this element. A element's "relevant link" is the element being 2440 /// matched if it is a link or the nearest ancestor link. 2441 pub visited_style: Option<Arc<ComputedValues>>, 2442 % for style_struct in data.active_style_structs(): 2443 ${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>, 2444 % endfor 2445 } 2446 2447 impl<'a> StyleBuilder<'a> { 2448 /// Trivially construct a `StyleBuilder`. 2449 pub fn new( 2450 device: &'a Device, 2451 stylist: Option<&'a Stylist>, 2452 parent_style: Option<&'a ComputedValues>, 2453 pseudo: Option<&'a PseudoElement>, 2454 rules: Option<StrongRuleNode>, 2455 is_root_element: bool, 2456 ) -> Self { 2457 let reset_style = device.default_computed_values(); 2458 let inherited_style = parent_style.unwrap_or(reset_style); 2459 2460 let flags = inherited_style.flags.inherited(); 2461 Self { 2462 device, 2463 stylist, 2464 inherited_style, 2465 reset_style, 2466 pseudo, 2467 rules, 2468 modified_reset: false, 2469 is_root_element, 2470 custom_properties: crate::custom_properties::ComputedCustomProperties::default(), 2471 invalid_non_custom_properties: LonghandIdSet::default(), 2472 writing_mode: inherited_style.writing_mode, 2473 effective_zoom: inherited_style.effective_zoom, 2474 effective_zoom_for_inheritance: computed::Zoom::ONE, 2475 color_scheme: inherited_style.get_inherited_ui().color_scheme_bits(), 2476 flags: Cell::new(flags), 2477 visited_style: None, 2478 % for style_struct in data.active_style_structs(): 2479 % if style_struct.inherited: 2480 ${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.get_${style_struct.name_lower}()), 2481 % else: 2482 ${style_struct.ident}: StyleStructRef::Borrowed(reset_style.get_${style_struct.name_lower}()), 2483 % endif 2484 % endfor 2485 } 2486 } 2487 2488 /// NOTE(emilio): This is done so we can compute relative units with respect 2489 /// to the parent style, but all the early properties / writing-mode / etc 2490 /// are already set to the right ones on the kid. 2491 /// 2492 /// Do _not_ actually call this to construct a style, this should mostly be 2493 /// used for animations. 2494 pub fn for_derived_style( 2495 device: &'a Device, 2496 stylist: Option<&'a Stylist>, 2497 style_to_derive_from: &'a ComputedValues, 2498 parent_style: Option<&'a ComputedValues>, 2499 ) -> Self { 2500 let reset_style = device.default_computed_values(); 2501 let inherited_style = parent_style.unwrap_or(reset_style); 2502 Self { 2503 device, 2504 stylist, 2505 inherited_style, 2506 reset_style, 2507 pseudo: None, 2508 modified_reset: false, 2509 is_root_element: false, 2510 rules: None, 2511 custom_properties: style_to_derive_from.custom_properties().clone(), 2512 invalid_non_custom_properties: LonghandIdSet::default(), 2513 writing_mode: style_to_derive_from.writing_mode, 2514 effective_zoom: style_to_derive_from.effective_zoom, 2515 effective_zoom_for_inheritance: Self::zoom_for_inheritance(style_to_derive_from.get_box().clone_zoom(), inherited_style), 2516 color_scheme: style_to_derive_from.get_inherited_ui().color_scheme_bits(), 2517 flags: Cell::new(style_to_derive_from.flags), 2518 visited_style: None, 2519 % for style_struct in data.active_style_structs(): 2520 ${style_struct.ident}: StyleStructRef::Borrowed( 2521 style_to_derive_from.get_${style_struct.name_lower}() 2522 ), 2523 % endfor 2524 } 2525 } 2526 2527 /// Copy the reset properties from `style`. 2528 pub fn copy_reset_from(&mut self, style: &'a ComputedValues) { 2529 % for style_struct in data.active_style_structs(): 2530 % if not style_struct.inherited: 2531 self.${style_struct.ident} = 2532 StyleStructRef::Borrowed(style.get_${style_struct.name_lower}()); 2533 % endif 2534 % endfor 2535 } 2536 2537 % for property in data.longhands: 2538 % if not property.logical: 2539 % if not property.style_struct.inherited: 2540 /// Inherit `${property.ident}` from our parent style. 2541 #[allow(non_snake_case)] 2542 pub fn inherit_${property.ident}(&mut self) { 2543 let inherited_struct = 2544 self.inherited_style.get_${property.style_struct.name_lower}(); 2545 2546 self.modified_reset = true; 2547 self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE); 2548 2549 % if property.ident == "content": 2550 self.add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE); 2551 % endif 2552 2553 % if property.ident == "display": 2554 self.add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE); 2555 % endif 2556 2557 if self.${property.style_struct.ident}.ptr_eq(inherited_struct) { 2558 return; 2559 } 2560 2561 self.${property.style_struct.ident}.mutate() 2562 .copy_${property.ident}_from(inherited_struct); 2563 } 2564 % else: 2565 /// Reset `${property.ident}` to the initial value. 2566 #[allow(non_snake_case)] 2567 pub fn reset_${property.ident}(&mut self) { 2568 let reset_struct = 2569 self.reset_style.get_${property.style_struct.name_lower}(); 2570 2571 if self.${property.style_struct.ident}.ptr_eq(reset_struct) { 2572 return; 2573 } 2574 2575 self.${property.style_struct.ident}.mutate() 2576 .reset_${property.ident}(reset_struct); 2577 } 2578 % endif 2579 2580 % if not property.is_vector or property.simple_vector_bindings or engine == "servo": 2581 /// Set the `${property.ident}` to the computed value `value`. 2582 #[allow(non_snake_case)] 2583 pub fn set_${property.ident}( 2584 &mut self, 2585 value: longhands::${property.ident}::computed_value::T 2586 ) { 2587 % if not property.style_struct.inherited: 2588 self.modified_reset = true; 2589 % endif 2590 2591 self.${property.style_struct.ident}.mutate() 2592 .set_${property.ident}( 2593 value, 2594 % if property.logical: 2595 self.writing_mode, 2596 % endif 2597 ); 2598 } 2599 % endif 2600 % endif 2601 % endfor 2602 <% del property %> 2603 2604 /// Inherits style from the parent element, accounting for the default 2605 /// computed values that need to be provided as well. 2606 pub fn for_inheritance( 2607 device: &'a Device, 2608 stylist: Option<&'a Stylist>, 2609 parent: Option<&'a ComputedValues>, 2610 pseudo: Option<&'a PseudoElement>, 2611 ) -> Self { 2612 // Rebuild the visited style from the parent, ensuring that it will also 2613 // not have rules. This matches the unvisited style that will be 2614 // produced by this builder. This assumes that the caller doesn't need 2615 // to adjust or process visited style, so we can just build visited 2616 // style here for simplicity. 2617 let visited_style = parent.and_then(|parent| { 2618 parent.visited_style().map(|style| { 2619 Self::for_inheritance( 2620 device, 2621 stylist, 2622 Some(style), 2623 pseudo, 2624 ).build() 2625 }) 2626 }); 2627 let custom_properties = if let Some(p) = parent { p.custom_properties().clone() } else { crate::custom_properties::ComputedCustomProperties::default() }; 2628 let mut ret = Self::new( 2629 device, 2630 stylist, 2631 parent, 2632 pseudo, 2633 /* rules = */ None, 2634 /* is_root_element = */ false, 2635 ); 2636 ret.custom_properties = custom_properties; 2637 ret.visited_style = visited_style; 2638 ret 2639 } 2640 2641 /// Returns whether we have a visited style. 2642 pub fn has_visited_style(&self) -> bool { 2643 self.visited_style.is_some() 2644 } 2645 2646 /// Returns whether we're a pseudo-elements style. 2647 pub fn is_pseudo_element(&self) -> bool { 2648 self.pseudo.map_or(false, |p| !p.is_anon_box()) 2649 } 2650 2651 /// Returns the style we're getting reset properties from. 2652 pub fn default_style(&self) -> &'a ComputedValues { 2653 self.reset_style 2654 } 2655 2656 % for style_struct in data.active_style_structs(): 2657 /// Gets an immutable view of the current `${style_struct.name}` style. 2658 pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} { 2659 &self.${style_struct.ident} 2660 } 2661 2662 /// Gets a mutable view of the current `${style_struct.name}` style. 2663 pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} { 2664 % if not style_struct.inherited: 2665 self.modified_reset = true; 2666 % endif 2667 self.${style_struct.ident}.mutate() 2668 } 2669 2670 /// Gets a mutable view of the current `${style_struct.name}` style. 2671 pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc<style_structs::${style_struct.name}> { 2672 % if not style_struct.inherited: 2673 self.modified_reset = true; 2674 % endif 2675 self.${style_struct.ident}.take() 2676 } 2677 2678 /// Gets a mutable view of the current `${style_struct.name}` style. 2679 pub fn put_${style_struct.name_lower}(&mut self, s: UniqueArc<style_structs::${style_struct.name}>) { 2680 self.${style_struct.ident}.put(s) 2681 } 2682 2683 /// Gets a mutable view of the current `${style_struct.name}` style, 2684 /// only if it's been mutated before. 2685 pub fn get_${style_struct.name_lower}_if_mutated(&mut self) 2686 -> Option<&mut style_structs::${style_struct.name}> { 2687 self.${style_struct.ident}.get_if_mutated() 2688 } 2689 2690 /// Reset the current `${style_struct.name}` style to its default value. 2691 pub fn reset_${style_struct.name_lower}_struct(&mut self) { 2692 self.${style_struct.ident} = 2693 StyleStructRef::Borrowed(self.reset_style.get_${style_struct.name_lower}()); 2694 } 2695 % endfor 2696 <% del style_struct %> 2697 2698 /// Returns whether this computed style represents a floated object. 2699 pub fn is_floating(&self) -> bool { 2700 self.get_box().clone_float().is_floating() 2701 } 2702 2703 /// Returns whether this computed style represents an absolutely-positioned 2704 /// object. 2705 pub fn is_absolutely_positioned(&self) -> bool { 2706 self.get_box().clone_position().is_absolutely_positioned() 2707 } 2708 2709 /// Whether this style has a top-layer style. 2710 #[cfg(feature = "servo")] 2711 pub fn in_top_layer(&self) -> bool { 2712 matches!(self.get_box().clone__servo_top_layer(), 2713 longhands::_servo_top_layer::computed_value::T::Top) 2714 } 2715 2716 /// Whether this style has a top-layer style. 2717 #[cfg(feature = "gecko")] 2718 pub fn in_top_layer(&self) -> bool { 2719 matches!(self.get_box().clone__moz_top_layer(), 2720 longhands::_moz_top_layer::computed_value::T::Auto) 2721 } 2722 2723 /// Clears the "have any reset structs been modified" flag. 2724 pub fn clear_modified_reset(&mut self) { 2725 self.modified_reset = false; 2726 } 2727 2728 /// Returns whether we have mutated any reset structs since the the last 2729 /// time `clear_modified_reset` was called. 2730 pub fn modified_reset(&self) -> bool { 2731 self.modified_reset 2732 } 2733 2734 /// Return the current flags. 2735 #[inline] 2736 pub fn flags(&self) -> ComputedValueFlags { 2737 self.flags.get() 2738 } 2739 2740 /// Add a flag to the current builder. 2741 #[inline] 2742 pub fn add_flags(&self, flag: ComputedValueFlags) { 2743 let flags = self.flags() | flag; 2744 self.flags.set(flags); 2745 } 2746 2747 /// Removes a flag to the current builder. 2748 #[inline] 2749 pub fn remove_flags(&self, flag: ComputedValueFlags) { 2750 let flags = self.flags() & !flag; 2751 self.flags.set(flags); 2752 } 2753 2754 /// Turns this `StyleBuilder` into a proper `ComputedValues` instance. 2755 pub fn build(self) -> Arc<ComputedValues> { 2756 ComputedValues::new( 2757 self.pseudo, 2758 self.custom_properties, 2759 self.writing_mode, 2760 self.effective_zoom, 2761 self.flags.get(), 2762 self.rules, 2763 self.visited_style, 2764 % for style_struct in data.active_style_structs(): 2765 self.${style_struct.ident}.build(), 2766 % endfor 2767 ) 2768 } 2769 2770 /// Get the custom properties map if necessary. 2771 pub fn custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties { 2772 &self.custom_properties 2773 } 2774 2775 2776 /// Get the inherited custom properties map. 2777 pub fn inherited_custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties { 2778 &self.inherited_style.custom_properties 2779 } 2780 2781 /// Access to various information about our inherited styles. We don't 2782 /// expose an inherited ComputedValues directly, because in the 2783 /// ::first-line case some of the inherited information needs to come from 2784 /// one ComputedValues instance and some from a different one. 2785 2786 /// Inherited writing-mode. 2787 pub fn inherited_writing_mode(&self) -> &WritingMode { 2788 &self.inherited_style.writing_mode 2789 } 2790 2791 /// The effective zoom value that we should multiply absolute lengths by. 2792 pub fn effective_zoom(&self) -> computed::Zoom { 2793 self.effective_zoom 2794 } 2795 2796 /// The zoom specified on this element. 2797 pub fn specified_zoom(&self) -> computed::Zoom { 2798 self.get_box().clone_zoom() 2799 } 2800 2801 /// Computes effective_zoom and effective_zoom_for_inheritance based on the current style 2802 /// information. 2803 pub fn recompute_effective_zooms(&mut self) { 2804 let specified = self.specified_zoom(); 2805 self.effective_zoom = self.inherited_style.effective_zoom.compute_effective(specified); 2806 self.effective_zoom_for_inheritance = Self::zoom_for_inheritance(specified, self.inherited_style); 2807 } 2808 2809 fn zoom_for_inheritance(specified: computed::Zoom, inherited_style: &ComputedValues) -> computed::Zoom { 2810 if specified.is_document() { 2811 // If our inherited effective zoom has derived to zero, there's not much we can do. 2812 // This value is not exposed to content anyways (it's used for scrollbars and to avoid 2813 // zoom affecting canvas). 2814 inherited_style.effective_zoom.inverted().unwrap_or(computed::Zoom::ONE) 2815 } else { 2816 specified 2817 } 2818 } 2819 2820 /// The computed value flags of our parent. 2821 #[inline] 2822 pub fn get_parent_flags(&self) -> ComputedValueFlags { 2823 self.inherited_style.flags 2824 } 2825 2826 /// Calculate the line height, given the currently resolved line-height and font. 2827 pub fn calc_line_height( 2828 &self, 2829 device: &Device, 2830 line_height_base: LineHeightBase, 2831 writing_mode: WritingMode, 2832 ) -> computed::NonNegativeLength { 2833 use crate::computed_value_flags::ComputedValueFlags; 2834 let (font, flag) = match line_height_base { 2835 LineHeightBase::CurrentStyle => ( 2836 self.get_font(), 2837 ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS, 2838 ), 2839 LineHeightBase::InheritedStyle => ( 2840 self.get_parent_font(), 2841 ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS, 2842 ), 2843 }; 2844 let line_height = font.clone_line_height(); 2845 if matches!(line_height, computed::LineHeight::Normal) { 2846 self.add_flags(flag); 2847 } 2848 let lh = device.calc_line_height(&font, writing_mode, None); 2849 if line_height_base == LineHeightBase::InheritedStyle { 2850 // Apply our own zoom if our style source is the parent style. 2851 computed::NonNegativeLength::new(self.effective_zoom_for_inheritance.zoom(lh.px())) 2852 } else { 2853 lh 2854 } 2855 } 2856 2857 /// And access to inherited style structs. 2858 % for style_struct in data.active_style_structs(): 2859 /// Gets our inherited `${style_struct.name}`. We don't name these 2860 /// accessors `inherited_${style_struct.name_lower}` because we already 2861 /// have things like "box" vs "inherited_box" as struct names. Do the 2862 /// next-best thing and call them `parent_${style_struct.name_lower}` 2863 /// instead. 2864 pub fn get_parent_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} { 2865 self.inherited_style.get_${style_struct.name_lower}() 2866 } 2867 % endfor 2868 } 2869 2870 /// A per-longhand function that performs the CSS cascade for that longhand. 2871 pub type CascadePropertyFn = 2872 unsafe extern "Rust" fn( 2873 declaration: &PropertyDeclaration, 2874 context: &mut computed::Context, 2875 ); 2876 2877 /// A per-longhand array of functions to perform the CSS cascade on each of 2878 /// them, effectively doing virtual dispatch. 2879 pub static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [ 2880 % for property in data.longhands: 2881 longhands::${property.ident}::cascade_property, 2882 % endfor 2883 ]; 2884 2885 /// An identifier for a given alias property. 2886 #[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)] 2887 #[repr(u16)] 2888 pub enum AliasId { 2889 % for i, property in enumerate(data.all_aliases()): 2890 /// ${property.name} 2891 ${property.camel_case} = ${i}, 2892 % endfor 2893 } 2894 2895 impl fmt::Debug for AliasId { 2896 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 2897 let name = NonCustomPropertyId::from(*self).name(); 2898 formatter.write_str(name) 2899 } 2900 } 2901 2902 impl AliasId { 2903 /// Returns the property we're aliasing, as a longhand or a shorthand. 2904 #[inline] 2905 pub fn aliased_property(self) -> NonCustomPropertyId { 2906 static MAP: [NonCustomPropertyId; ${len(data.all_aliases())}] = [ 2907 % for alias in data.all_aliases(): 2908 % if alias.original.type() == "longhand": 2909 NonCustomPropertyId::from_longhand(LonghandId::${alias.original.camel_case}), 2910 % else: 2911 <% assert alias.original.type() == "shorthand" %> 2912 NonCustomPropertyId::from_shorthand(ShorthandId::${alias.original.camel_case}), 2913 % endif 2914 % endfor 2915 ]; 2916 MAP[self as usize] 2917 } 2918 } 2919 2920 /// Call the given macro with tokens like this for each longhand and shorthand properties 2921 /// that is enabled in content: 2922 /// 2923 /// ``` 2924 /// [CamelCaseName, SetCamelCaseName, PropertyId::Longhand(LonghandId::CamelCaseName)], 2925 /// ``` 2926 /// 2927 /// NOTE(emilio): Callers are responsible to deal with prefs. 2928 #[macro_export] 2929 macro_rules! css_properties_accessors { 2930 ($macro_name: ident) => { 2931 $macro_name! { 2932 % for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]: 2933 % for property in props: 2934 % if property.enabled_in_content(): 2935 % for prop in [property] + property.aliases: 2936 % if '-' in prop.name: 2937 [${prop.ident.capitalize()}, Set${prop.ident.capitalize()}, 2938 PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())], 2939 % endif 2940 [${prop.camel_case}, Set${prop.camel_case}, 2941 PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())], 2942 % endfor 2943 % endif 2944 % endfor 2945 % endfor 2946 } 2947 } 2948 } 2949 2950 /// Call the given macro with tokens like this for each longhand properties: 2951 /// 2952 /// ``` 2953 /// { snake_case_ident } 2954 /// ``` 2955 #[macro_export] 2956 macro_rules! longhand_properties_idents { 2957 ($macro_name: ident) => { 2958 $macro_name! { 2959 % for property in data.longhands: 2960 { ${property.ident} } 2961 % endfor 2962 } 2963 } 2964 } 2965 2966 // Large pages generate tens of thousands of ComputedValues. 2967 #[cfg(feature = "gecko")] 2968 size_of_test!(ComputedValues, 248); 2969 #[cfg(feature = "servo")] 2970 size_of_test!(ComputedValues, 216); 2971 2972 // FFI relies on this. 2973 size_of_test!(Option<Arc<ComputedValues>>, 8); 2974 2975 // There are two reasons for this test to fail: 2976 // 2977 // * Your changes made a specified value type for a given property go 2978 // over the threshold. In that case, you should try to shrink it again 2979 // or, if not possible, mark the property as boxed in the property 2980 // definition. 2981 // 2982 // * Your changes made a specified value type smaller, so that it no 2983 // longer needs to be boxed. In this case you just need to remove 2984 // boxed=True from the property definition. Nice job! 2985 #[cfg(target_pointer_width = "64")] 2986 #[allow(dead_code)] // https://github.com/rust-lang/rust/issues/96952 2987 const BOX_THRESHOLD: usize = 24; 2988 % for longhand in data.longhands: 2989 #[cfg(target_pointer_width = "64")] 2990 % if longhand.boxed: 2991 const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() > BOX_THRESHOLD); 2992 % else: 2993 const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() <= BOX_THRESHOLD); 2994 % endif 2995 % endfor 2996 2997 % if engine == "servo": 2998 % for effect_name in ["repaint", "recalculate_overflow", "rebuild_stacking_context", "rebuild_box"]: 2999 pub(crate) fn restyle_damage_${effect_name} (old: &ComputedValues, new: &ComputedValues) -> bool { 3000 % for style_struct in data.active_style_structs(): 3001 % for longhand in style_struct.longhands: 3002 % if effect_name in longhand.servo_restyle_damage.split() and not longhand.logical: 3003 old.get_${style_struct.name_lower}().${longhand.ident} != new.get_${style_struct.name_lower}().${longhand.ident} || 3004 % endif 3005 % endfor 3006 % endfor 3007 false 3008 } 3009 % endfor 3010 % endif