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 // Please note that valid Rust syntax may be mangled by the Mako parser.
8 // For example, Vec<&Foo> will be mangled as Vec&Foo>. To work around these issues, the code
9 // can be escaped. In the above example, Vec<<&Foo> or Vec< &Foo> achieves the desired result of Vec<&Foo>.
10
11 <%namespace name="helpers" file="/helpers.mako.rs" />
12
13 #[cfg(feature = "servo")]
14 use app_units::Au;
15 use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
16 use servo_arc::{Arc, UniqueArc};
17 use std::borrow::Cow;
18 use std::{ops, ptr};
19 use std::fmt::{self, Write};
20 use std::mem;
21
22 use cssparser::{Parser, RGBA, TokenSerializationType};
23 use cssparser::ParserInput;
24 #[cfg(feature = "servo")] use euclid::SideOffsets2D;
25 use crate::context::QuirksMode;
26 #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{self, nsCSSPropertyID};
27 #[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin;
28 #[cfg(feature = "servo")] use crate::computed_values;
29 use crate::logical_geometry::WritingMode;
30 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
31 use crate::computed_value_flags::*;
32 use crate::hash::FxHashMap;
33 use crate::media_queries::Device;
34 use crate::parser::ParserContext;
35 use crate::selector_parser::PseudoElement;
36 #[cfg(feature = "servo")] use servo_config::prefs;
37 use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
38 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
39 use to_shmem::impl_trivial_to_shmem;
40 use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
41 use crate::use_counters::UseCounters;
42 use crate::values::generics::text::LineHeight;
43 use crate::values::{computed, resolved};
44 use crate::values::computed::NonNegativeLength;
45 use crate::values::serialize_atom_name;
46 use crate::values::specified::font::SystemFont;
47 use crate::rule_tree::StrongRuleNode;
48 use crate::Zero;
49 use crate::str::{CssString, CssStringWriter};
50 use std::cell::Cell;
51
52 pub use self::declaration_block::*;
53 pub use self::cascade::*;
54
55 <%!
56 from collections import defaultdict
57 from data import Method, PropertyRestrictions, Keyword, to_rust_ident, \
58 to_camel_case, RULE_VALUES, SYSTEM_FONT_LONGHANDS
59 import os.path
60 %>
61
62 #[path="${repr(os.path.join(os.path.dirname(__file__), 'declaration_block.rs'))[1:-1]}"]
63 pub mod declaration_block;
64 #[path="${repr(os.path.join(os.path.dirname(__file__), 'cascade.rs'))[1:-1]}"]
65 pub mod cascade;
66
67 /// Conversion with fewer impls than From/Into
68 pub trait MaybeBoxed<Out> {
69 /// Convert
maybe_boxed(self) -> Out70 fn maybe_boxed(self) -> Out;
71 }
72
73 impl<T> MaybeBoxed<T> for T {
74 #[inline]
maybe_boxed(self) -> T75 fn maybe_boxed(self) -> T { self }
76 }
77
78 impl<T> MaybeBoxed<Box<T>> for T {
79 #[inline]
maybe_boxed(self) -> Box<T>80 fn maybe_boxed(self) -> Box<T> { Box::new(self) }
81 }
82
83 macro_rules! expanded {
84 ( $( $name: ident: $value: expr ),+ ) => {
85 expanded!( $( $name: $value, )+ )
86 };
87 ( $( $name: ident: $value: expr, )+ ) => {
88 Longhands {
89 $(
90 $name: MaybeBoxed::maybe_boxed($value),
91 )+
92 }
93 }
94 }
95
96 /// A module with all the code for longhand properties.
97 #[allow(missing_docs)]
98 pub mod longhands {
99 % for style_struct in data.style_structs:
100 include!("${repr(os.path.join(OUT_DIR, 'longhands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
101 % endfor
102 pub const ANIMATABLE_PROPERTY_COUNT: usize = ${sum(1 for prop in data.longhands if prop.animatable)};
103 }
104
105 macro_rules! unwrap_or_initial {
106 ($prop: ident) => (unwrap_or_initial!($prop, $prop));
107 ($prop: ident, $expr: expr) =>
108 ($expr.unwrap_or_else(|| $prop::get_initial_specified_value()));
109 }
110
111 /// A module with code for all the shorthand css properties, and a few
112 /// serialization helpers.
113 #[allow(missing_docs)]
114 pub mod shorthands {
115 use cssparser::Parser;
116 use crate::parser::{Parse, ParserContext};
117 use style_traits::{ParseError, StyleParseErrorKind};
118 use crate::values::specified;
119
120 use style_traits::{CssWriter, ToCss};
121 use crate::values::specified::{BorderStyle, Color};
122 use std::fmt::{self, Write};
123
serialize_directional_border<W, I,>( dest: &mut CssWriter<W>, width: &I, style: &BorderStyle, color: &Color, ) -> fmt::Result where W: Write, I: ToCss,124 fn serialize_directional_border<W, I,>(
125 dest: &mut CssWriter<W>,
126 width: &I,
127 style: &BorderStyle,
128 color: &Color,
129 ) -> fmt::Result
130 where
131 W: Write,
132 I: ToCss,
133 {
134 width.to_css(dest)?;
135 // FIXME(emilio): Should we really serialize the border style if it's
136 // `solid`?
137 dest.write_str(" ")?;
138 style.to_css(dest)?;
139 if *color != Color::CurrentColor {
140 dest.write_str(" ")?;
141 color.to_css(dest)?;
142 }
143 Ok(())
144 }
145
146 % for style_struct in data.style_structs:
147 include!("${repr(os.path.join(OUT_DIR, 'shorthands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
148 % endfor
149
150 // We didn't define the 'all' shorthand using the regular helpers:shorthand
151 // mechanism, since it causes some very large types to be generated.
152 //
153 // Also, make sure logical properties appear before its physical
154 // counter-parts, in order to prevent bugs like:
155 //
156 // https://bugzilla.mozilla.org/show_bug.cgi?id=1410028
157 //
158 // FIXME(emilio): Adopt the resolution from:
159 //
160 // https://github.com/w3c/csswg-drafts/issues/1898
161 //
162 // when there is one, whatever that is.
163 <%
164 logical_longhands = []
165 other_longhands = []
166
167 for p in data.longhands:
168 if p.name in ['direction', 'unicode-bidi']:
169 continue;
170 if not p.enabled_in_content() and not p.experimental(engine):
171 continue;
172 if p.logical:
173 logical_longhands.append(p.name)
174 else:
175 other_longhands.append(p.name)
176
177 data.declare_shorthand(
178 "all",
179 logical_longhands + other_longhands,
180 engines="gecko servo-2013 servo-2020",
181 spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
182 )
183 %>
184
185 /// The max amount of longhands that the `all` shorthand will ever contain.
186 pub const ALL_SHORTHAND_MAX_LEN: usize = ${len(logical_longhands + other_longhands)};
187 }
188
189 <%
190 from itertools import groupby
191
192 # After this code, `data.longhands` is sorted in the following order:
193 # - first all keyword variants and all variants known to be Copy,
194 # - second all the other variants, such as all variants with the same field
195 # have consecutive discriminants.
196 # The variable `variants` contain the same entries as `data.longhands` in
197 # the same order, but must exist separately to the data source, because
198 # we then need to add three additional variants `WideKeywordDeclaration`,
199 # `VariableDeclaration` and `CustomDeclaration`.
200
201 variants = []
202 for property in data.longhands:
203 variants.append({
204 "name": property.camel_case,
205 "type": property.specified_type(),
206 "doc": "`" + property.name + "`",
207 "copy": property.specified_is_copy(),
208 })
209
210 groups = {}
211 keyfunc = lambda x: x["type"]
212 sortkeys = {}
213 for ty, group in groupby(sorted(variants, key=keyfunc), keyfunc):
214 group = list(group)
215 groups[ty] = group
216 for v in group:
217 if len(group) == 1:
218 sortkeys[v["name"]] = (not v["copy"], 1, v["name"], "")
219 else:
220 sortkeys[v["name"]] = (not v["copy"], len(group), ty, v["name"])
221 variants.sort(key=lambda x: sortkeys[x["name"]])
222
223 # It is extremely important to sort the `data.longhands` array here so
224 # that it is in the same order as `variants`, for `LonghandId` and
225 # `PropertyDeclarationId` to coincide.
226 data.longhands.sort(key=lambda x: sortkeys[x.camel_case])
227 %>
228
229 // WARNING: It is *really* important for the variants of `LonghandId`
230 // and `PropertyDeclaration` to be defined in the exact same order,
231 // with the exception of `CSSWideKeyword`, `WithVariables` and `Custom`,
232 // which don't exist in `LonghandId`.
233
234 <%
235 extra = [
236 {
237 "name": "CSSWideKeyword",
238 "type": "WideKeywordDeclaration",
239 "doc": "A CSS-wide keyword.",
240 "copy": False,
241 },
242 {
243 "name": "WithVariables",
244 "type": "VariableDeclaration",
245 "doc": "An unparsed declaration.",
246 "copy": False,
247 },
248 {
249 "name": "Custom",
250 "type": "CustomDeclaration",
251 "doc": "A custom property declaration.",
252 "copy": False,
253 },
254 ]
255 for v in extra:
256 variants.append(v)
257 groups[v["type"]] = [v]
258 %>
259
260 /// Servo's representation for a property declaration.
261 #[derive(ToShmem)]
262 #[repr(u16)]
263 pub enum PropertyDeclaration {
264 % for variant in variants:
265 /// ${variant["doc"]}
266 ${variant["name"]}(${variant["type"]}),
267 % endfor
268 }
269
270 #[repr(C)]
271 struct PropertyDeclarationVariantRepr<T> {
272 tag: u16,
273 value: T
274 }
275
276 impl Clone for PropertyDeclaration {
277 #[inline]
clone(&self) -> Self278 fn clone(&self) -> Self {
279 use self::PropertyDeclaration::*;
280
281 <%
282 [copy, others] = [list(g) for _, g in groupby(variants, key=lambda x: not x["copy"])]
283 %>
284
285 let self_tag = unsafe {
286 (*(self as *const _ as *const PropertyDeclarationVariantRepr<()>)).tag
287 };
288 if self_tag <= LonghandId::${copy[-1]["name"]} as u16 {
289 #[derive(Clone, Copy)]
290 #[repr(u16)]
291 enum CopyVariants {
292 % for v in copy:
293 _${v["name"]}(${v["type"]}),
294 % endfor
295 }
296
297 unsafe {
298 let mut out = mem::MaybeUninit::uninit();
299 ptr::write(
300 out.as_mut_ptr() as *mut CopyVariants,
301 *(self as *const _ as *const CopyVariants),
302 );
303 return out.assume_init();
304 }
305 }
306
307 // This function ensures that all properties not handled above
308 // do not have a specified value implements Copy. If you hit
309 // compile error here, you may want to add the type name into
310 // Longhand.specified_is_copy in data.py.
311 fn _static_assert_others_are_not_copy() {
312 struct Helper<T>(T);
313 trait AssertCopy { fn assert() {} }
314 trait AssertNotCopy { fn assert() {} }
315 impl<T: Copy> AssertCopy for Helper<T> {}
316 % for ty in sorted(set(x["type"] for x in others)):
317 impl AssertNotCopy for Helper<${ty}> {}
318 Helper::<${ty}>::assert();
319 % endfor
320 }
321
322 match *self {
323 ${" |\n".join("{}(..)".format(v["name"]) for v in copy)} => {
324 unsafe { debug_unreachable!() }
325 }
326 % for ty, vs in groupby(others, key=lambda x: x["type"]):
327 <%
328 vs = list(vs)
329 %>
330 % if len(vs) == 1:
331 ${vs[0]["name"]}(ref value) => {
332 ${vs[0]["name"]}(value.clone())
333 }
334 % else:
335 ${" |\n".join("{}(ref value)".format(v["name"]) for v in vs)} => {
336 unsafe {
337 let mut out = mem::MaybeUninit::uninit();
338 ptr::write(
339 out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${ty}>,
340 PropertyDeclarationVariantRepr {
341 tag: *(self as *const _ as *const u16),
342 value: value.clone(),
343 },
344 );
345 out.assume_init()
346 }
347 }
348 % endif
349 % endfor
350 }
351 }
352 }
353
354 impl PartialEq for PropertyDeclaration {
355 #[inline]
eq(&self, other: &Self) -> bool356 fn eq(&self, other: &Self) -> bool {
357 use self::PropertyDeclaration::*;
358
359 unsafe {
360 let this_repr =
361 &*(self as *const _ as *const PropertyDeclarationVariantRepr<()>);
362 let other_repr =
363 &*(other as *const _ as *const PropertyDeclarationVariantRepr<()>);
364 if this_repr.tag != other_repr.tag {
365 return false;
366 }
367 match *self {
368 % for ty, vs in groupby(variants, key=lambda x: x["type"]):
369 ${" |\n".join("{}(ref this)".format(v["name"]) for v in vs)} => {
370 let other_repr =
371 &*(other as *const _ as *const PropertyDeclarationVariantRepr<${ty}>);
372 *this == other_repr.value
373 }
374 % endfor
375 }
376 }
377 }
378 }
379
380 impl MallocSizeOf for PropertyDeclaration {
381 #[inline]
size_of(&self, ops: &mut MallocSizeOfOps) -> usize382 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
383 use self::PropertyDeclaration::*;
384
385 match *self {
386 % for ty, vs in groupby(variants, key=lambda x: x["type"]):
387 ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
388 value.size_of(ops)
389 }
390 % endfor
391 }
392 }
393 }
394
395
396 impl PropertyDeclaration {
397 /// Like the method on ToCss, but without the type parameter to avoid
398 /// accidentally monomorphizing this large function multiple times for
399 /// different writers.
to_css(&self, dest: &mut CssStringWriter) -> fmt::Result400 pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
401 use self::PropertyDeclaration::*;
402
403 let mut dest = CssWriter::new(dest);
404 match *self {
405 % for ty, vs in groupby(variants, key=lambda x: x["type"]):
406 ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
407 value.to_css(&mut dest)
408 }
409 % endfor
410 }
411 }
412 }
413
414 /// A module with all the code related to animated properties.
415 ///
416 /// This needs to be "included" by mako at least after all longhand modules,
417 /// given they populate the global data.
418 pub mod animated_properties {
419 <%include file="/helpers/animated_properties.mako.rs" />
420 }
421
422 /// A longhand or shorthand property.
423 #[derive(Clone, Copy, Debug)]
424 pub struct NonCustomPropertyId(usize);
425
426 /// The length of all the non-custom properties.
427 pub const NON_CUSTOM_PROPERTY_ID_COUNT: usize =
428 ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())};
429
430 /// The length of all counted unknown properties.
431 pub const COUNTED_UNKNOWN_PROPERTY_COUNT: usize = ${len(data.counted_unknown_properties)};
432
433 % if engine == "gecko":
434 #[allow(dead_code)]
static_assert_nscsspropertyid()435 unsafe fn static_assert_nscsspropertyid() {
436 % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
437 std::mem::transmute::<[u8; ${i}], [u8; ${property.nscsspropertyid()} as usize]>([0; ${i}]); // ${property.name}
438 % endfor
439 }
440 % endif
441
442 impl NonCustomPropertyId {
443 /// Returns the underlying index, used for use counter.
bit(self) -> usize444 pub fn bit(self) -> usize {
445 self.0
446 }
447
448 #[cfg(feature = "gecko")]
449 #[inline]
to_nscsspropertyid(self) -> nsCSSPropertyID450 fn to_nscsspropertyid(self) -> nsCSSPropertyID {
451 // unsafe: guaranteed by static_assert_nscsspropertyid above.
452 unsafe { std::mem::transmute(self.0 as i32) }
453 }
454
455 /// Convert an `nsCSSPropertyID` into a `NonCustomPropertyId`.
456 #[cfg(feature = "gecko")]
457 #[inline]
from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()>458 pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
459 let prop = prop as i32;
460 if prop < 0 {
461 return Err(());
462 }
463 if prop >= NON_CUSTOM_PROPERTY_ID_COUNT as i32 {
464 return Err(());
465 }
466 // unsafe: guaranteed by static_assert_nscsspropertyid above.
467 Ok(unsafe { std::mem::transmute(prop as usize) })
468 }
469
470 /// Get the property name.
471 #[inline]
name(self) -> &'static str472 pub fn name(self) -> &'static str {
473 static MAP: [&'static str; NON_CUSTOM_PROPERTY_ID_COUNT] = [
474 % for property in data.longhands + data.shorthands + data.all_aliases():
475 "${property.name}",
476 % endfor
477 ];
478 MAP[self.0]
479 }
480
481 /// Returns whether this property is transitionable.
482 #[inline]
is_transitionable(self) -> bool483 pub fn is_transitionable(self) -> bool {
484 ${static_non_custom_property_id_set("TRANSITIONABLE", lambda p: p.transitionable)}
485 TRANSITIONABLE.contains(self)
486 }
487
488 /// Returns whether this property is animatable.
489 #[inline]
is_animatable(self) -> bool490 pub fn is_animatable(self) -> bool {
491 ${static_non_custom_property_id_set("ANIMATABLE", lambda p: p.animatable)}
492 ANIMATABLE.contains(self)
493 }
494
495 #[inline]
enabled_for_all_content(self) -> bool496 fn enabled_for_all_content(self) -> bool {
497 ${static_non_custom_property_id_set(
498 "EXPERIMENTAL",
499 lambda p: p.experimental(engine)
500 )}
501
502 ${static_non_custom_property_id_set(
503 "ALWAYS_ENABLED",
504 lambda p: (not p.experimental(engine)) and p.enabled_in_content()
505 )}
506
507 let passes_pref_check = || {
508 % if engine == "gecko":
509 unsafe { structs::nsCSSProps_gPropertyEnabled[self.0] }
510 % else:
511 static PREF_NAME: [Option< &str>; ${
512 len(data.longhands) + len(data.shorthands) + len(data.all_aliases())
513 }] = [
514 % for property in data.longhands + data.shorthands + data.all_aliases():
515 <%
516 attrs = {"servo-2013": "servo_2013_pref", "servo-2020": "servo_2020_pref"}
517 pref = getattr(property, attrs[engine])
518 %>
519 % if pref:
520 Some("${pref}"),
521 % else:
522 None,
523 % endif
524 % endfor
525 ];
526 let pref = match PREF_NAME[self.0] {
527 None => return true,
528 Some(pref) => pref,
529 };
530
531 prefs::pref_map().get(pref).as_bool().unwrap_or(false)
532 % endif
533 };
534
535 if ALWAYS_ENABLED.contains(self) {
536 return true
537 }
538
539 if EXPERIMENTAL.contains(self) && passes_pref_check() {
540 return true
541 }
542
543 false
544 }
545
546 /// Returns whether a given rule allows a given property.
547 #[inline]
allowed_in_rule(self, rule_type: CssRuleType) -> bool548 pub fn allowed_in_rule(self, rule_type: CssRuleType) -> bool {
549 debug_assert!(
550 matches!(
551 rule_type,
552 CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style
553 ),
554 "Declarations are only expected inside a keyframe, page, or style rule."
555 );
556
557 static MAP: [u8; NON_CUSTOM_PROPERTY_ID_COUNT] = [
558 % for property in data.longhands + data.shorthands + data.all_aliases():
559 ${property.rule_types_allowed},
560 % endfor
561 ];
562 match rule_type {
563 % for name in RULE_VALUES:
564 CssRuleType::${name} => MAP[self.0] & ${RULE_VALUES[name]} != 0,
565 % endfor
566 _ => true
567 }
568 }
569
allowed_in(self, context: &ParserContext) -> bool570 fn allowed_in(self, context: &ParserContext) -> bool {
571 if !self.allowed_in_rule(context.rule_type()) {
572 return false;
573 }
574
575 self.allowed_in_ignoring_rule_type(context)
576 }
577
578
allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool579 fn allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool {
580 // The semantics of these are kinda hard to reason about, what follows
581 // is a description of the different combinations that can happen with
582 // these three sets.
583 //
584 // Experimental properties are generally controlled by prefs, but an
585 // experimental property explicitly enabled in certain context (UA or
586 // chrome sheets) is always usable in the context regardless of the
587 // pref value.
588 //
589 // Non-experimental properties are either normal properties which are
590 // usable everywhere, or internal-only properties which are only usable
591 // in certain context they are explicitly enabled in.
592 if self.enabled_for_all_content() {
593 return true;
594 }
595
596 ${static_non_custom_property_id_set(
597 "ENABLED_IN_UA_SHEETS",
598 lambda p: p.explicitly_enabled_in_ua_sheets()
599 )}
600 ${static_non_custom_property_id_set(
601 "ENABLED_IN_CHROME",
602 lambda p: p.explicitly_enabled_in_chrome()
603 )}
604
605 if context.stylesheet_origin == Origin::UserAgent &&
606 ENABLED_IN_UA_SHEETS.contains(self)
607 {
608 return true
609 }
610
611 if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) {
612 return true
613 }
614
615 false
616 }
617
618 /// The supported types of this property. The return value should be
619 /// style_traits::CssType when it can become a bitflags type.
supported_types(&self) -> u8620 fn supported_types(&self) -> u8 {
621 const SUPPORTED_TYPES: [u8; ${len(data.longhands) + len(data.shorthands)}] = [
622 % for prop in data.longhands:
623 <${prop.specified_type()} as SpecifiedValueInfo>::SUPPORTED_TYPES,
624 % endfor
625 % for prop in data.shorthands:
626 % if prop.name == "all":
627 0, // 'all' accepts no value other than CSS-wide keywords
628 % else:
629 <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES,
630 % endif
631 % endfor
632 ];
633 SUPPORTED_TYPES[self.0]
634 }
635
636 /// See PropertyId::collect_property_completion_keywords.
collect_property_completion_keywords(&self, f: KeywordsCollectFn)637 fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
638 fn do_nothing(_: KeywordsCollectFn) {}
639 const COLLECT_FUNCTIONS: [fn(KeywordsCollectFn);
640 ${len(data.longhands) + len(data.shorthands)}] = [
641 % for prop in data.longhands:
642 <${prop.specified_type()} as SpecifiedValueInfo>::collect_completion_keywords,
643 % endfor
644 % for prop in data.shorthands:
645 % if prop.name == "all":
646 do_nothing, // 'all' accepts no value other than CSS-wide keywords
647 % else:
648 <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::
649 collect_completion_keywords,
650 % endif
651 % endfor
652 ];
653 COLLECT_FUNCTIONS[self.0](f);
654 }
655
656 /// Turns this `NonCustomPropertyId` into a `PropertyId`.
657 #[inline]
to_property_id(self) -> PropertyId658 pub fn to_property_id(self) -> PropertyId {
659 use std::mem::transmute;
660 if self.0 < ${len(data.longhands)} {
661 return unsafe {
662 PropertyId::Longhand(transmute(self.0 as u16))
663 }
664 }
665 if self.0 < ${len(data.longhands) + len(data.shorthands)} {
666 return unsafe {
667 PropertyId::Shorthand(transmute((self.0 - ${len(data.longhands)}) as u16))
668 }
669 }
670 assert!(self.0 < NON_CUSTOM_PROPERTY_ID_COUNT);
671 let alias_id: AliasId = unsafe {
672 transmute((self.0 - ${len(data.longhands) + len(data.shorthands)}) as u16)
673 };
674
675 match alias_id.aliased_property() {
676 AliasedPropertyId::Longhand(longhand) => PropertyId::LonghandAlias(longhand, alias_id),
677 AliasedPropertyId::Shorthand(shorthand) => PropertyId::ShorthandAlias(shorthand, alias_id),
678 }
679 }
680 }
681
682 impl From<LonghandId> for NonCustomPropertyId {
683 #[inline]
from(id: LonghandId) -> Self684 fn from(id: LonghandId) -> Self {
685 NonCustomPropertyId(id as usize)
686 }
687 }
688
689 impl From<ShorthandId> for NonCustomPropertyId {
690 #[inline]
from(id: ShorthandId) -> Self691 fn from(id: ShorthandId) -> Self {
692 NonCustomPropertyId((id as usize) + ${len(data.longhands)})
693 }
694 }
695
696 impl From<AliasId> for NonCustomPropertyId {
697 #[inline]
from(id: AliasId) -> Self698 fn from(id: AliasId) -> Self {
699 NonCustomPropertyId(id as usize + ${len(data.longhands) + len(data.shorthands)})
700 }
701 }
702
703 /// A set of all properties
704 #[derive(Clone, PartialEq)]
705 pub struct NonCustomPropertyIdSet {
706 storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32]
707 }
708
709 impl NonCustomPropertyIdSet {
710 /// Creates an empty `NonCustomPropertyIdSet`.
new() -> Self711 pub fn new() -> Self {
712 Self {
713 storage: Default::default(),
714 }
715 }
716
717 /// Insert a non-custom-property in the set.
718 #[inline]
insert(&mut self, id: NonCustomPropertyId)719 pub fn insert(&mut self, id: NonCustomPropertyId) {
720 let bit = id.0;
721 self.storage[bit / 32] |= 1 << (bit % 32);
722 }
723
724 /// Return whether the given property is in the set
725 #[inline]
contains(&self, id: NonCustomPropertyId) -> bool726 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
727 let bit = id.0;
728 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
729 }
730 }
731
732 <%def name="static_non_custom_property_id_set(name, is_member)">
733 static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {
734 <%
735 storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)
736 for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
737 if is_member(property):
738 storage[int(i / 32)] |= 1 << (i % 32)
739 %>
740 storage: [${", ".join("0x%x" % word for word in storage)}]
741 };
742 </%def>
743
744 <%def name="static_longhand_id_set(name, is_member)">
745 static ${name}: LonghandIdSet = LonghandIdSet {
746 <%
747 storage = [0] * int((len(data.longhands) - 1 + 32) / 32)
748 for i, property in enumerate(data.longhands):
749 if is_member(property):
750 storage[int(i / 32)] |= 1 << (i % 32)
751 %>
752 storage: [${", ".join("0x%x" % word for word in storage)}]
753 };
754 </%def>
755
756 <%
757 logical_groups = defaultdict(list)
758 for prop in data.longhands:
759 if prop.logical_group:
760 logical_groups[prop.logical_group].append(prop)
761
762 for group, props in logical_groups.items():
763 logical_count = sum(1 for p in props if p.logical)
764 if logical_count * 2 != len(props):
765 raise RuntimeError("Logical group {} has ".format(group) +
766 "unbalanced logical / physical properties")
767
768 FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data)
769 FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data)
770 MARKER_RESTRICTIONS = PropertyRestrictions.marker(data)
771 PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data)
772 CUE_RESTRICTIONS = PropertyRestrictions.cue(data)
773
774 def restriction_flags(property):
775 name = property.name
776 flags = []
777 if name in FIRST_LINE_RESTRICTIONS:
778 flags.append("APPLIES_TO_FIRST_LINE")
779 if name in FIRST_LETTER_RESTRICTIONS:
780 flags.append("APPLIES_TO_FIRST_LETTER")
781 if name in PLACEHOLDER_RESTRICTIONS:
782 flags.append("APPLIES_TO_PLACEHOLDER")
783 if name in MARKER_RESTRICTIONS:
784 flags.append("APPLIES_TO_MARKER")
785 if name in CUE_RESTRICTIONS:
786 flags.append("APPLIES_TO_CUE")
787 return flags
788
789 %>
790
791 /// A group for properties which may override each other
792 /// via logical resolution.
793 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
794 #[repr(u8)]
795 pub enum LogicalGroup {
796 % for i, group in enumerate(logical_groups.keys()):
797 /// ${group}
798 ${to_camel_case(group)} = ${i},
799 % endfor
800 }
801
802
803 /// A set of logical groups.
804 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
805 pub struct LogicalGroupSet {
806 storage: [u32; (${len(logical_groups)} - 1 + 32) / 32]
807 }
808
809 impl LogicalGroupSet {
810 /// Creates an empty `NonCustomPropertyIdSet`.
new() -> Self811 pub fn new() -> Self {
812 Self {
813 storage: Default::default(),
814 }
815 }
816
817 /// Return whether the given group is in the set
818 #[inline]
contains(&self, g: LogicalGroup) -> bool819 pub fn contains(&self, g: LogicalGroup) -> bool {
820 let bit = g as usize;
821 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
822 }
823
824 /// Insert a group the set.
825 #[inline]
insert(&mut self, g: LogicalGroup)826 pub fn insert(&mut self, g: LogicalGroup) {
827 let bit = g as usize;
828 self.storage[bit / 32] |= 1 << (bit % 32);
829 }
830 }
831
832 /// A set of longhand properties
833 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
834 pub struct LonghandIdSet {
835 storage: [u32; (${len(data.longhands)} - 1 + 32) / 32]
836 }
837
838 impl_trivial_to_shmem!(LonghandIdSet);
839
840 /// An iterator over a set of longhand ids.
841 pub struct LonghandIdSetIterator<'a> {
842 longhands: &'a LonghandIdSet,
843 cur: usize,
844 }
845
846 impl<'a> Iterator for LonghandIdSetIterator<'a> {
847 type Item = LonghandId;
848
next(&mut self) -> Option<Self::Item>849 fn next(&mut self) -> Option<Self::Item> {
850 use std::mem;
851
852 loop {
853 if self.cur >= ${len(data.longhands)} {
854 return None;
855 }
856
857 let id: LonghandId = unsafe { mem::transmute(self.cur as u16) };
858 self.cur += 1;
859
860 if self.longhands.contains(id) {
861 return Some(id);
862 }
863 }
864 }
865 }
866
867 impl LonghandIdSet {
868 #[inline]
reset() -> &'static Self869 fn reset() -> &'static Self {
870 ${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
871 &RESET
872 }
873
874 #[inline]
animatable() -> &'static Self875 fn animatable() -> &'static Self {
876 ${static_longhand_id_set("ANIMATABLE", lambda p: p.animatable)}
877 &ANIMATABLE
878 }
879
880 #[inline]
discrete_animatable() -> &'static Self881 fn discrete_animatable() -> &'static Self {
882 ${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_value_type == "discrete")}
883 &DISCRETE_ANIMATABLE
884 }
885
886 #[inline]
logical() -> &'static Self887 fn logical() -> &'static Self {
888 ${static_longhand_id_set("LOGICAL", lambda p: p.logical)}
889 &LOGICAL
890 }
891
892 /// Returns the set of longhands that are ignored when document colors are
893 /// disabled.
894 #[inline]
ignored_when_colors_disabled() -> &'static Self895 pub fn ignored_when_colors_disabled() -> &'static Self {
896 ${static_longhand_id_set(
897 "IGNORED_WHEN_COLORS_DISABLED",
898 lambda p: p.ignored_when_colors_disabled
899 )}
900 &IGNORED_WHEN_COLORS_DISABLED
901 }
902
903 /// Returns the set of properties that are declared as having no effect on
904 /// Gecko <scrollbar> elements or their descendant scrollbar parts.
905 #[cfg(debug_assertions)]
906 #[cfg(feature = "gecko")]
907 #[inline]
has_no_effect_on_gecko_scrollbars() -> &'static Self908 pub fn has_no_effect_on_gecko_scrollbars() -> &'static Self {
909 // data.py asserts that has_no_effect_on_gecko_scrollbars is True or
910 // False for properties that are inherited and Gecko pref controlled,
911 // and is None for all other properties.
912 ${static_longhand_id_set(
913 "HAS_NO_EFFECT_ON_SCROLLBARS",
914 lambda p: p.has_effect_on_gecko_scrollbars is False
915 )}
916 &HAS_NO_EFFECT_ON_SCROLLBARS
917 }
918
919 /// Returns the set of padding properties for the purpose of disabling
920 /// native appearance.
921 #[inline]
padding_properties() -> &'static Self922 pub fn padding_properties() -> &'static Self {
923 <% assert "padding" in logical_groups %>
924 ${static_longhand_id_set(
925 "PADDING_PROPERTIES",
926 lambda p: p.logical_group == "padding"
927 )}
928 &PADDING_PROPERTIES
929 }
930
931 /// Returns the set of border properties for the purpose of disabling native
932 /// appearance.
933 #[inline]
border_background_properties() -> &'static Self934 pub fn border_background_properties() -> &'static Self {
935 ${static_longhand_id_set(
936 "BORDER_BACKGROUND_PROPERTIES",
937 lambda p: (p.logical_group and p.logical_group.startswith("border")) or \
938 p.name in ["background-color", "background-image"]
939 )}
940 &BORDER_BACKGROUND_PROPERTIES
941 }
942
943 /// Iterate over the current longhand id set.
iter(&self) -> LonghandIdSetIterator944 pub fn iter(&self) -> LonghandIdSetIterator {
945 LonghandIdSetIterator { longhands: self, cur: 0, }
946 }
947
948 /// Returns whether this set contains at least every longhand that `other`
949 /// also contains.
contains_all(&self, other: &Self) -> bool950 pub fn contains_all(&self, other: &Self) -> bool {
951 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
952 if (*self_cell & *other_cell) != *other_cell {
953 return false;
954 }
955 }
956 true
957 }
958
959 /// Returns whether this set contains any longhand that `other` also contains.
contains_any(&self, other: &Self) -> bool960 pub fn contains_any(&self, other: &Self) -> bool {
961 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
962 if (*self_cell & *other_cell) != 0 {
963 return true;
964 }
965 }
966 false
967 }
968
969 /// Remove all the given properties from the set.
970 #[inline]
remove_all(&mut self, other: &Self)971 pub fn remove_all(&mut self, other: &Self) {
972 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
973 *self_cell &= !*other_cell;
974 }
975 }
976
977 /// Create an empty set
978 #[inline]
new() -> LonghandIdSet979 pub fn new() -> LonghandIdSet {
980 LonghandIdSet { storage: [0; (${len(data.longhands)} - 1 + 32) / 32] }
981 }
982
983 /// Return whether the given property is in the set
984 #[inline]
contains(&self, id: LonghandId) -> bool985 pub fn contains(&self, id: LonghandId) -> bool {
986 let bit = id as usize;
987 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
988 }
989
990 /// Return whether this set contains any reset longhand.
991 #[inline]
contains_any_reset(&self) -> bool992 pub fn contains_any_reset(&self) -> bool {
993 self.contains_any(Self::reset())
994 }
995
996 /// Add the given property to the set
997 #[inline]
insert(&mut self, id: LonghandId)998 pub fn insert(&mut self, id: LonghandId) {
999 let bit = id as usize;
1000 self.storage[bit / 32] |= 1 << (bit % 32);
1001 }
1002
1003 /// Remove the given property from the set
1004 #[inline]
remove(&mut self, id: LonghandId)1005 pub fn remove(&mut self, id: LonghandId) {
1006 let bit = id as usize;
1007 self.storage[bit / 32] &= !(1 << (bit % 32));
1008 }
1009
1010 /// Clear all bits
1011 #[inline]
clear(&mut self)1012 pub fn clear(&mut self) {
1013 for cell in &mut self.storage {
1014 *cell = 0
1015 }
1016 }
1017
1018 /// Returns whether the set is empty.
1019 #[inline]
is_empty(&self) -> bool1020 pub fn is_empty(&self) -> bool {
1021 self.storage.iter().all(|c| *c == 0)
1022 }
1023 }
1024
1025 /// An enum to represent a CSS Wide keyword.
1026 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
1027 ToCss, ToShmem)]
1028 pub enum CSSWideKeyword {
1029 /// The `initial` keyword.
1030 Initial,
1031 /// The `inherit` keyword.
1032 Inherit,
1033 /// The `unset` keyword.
1034 Unset,
1035 /// The `revert` keyword.
1036 Revert,
1037 }
1038
1039 impl CSSWideKeyword {
to_str(&self) -> &'static str1040 fn to_str(&self) -> &'static str {
1041 match *self {
1042 CSSWideKeyword::Initial => "initial",
1043 CSSWideKeyword::Inherit => "inherit",
1044 CSSWideKeyword::Unset => "unset",
1045 CSSWideKeyword::Revert => "revert",
1046 }
1047 }
1048 }
1049
1050 impl CSSWideKeyword {
parse(input: &mut Parser) -> Result<Self, ()>1051 fn parse(input: &mut Parser) -> Result<Self, ()> {
1052 let keyword = {
1053 let ident = input.expect_ident().map_err(|_| ())?;
1054 match_ignore_ascii_case! { ident,
1055 // If modifying this set of keyword, also update values::CustomIdent::from_ident
1056 "initial" => CSSWideKeyword::Initial,
1057 "inherit" => CSSWideKeyword::Inherit,
1058 "unset" => CSSWideKeyword::Unset,
1059 "revert" => CSSWideKeyword::Revert,
1060 _ => return Err(()),
1061 }
1062 };
1063 input.expect_exhausted().map_err(|_| ())?;
1064 Ok(keyword)
1065 }
1066 }
1067
1068 bitflags! {
1069 /// A set of flags for properties.
1070 pub struct PropertyFlags: u16 {
1071 /// This longhand property applies to ::first-letter.
1072 const APPLIES_TO_FIRST_LETTER = 1 << 1;
1073 /// This longhand property applies to ::first-line.
1074 const APPLIES_TO_FIRST_LINE = 1 << 2;
1075 /// This longhand property applies to ::placeholder.
1076 const APPLIES_TO_PLACEHOLDER = 1 << 3;
1077 /// This longhand property applies to ::cue.
1078 const APPLIES_TO_CUE = 1 << 4;
1079 /// This longhand property applies to ::marker.
1080 const APPLIES_TO_MARKER = 1 << 5;
1081 /// This property is a legacy shorthand.
1082 ///
1083 /// https://drafts.csswg.org/css-cascade/#legacy-shorthand
1084 const IS_LEGACY_SHORTHAND = 1 << 6;
1085
1086 /* The following flags are currently not used in Rust code, they
1087 * only need to be listed in corresponding properties so that
1088 * they can be checked in the C++ side via ServoCSSPropList.h. */
1089 /// This property can be animated on the compositor.
1090 const CAN_ANIMATE_ON_COMPOSITOR = 0;
1091 /// This shorthand property is accessible from getComputedStyle.
1092 const SHORTHAND_IN_GETCS = 0;
1093 }
1094 }
1095
1096 /// An identifier for a given longhand property.
1097 #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
1098 #[repr(u16)]
1099 pub enum LonghandId {
1100 % for i, property in enumerate(data.longhands):
1101 /// ${property.name}
1102 ${property.camel_case} = ${i},
1103 % endfor
1104 }
1105
1106 impl ToCss for LonghandId {
1107 #[inline]
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1108 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1109 where
1110 W: Write,
1111 {
1112 dest.write_str(self.name())
1113 }
1114 }
1115
1116 impl fmt::Debug for LonghandId {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result1117 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1118 formatter.write_str(self.name())
1119 }
1120 }
1121
1122 impl LonghandId {
1123 /// Get the name of this longhand property.
1124 #[inline]
name(&self) -> &'static str1125 pub fn name(&self) -> &'static str {
1126 NonCustomPropertyId::from(*self).name()
1127 }
1128
1129 /// Returns whether the longhand property is inherited by default.
1130 #[inline]
inherited(self) -> bool1131 pub fn inherited(self) -> bool {
1132 !LonghandIdSet::reset().contains(self)
1133 }
1134
shorthands(&self) -> NonCustomPropertyIterator<ShorthandId>1135 fn shorthands(&self) -> NonCustomPropertyIterator<ShorthandId> {
1136 // first generate longhand to shorthands lookup map
1137 //
1138 // NOTE(emilio): This currently doesn't exclude the "all" shorthand. It
1139 // could potentially do so, which would speed up serialization
1140 // algorithms and what not, I guess.
1141 <%
1142 from functools import cmp_to_key
1143 longhand_to_shorthand_map = {}
1144 num_sub_properties = {}
1145 for shorthand in data.shorthands:
1146 num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties)
1147 for sub_property in shorthand.sub_properties:
1148 if sub_property.ident not in longhand_to_shorthand_map:
1149 longhand_to_shorthand_map[sub_property.ident] = []
1150
1151 longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)
1152
1153 def cmp(a, b):
1154 return (a > b) - (a < b)
1155
1156 def preferred_order(x, y):
1157 # Since we want properties in order from most subproperties to least,
1158 # reverse the arguments to cmp from the expected order.
1159 result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0))
1160 if result:
1161 return result
1162 # Fall back to lexicographic comparison.
1163 return cmp(x, y)
1164
1165 # Sort the lists of shorthand properties according to preferred order:
1166 # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order
1167 for shorthand_list in longhand_to_shorthand_map.values():
1168 shorthand_list.sort(key=cmp_to_key(preferred_order))
1169 %>
1170
1171 // based on lookup results for each longhand, create result arrays
1172 % for property in data.longhands:
1173 static ${property.ident.upper()}: &'static [ShorthandId] = &[
1174 % for shorthand in longhand_to_shorthand_map.get(property.ident, []):
1175 ShorthandId::${shorthand},
1176 % endfor
1177 ];
1178 % endfor
1179
1180 NonCustomPropertyIterator {
1181 filter: NonCustomPropertyId::from(*self).enabled_for_all_content(),
1182 iter: match *self {
1183 % for property in data.longhands:
1184 LonghandId::${property.camel_case} => ${property.ident.upper()},
1185 % endfor
1186 }.iter(),
1187 }
1188 }
1189
parse_value<'i, 't>( &self, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<PropertyDeclaration, ParseError<'i>>1190 fn parse_value<'i, 't>(
1191 &self,
1192 context: &ParserContext,
1193 input: &mut Parser<'i, 't>,
1194 ) -> Result<PropertyDeclaration, ParseError<'i>> {
1195 type ParsePropertyFn = for<'i, 't> fn(
1196 context: &ParserContext,
1197 input: &mut Parser<'i, 't>,
1198 ) -> Result<PropertyDeclaration, ParseError<'i>>;
1199 static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [
1200 % for property in data.longhands:
1201 longhands::${property.ident}::parse_declared,
1202 % endfor
1203 ];
1204 (PARSE_PROPERTY[*self as usize])(context, input)
1205 }
1206
1207 /// Returns whether this property is animatable.
1208 #[inline]
is_animatable(self) -> bool1209 pub fn is_animatable(self) -> bool {
1210 LonghandIdSet::animatable().contains(self)
1211 }
1212
1213 /// Returns whether this property is animatable in a discrete way.
1214 #[inline]
is_discrete_animatable(self) -> bool1215 pub fn is_discrete_animatable(self) -> bool {
1216 LonghandIdSet::discrete_animatable().contains(self)
1217 }
1218
1219 /// Converts from a LonghandId to an adequate nsCSSPropertyID.
1220 #[cfg(feature = "gecko")]
1221 #[inline]
to_nscsspropertyid(self) -> nsCSSPropertyID1222 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
1223 NonCustomPropertyId::from(self).to_nscsspropertyid()
1224 }
1225
1226 #[cfg(feature = "gecko")]
1227 #[allow(non_upper_case_globals)]
1228 /// Returns a longhand id from Gecko's nsCSSPropertyID.
from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()>1229 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
1230 match PropertyId::from_nscsspropertyid(id) {
1231 Ok(PropertyId::Longhand(id)) |
1232 Ok(PropertyId::LonghandAlias(id, _)) => Ok(id),
1233 _ => Err(()),
1234 }
1235 }
1236
1237 /// Return whether this property is logical.
1238 #[inline]
is_logical(self) -> bool1239 pub fn is_logical(self) -> bool {
1240 LonghandIdSet::logical().contains(self)
1241 }
1242
1243 /// If this is a logical property, return the corresponding physical one in
1244 /// the given writing mode.
1245 ///
1246 /// Otherwise, return unchanged.
1247 #[inline]
to_physical(&self, wm: WritingMode) -> Self1248 pub fn to_physical(&self, wm: WritingMode) -> Self {
1249 match *self {
1250 % for property in data.longhands:
1251 % if property.logical:
1252 <% logical_group = property.logical_group %>
1253 LonghandId::${property.camel_case} => {
1254 <%helpers:logical_setter_helper name="${property.name}">
1255 <%def name="inner(physical_ident)">
1256 <%
1257 physical_name = physical_ident.replace("_", "-")
1258 physical_property = data.longhands_by_name[physical_name]
1259 assert logical_group == physical_property.logical_group
1260 %>
1261 LonghandId::${to_camel_case(physical_ident)}
1262 </%def>
1263 </%helpers:logical_setter_helper>
1264 }
1265 % endif
1266 % endfor
1267 _ => *self
1268 }
1269 }
1270
1271 /// Return the logical group of this longhand property.
logical_group(&self) -> Option<LogicalGroup>1272 pub fn logical_group(&self) -> Option<LogicalGroup> {
1273 const LOGICAL_GROUPS: [Option<LogicalGroup>; ${len(data.longhands)}] = [
1274 % for prop in data.longhands:
1275 % if prop.logical_group:
1276 Some(LogicalGroup::${to_camel_case(prop.logical_group)}),
1277 % else:
1278 None,
1279 % endif
1280 % endfor
1281 ];
1282 LOGICAL_GROUPS[*self as usize]
1283 }
1284
1285 /// Returns PropertyFlags for given longhand property.
1286 #[inline(always)]
flags(self) -> PropertyFlags1287 pub fn flags(self) -> PropertyFlags {
1288 // TODO(emilio): This can be simplified further as Rust gains more
1289 // constant expression support.
1290 const FLAGS: [u16; ${len(data.longhands)}] = [
1291 % for property in data.longhands:
1292 % for flag in property.flags + restriction_flags(property):
1293 PropertyFlags::${flag}.bits |
1294 % endfor
1295 0,
1296 % endfor
1297 ];
1298 PropertyFlags::from_bits_truncate(FLAGS[self as usize])
1299 }
1300
1301 /// Only a few properties are allowed to depend on the visited state of
1302 /// links. When cascading visited styles, we can save time by only
1303 /// processing these properties.
is_visited_dependent(&self) -> bool1304 fn is_visited_dependent(&self) -> bool {
1305 matches!(*self,
1306 % if engine == "gecko":
1307 LonghandId::ColumnRuleColor |
1308 LonghandId::TextEmphasisColor |
1309 LonghandId::WebkitTextFillColor |
1310 LonghandId::WebkitTextStrokeColor |
1311 LonghandId::TextDecorationColor |
1312 LonghandId::Fill |
1313 LonghandId::Stroke |
1314 LonghandId::CaretColor |
1315 % endif
1316 LonghandId::BackgroundColor |
1317 LonghandId::BorderTopColor |
1318 LonghandId::BorderRightColor |
1319 LonghandId::BorderBottomColor |
1320 LonghandId::BorderLeftColor |
1321 % if engine in ["gecko", "servo-2013"]:
1322 LonghandId::OutlineColor |
1323 % endif
1324 LonghandId::Color
1325 )
1326 }
1327
1328 /// Returns true if the property is one that is ignored when document
1329 /// colors are disabled.
1330 #[inline]
ignored_when_document_colors_disabled(self) -> bool1331 fn ignored_when_document_colors_disabled(self) -> bool {
1332 LonghandIdSet::ignored_when_colors_disabled().contains(self)
1333 }
1334
1335 /// The computed value of some properties depends on the (sometimes
1336 /// computed) value of *other* properties.
1337 ///
1338 /// So we classify properties into "early" and "other", such that the only
1339 /// dependencies can be from "other" to "early".
1340 ///
1341 /// Unfortunately, it’s not easy to check that this classification is
1342 /// correct.
is_early_property(&self) -> bool1343 fn is_early_property(&self) -> bool {
1344 matches!(*self,
1345 % if engine == "gecko":
1346
1347 // Needed to properly compute the writing mode, to resolve logical
1348 // properties, and similar stuff. In this block instead of along
1349 // `WritingMode` and `Direction` just for convenience, since it's
1350 // Gecko-only (for now at least).
1351 //
1352 // see WritingMode::new.
1353 LonghandId::TextOrientation |
1354
1355 // Needed to properly compute the zoomed font-size.
1356 //
1357 // FIXME(emilio): This could probably just be a cascade flag like
1358 // IN_SVG_SUBTREE or such, and we could nuke this property.
1359 LonghandId::XTextZoom |
1360
1361 // Needed to do font-size computation in a language-dependent way.
1362 LonghandId::XLang |
1363 // Needed for ruby to respect language-dependent min-font-size
1364 // preferences properly, see bug 1165538.
1365 LonghandId::MozMinFontSizeRatio |
1366
1367 // font-size depends on math-depth's computed value.
1368 LonghandId::MathDepth |
1369 % endif
1370
1371 // Needed to compute the first available font, in order to
1372 // compute font-relative units correctly.
1373 LonghandId::FontSize |
1374 LonghandId::FontWeight |
1375 LonghandId::FontStretch |
1376 LonghandId::FontStyle |
1377 LonghandId::FontFamily |
1378
1379 // Needed to properly compute the writing mode, to resolve logical
1380 // properties, and similar stuff.
1381 LonghandId::WritingMode |
1382 LonghandId::Direction
1383 )
1384 }
1385 }
1386
1387 /// An iterator over all the property ids that are enabled for a given
1388 /// shorthand, if that shorthand is enabled for all content too.
1389 pub struct NonCustomPropertyIterator<Item: 'static> {
1390 filter: bool,
1391 iter: std::slice::Iter<'static, Item>,
1392 }
1393
1394 impl<Item> Iterator for NonCustomPropertyIterator<Item>
1395 where
1396 Item: 'static + Copy + Into<NonCustomPropertyId>,
1397 {
1398 type Item = Item;
1399
next(&mut self) -> Option<Self::Item>1400 fn next(&mut self) -> Option<Self::Item> {
1401 loop {
1402 let id = *self.iter.next()?;
1403 if !self.filter || id.into().enabled_for_all_content() {
1404 return Some(id)
1405 }
1406 }
1407 }
1408 }
1409
1410 /// An identifier for a given shorthand property.
1411 #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
1412 #[repr(u16)]
1413 pub enum ShorthandId {
1414 % for i, property in enumerate(data.shorthands):
1415 /// ${property.name}
1416 ${property.camel_case} = ${i},
1417 % endfor
1418 }
1419
1420 impl ToCss for ShorthandId {
1421 #[inline]
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1422 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1423 where
1424 W: Write,
1425 {
1426 dest.write_str(self.name())
1427 }
1428 }
1429
1430 impl ShorthandId {
1431 /// Get the name for this shorthand property.
1432 #[inline]
name(&self) -> &'static str1433 pub fn name(&self) -> &'static str {
1434 NonCustomPropertyId::from(*self).name()
1435 }
1436
1437 /// Converts from a ShorthandId to an adequate nsCSSPropertyID.
1438 #[cfg(feature = "gecko")]
1439 #[inline]
to_nscsspropertyid(self) -> nsCSSPropertyID1440 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
1441 NonCustomPropertyId::from(self).to_nscsspropertyid()
1442 }
1443
1444 /// Converts from a nsCSSPropertyID to a ShorthandId.
1445 #[cfg(feature = "gecko")]
1446 #[inline]
from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()>1447 pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
1448 PropertyId::from_nscsspropertyid(prop)?.as_shorthand().map_err(|_| ())
1449 }
1450
1451 /// Get the longhand ids that form this shorthand.
longhands(&self) -> NonCustomPropertyIterator<LonghandId>1452 pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> {
1453 % for property in data.shorthands:
1454 static ${property.ident.upper()}: &'static [LonghandId] = &[
1455 % for sub in property.sub_properties:
1456 LonghandId::${sub.camel_case},
1457 % endfor
1458 ];
1459 % endfor
1460 NonCustomPropertyIterator {
1461 filter: NonCustomPropertyId::from(*self).enabled_for_all_content(),
1462 iter: match *self {
1463 % for property in data.shorthands:
1464 ShorthandId::${property.camel_case} => ${property.ident.upper()},
1465 % endfor
1466 }.iter()
1467 }
1468 }
1469
1470 /// Try to serialize the given declarations as this shorthand.
1471 ///
1472 /// Returns an error if writing to the stream fails, or if the declarations
1473 /// do not map to a shorthand.
longhands_to_css( &self, declarations: &[&PropertyDeclaration], dest: &mut CssStringWriter, ) -> fmt::Result1474 pub fn longhands_to_css(
1475 &self,
1476 declarations: &[&PropertyDeclaration],
1477 dest: &mut CssStringWriter,
1478 ) -> fmt::Result {
1479 type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result;
1480 fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result {
1481 // No need to try to serialize the declarations as the 'all'
1482 // shorthand, since it only accepts CSS-wide keywords (and variable
1483 // references), which will be handled in
1484 // get_shorthand_appendable_value.
1485 Ok(())
1486 }
1487
1488 static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [
1489 % for shorthand in data.shorthands:
1490 % if shorthand.ident == "all":
1491 all_to_css,
1492 % else:
1493 shorthands::${shorthand.ident}::to_css,
1494 % endif
1495 % endfor
1496 ];
1497
1498 LONGHANDS_TO_CSS[*self as usize](declarations, dest)
1499 }
1500
1501 /// Finds and returns an appendable value for the given declarations.
1502 ///
1503 /// Returns the optional appendable value.
get_shorthand_appendable_value<'a, 'b: 'a>( self, declarations: &'a [&'b PropertyDeclaration], ) -> Option<AppendableValue<'a, 'b>>1504 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
1505 self,
1506 declarations: &'a [&'b PropertyDeclaration],
1507 ) -> Option<AppendableValue<'a, 'b>> {
1508 let first_declaration = declarations.get(0)?;
1509 let rest = || declarations.iter().skip(1);
1510
1511 // https://drafts.csswg.org/css-variables/#variables-in-shorthands
1512 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
1513 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
1514 return Some(AppendableValue::Css(css));
1515 }
1516 return None;
1517 }
1518
1519 // Check whether they are all the same CSS-wide keyword.
1520 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
1521 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
1522 return Some(AppendableValue::Css(keyword.to_str()))
1523 }
1524 return None;
1525 }
1526
1527 if self == ShorthandId::All {
1528 // 'all' only supports variables and CSS wide keywords.
1529 return None;
1530 }
1531
1532 // Check whether all declarations can be serialized as part of shorthand.
1533 if declarations.iter().all(|d| d.may_serialize_as_part_of_shorthand()) {
1534 return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
1535 }
1536
1537 None
1538 }
1539
1540 /// Returns PropertyFlags for the given shorthand property.
1541 #[inline]
flags(self) -> PropertyFlags1542 pub fn flags(self) -> PropertyFlags {
1543 const FLAGS: [u16; ${len(data.shorthands)}] = [
1544 % for property in data.shorthands:
1545 % for flag in property.flags:
1546 PropertyFlags::${flag}.bits |
1547 % endfor
1548 0,
1549 % endfor
1550 ];
1551 PropertyFlags::from_bits_truncate(FLAGS[self as usize])
1552 }
1553
1554 /// Returns whether this property is a legacy shorthand.
1555 #[inline]
is_legacy_shorthand(self) -> bool1556 pub fn is_legacy_shorthand(self) -> bool {
1557 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
1558 }
1559
1560 /// Returns the order in which this property appears relative to other
1561 /// shorthands in idl-name-sorting order.
1562 #[inline]
idl_name_sort_order(self) -> u321563 pub fn idl_name_sort_order(self) -> u32 {
1564 <%
1565 from data import to_idl_name
1566 ordered = {}
1567 sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
1568 for order, shorthand in enumerate(sorted_shorthands):
1569 ordered[shorthand.ident] = order
1570 %>
1571 static IDL_NAME_SORT_ORDER: [u32; ${len(data.shorthands)}] = [
1572 % for property in data.shorthands:
1573 ${ordered[property.ident]},
1574 % endfor
1575 ];
1576 IDL_NAME_SORT_ORDER[self as usize]
1577 }
1578
parse_into<'i, 't>( &self, declarations: &mut SourcePropertyDeclaration, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>1579 fn parse_into<'i, 't>(
1580 &self,
1581 declarations: &mut SourcePropertyDeclaration,
1582 context: &ParserContext,
1583 input: &mut Parser<'i, 't>,
1584 ) -> Result<(), ParseError<'i>> {
1585 type ParseIntoFn = for<'i, 't> fn(
1586 declarations: &mut SourcePropertyDeclaration,
1587 context: &ParserContext,
1588 input: &mut Parser<'i, 't>,
1589 ) -> Result<(), ParseError<'i>>;
1590
1591 fn parse_all<'i, 't>(
1592 _: &mut SourcePropertyDeclaration,
1593 _: &ParserContext,
1594 input: &mut Parser<'i, 't>
1595 ) -> Result<(), ParseError<'i>> {
1596 // 'all' accepts no value other than CSS-wide keywords
1597 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1598 }
1599
1600
1601 static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
1602 % for shorthand in data.shorthands:
1603 % if shorthand.ident == "all":
1604 parse_all,
1605 % else:
1606 shorthands::${shorthand.ident}::parse_into,
1607 % endif
1608 % endfor
1609 ];
1610
1611 (PARSE_INTO[*self as usize])(declarations, context, input)
1612 }
1613 }
1614
1615 /// An unparsed property value that contains `var()` functions.
1616 #[derive(Debug, Eq, PartialEq, ToShmem)]
1617 pub struct UnparsedValue {
1618 /// The css serialization for this value.
1619 css: String,
1620 /// The first token type for this serialization.
1621 first_token_type: TokenSerializationType,
1622 /// The url data for resolving url values.
1623 url_data: UrlExtraData,
1624 /// The shorthand this came from.
1625 from_shorthand: Option<ShorthandId>,
1626 }
1627
1628 impl ToCss for UnparsedValue {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1629 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1630 where
1631 W: Write,
1632 {
1633 // https://drafts.csswg.org/css-variables/#variables-in-shorthands
1634 if self.from_shorthand.is_none() {
1635 dest.write_str(&*self.css)?;
1636 }
1637 Ok(())
1638 }
1639 }
1640
1641 /// A simple cache for properties that come from a shorthand and have variable
1642 /// references.
1643 ///
1644 /// This cache works because of the fact that you can't have competing values
1645 /// for a given longhand coming from the same shorthand (but note that this is
1646 /// why the shorthand needs to be part of the cache key).
1647 pub type ShorthandsWithPropertyReferencesCache =
1648 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1649
1650 impl UnparsedValue {
substitute_variables<'cache>( &self, longhand_id: LonghandId, writing_mode: WritingMode, custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>, quirks_mode: QuirksMode, device: &Device, shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache, ) -> Cow<'cache, PropertyDeclaration>1651 fn substitute_variables<'cache>(
1652 &self,
1653 longhand_id: LonghandId,
1654 writing_mode: WritingMode,
1655 custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
1656 quirks_mode: QuirksMode,
1657 device: &Device,
1658 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1659 ) -> Cow<'cache, PropertyDeclaration> {
1660 let invalid_at_computed_value_time = || {
1661 let keyword = if longhand_id.inherited() {
1662 CSSWideKeyword::Inherit
1663 } else {
1664 CSSWideKeyword::Initial
1665 };
1666 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1667 };
1668
1669 if let Some(shorthand_id) = self.from_shorthand {
1670 let key = (shorthand_id, longhand_id);
1671 if shorthand_cache.contains_key(&key) {
1672 // FIXME: This double lookup should be avoidable, but rustc
1673 // doesn't like that, see:
1674 //
1675 // https://github.com/rust-lang/rust/issues/82146
1676 return Cow::Borrowed(&shorthand_cache[&key]);
1677 }
1678 }
1679
1680 let css = match crate::custom_properties::substitute(
1681 &self.css,
1682 self.first_token_type,
1683 custom_properties,
1684 device,
1685 ) {
1686 Ok(css) => css,
1687 Err(..) => return invalid_at_computed_value_time(),
1688 };
1689
1690 // As of this writing, only the base URL is used for property
1691 // values.
1692 //
1693 // NOTE(emilio): we intentionally pase `None` as the rule type here.
1694 // If something starts depending on it, it's probably a bug, since
1695 // it'd change how values are parsed depending on whether we're in a
1696 // @keyframes rule or not, for example... So think twice about
1697 // whether you want to do this!
1698 //
1699 // FIXME(emilio): ParsingMode is slightly fishy...
1700 let context = ParserContext::new(
1701 Origin::Author,
1702 &self.url_data,
1703 None,
1704 ParsingMode::DEFAULT,
1705 quirks_mode,
1706 None,
1707 None,
1708 );
1709
1710 let mut input = ParserInput::new(&css);
1711 let mut input = Parser::new(&mut input);
1712 input.skip_whitespace();
1713
1714 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1715 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1716 }
1717
1718 let shorthand = match self.from_shorthand {
1719 None => {
1720 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input)) {
1721 Ok(decl) => Cow::Owned(decl),
1722 Err(..) => invalid_at_computed_value_time(),
1723 }
1724 },
1725 Some(shorthand) => shorthand,
1726 };
1727
1728 let mut decls = SourcePropertyDeclaration::new();
1729 // parse_into takes care of doing `parse_entirely` for us.
1730 if shorthand.parse_into(&mut decls, &context, &mut input).is_err() {
1731 return invalid_at_computed_value_time();
1732 }
1733
1734 for declaration in decls.declarations.drain(..) {
1735 let longhand = declaration.id().as_longhand().unwrap();
1736 if longhand.is_logical() {
1737 shorthand_cache.insert((shorthand, longhand.to_physical(writing_mode)), declaration.clone());
1738 }
1739 shorthand_cache.insert((shorthand, longhand), declaration);
1740 }
1741
1742 let key = (shorthand, longhand_id);
1743 match shorthand_cache.get(&key) {
1744 Some(decl) => Cow::Borrowed(decl),
1745 None => {
1746 // FIXME: We should always have the key here but it seems
1747 // sometimes we don't, see bug 1696409.
1748 #[cfg(feature = "gecko")]
1749 {
1750 if structs::GECKO_IS_NIGHTLY {
1751 panic!("Expected {:?} to be in the cache but it was not!", key);
1752 }
1753 }
1754 invalid_at_computed_value_time()
1755 }
1756 }
1757 }
1758 }
1759
1760 /// An identifier for a given property declaration, which can be either a
1761 /// longhand or a custom property.
1762 #[derive(Clone, Copy, Debug, PartialEq)]
1763 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
1764 pub enum PropertyDeclarationId<'a> {
1765 /// A longhand.
1766 Longhand(LonghandId),
1767 /// A custom property declaration.
1768 Custom(&'a crate::custom_properties::Name),
1769 }
1770
1771 impl<'a> ToCss for PropertyDeclarationId<'a> {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1772 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1773 where
1774 W: Write,
1775 {
1776 match *self {
1777 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1778 PropertyDeclarationId::Custom(ref name) => {
1779 dest.write_str("--")?;
1780 serialize_atom_name(name, dest)
1781 }
1782 }
1783 }
1784 }
1785
1786 impl<'a> PropertyDeclarationId<'a> {
1787 /// Whether a given declaration id is either the same as `other`, or a
1788 /// longhand of it.
is_or_is_longhand_of(&self, other: &PropertyId) -> bool1789 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1790 match *self {
1791 PropertyDeclarationId::Longhand(id) => {
1792 match *other {
1793 PropertyId::Longhand(other_id) |
1794 PropertyId::LonghandAlias(other_id, _) => id == other_id,
1795 PropertyId::Shorthand(shorthand) |
1796 PropertyId::ShorthandAlias(shorthand, _) => self.is_longhand_of(shorthand),
1797 PropertyId::Custom(_) => false,
1798 }
1799 }
1800 PropertyDeclarationId::Custom(name) => {
1801 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1802 }
1803 }
1804 }
1805
1806 /// Whether a given declaration id is a longhand belonging to this
1807 /// shorthand.
is_longhand_of(&self, shorthand: ShorthandId) -> bool1808 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1809 match *self {
1810 PropertyDeclarationId::Longhand(ref id) => id.shorthands().any(|s| s == shorthand),
1811 _ => false,
1812 }
1813 }
1814
1815 /// Returns the name of the property without CSS escaping.
name(&self) -> Cow<'static, str>1816 pub fn name(&self) -> Cow<'static, str> {
1817 match *self {
1818 PropertyDeclarationId::Longhand(id) => id.name().into(),
1819 PropertyDeclarationId::Custom(name) => {
1820 let mut s = String::new();
1821 write!(&mut s, "--{}", name).unwrap();
1822 s.into()
1823 }
1824 }
1825 }
1826
1827 /// Returns longhand id if it is, None otherwise.
1828 #[inline]
as_longhand(&self) -> Option<LonghandId>1829 pub fn as_longhand(&self) -> Option<LonghandId> {
1830 match *self {
1831 PropertyDeclarationId::Longhand(id) => Some(id),
1832 _ => None,
1833 }
1834 }
1835 }
1836
1837 /// Servo's representation of a CSS property, that is, either a longhand, a
1838 /// shorthand, or a custom property.
1839 #[derive(Clone, Eq, PartialEq)]
1840 pub enum PropertyId {
1841 /// A longhand property.
1842 Longhand(LonghandId),
1843 /// A shorthand property.
1844 Shorthand(ShorthandId),
1845 /// An alias for a longhand property.
1846 LonghandAlias(LonghandId, AliasId),
1847 /// An alias for a shorthand property.
1848 ShorthandAlias(ShorthandId, AliasId),
1849 /// A custom property.
1850 Custom(crate::custom_properties::Name),
1851 }
1852
1853 impl fmt::Debug for PropertyId {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result1854 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1855 self.to_css(&mut CssWriter::new(formatter))
1856 }
1857 }
1858
1859 impl ToCss for PropertyId {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1860 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1861 where
1862 W: Write,
1863 {
1864 match *self {
1865 PropertyId::Longhand(id) => dest.write_str(id.name()),
1866 PropertyId::Shorthand(id) => dest.write_str(id.name()),
1867 PropertyId::LonghandAlias(id, _) => dest.write_str(id.name()),
1868 PropertyId::ShorthandAlias(id, _) => dest.write_str(id.name()),
1869 PropertyId::Custom(ref name) => {
1870 dest.write_str("--")?;
1871 serialize_atom_name(name, dest)
1872 }
1873 }
1874 }
1875 }
1876
1877 /// The counted unknown property list which is used for css use counters.
1878 ///
1879 /// FIXME: This should be just #[repr(u8)], but can't be because of ABI issues,
1880 /// see https://bugs.llvm.org/show_bug.cgi?id=44228.
1881 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)]
1882 #[repr(u32)]
1883 pub enum CountedUnknownProperty {
1884 % for prop in data.counted_unknown_properties:
1885 /// ${prop.name}
1886 ${prop.camel_case},
1887 % endfor
1888 }
1889
1890 impl CountedUnknownProperty {
1891 /// Parse the counted unknown property, for testing purposes only.
parse_for_testing(property_name: &str) -> Option<Self>1892 pub fn parse_for_testing(property_name: &str) -> Option<Self> {
1893 ascii_case_insensitive_phf_map! {
1894 unknown_id -> CountedUnknownProperty = {
1895 % for property in data.counted_unknown_properties:
1896 "${property.name}" => CountedUnknownProperty::${property.camel_case},
1897 % endfor
1898 }
1899 }
1900 unknown_id(property_name).cloned()
1901 }
1902
1903 /// Returns the underlying index, used for use counter.
1904 #[inline]
bit(self) -> usize1905 pub fn bit(self) -> usize {
1906 self as usize
1907 }
1908 }
1909
1910 impl PropertyId {
1911 /// Return the longhand id that this property id represents.
1912 #[inline]
longhand_id(&self) -> Option<LonghandId>1913 pub fn longhand_id(&self) -> Option<LonghandId> {
1914 Some(match *self {
1915 PropertyId::Longhand(id) => id,
1916 PropertyId::LonghandAlias(id, _) => id,
1917 _ => return None,
1918 })
1919 }
1920
1921 /// Returns a given property from the given name, _regardless of whether it
1922 /// is enabled or not_, or Err(()) for unknown properties.
1923 ///
1924 /// Do not use for non-testing purposes.
parse_unchecked_for_testing(name: &str) -> Result<Self, ()>1925 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
1926 Self::parse_unchecked(name, None)
1927 }
1928
1929 /// Returns a given property from the given name, _regardless of whether it
1930 /// is enabled or not_, or Err(()) for unknown properties.
parse_unchecked( property_name: &str, use_counters: Option< &UseCounters>, ) -> Result<Self, ()>1931 fn parse_unchecked(
1932 property_name: &str,
1933 use_counters: Option< &UseCounters>,
1934 ) -> Result<Self, ()> {
1935 // A special id for css use counters.
1936 // ShorthandAlias is not used in the Servo build.
1937 // That's why we need to allow dead_code.
1938 #[allow(dead_code)]
1939 pub enum StaticId {
1940 Longhand(LonghandId),
1941 Shorthand(ShorthandId),
1942 LonghandAlias(LonghandId, AliasId),
1943 ShorthandAlias(ShorthandId, AliasId),
1944 CountedUnknown(CountedUnknownProperty),
1945 }
1946 ascii_case_insensitive_phf_map! {
1947 static_id -> StaticId = {
1948 % for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
1949 % for property in properties:
1950 "${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}),
1951 % for alias in property.aliases:
1952 "${alias.name}" => {
1953 StaticId::${kind}Alias(
1954 ${kind}Id::${property.camel_case},
1955 AliasId::${alias.camel_case},
1956 )
1957 },
1958 % endfor
1959 % endfor
1960 % endfor
1961 % for property in data.counted_unknown_properties:
1962 "${property.name}" => {
1963 StaticId::CountedUnknown(CountedUnknownProperty::${property.camel_case})
1964 },
1965 % endfor
1966 }
1967 }
1968
1969 if let Some(id) = static_id(property_name) {
1970 return Ok(match *id {
1971 StaticId::Longhand(id) => PropertyId::Longhand(id),
1972 StaticId::Shorthand(id) => {
1973 #[cfg(feature = "gecko")]
1974 {
1975 // We want to count `zoom` even if disabled.
1976 if matches!(id, ShorthandId::Zoom) {
1977 if let Some(counters) = use_counters {
1978 counters.non_custom_properties.record(id.into());
1979 }
1980 }
1981 }
1982
1983 PropertyId::Shorthand(id)
1984 },
1985 StaticId::LonghandAlias(id, alias) => PropertyId::LonghandAlias(id, alias),
1986 StaticId::ShorthandAlias(id, alias) => PropertyId::ShorthandAlias(id, alias),
1987 StaticId::CountedUnknown(unknown_prop) => {
1988 if let Some(counters) = use_counters {
1989 counters.counted_unknown_properties.record(unknown_prop);
1990 }
1991
1992 // Always return Err(()) because these aren't valid custom property names.
1993 return Err(());
1994 }
1995 });
1996 }
1997
1998 let name = crate::custom_properties::parse_name(property_name)?;
1999 Ok(PropertyId::Custom(crate::custom_properties::Name::from(name)))
2000 }
2001
2002 /// Parses a property name, and returns an error if it's unknown or isn't
2003 /// enabled for all content.
2004 #[inline]
parse_enabled_for_all_content(name: &str) -> Result<Self, ()>2005 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
2006 let id = Self::parse_unchecked(name, None)?;
2007
2008 if !id.enabled_for_all_content() {
2009 return Err(());
2010 }
2011
2012 Ok(id)
2013 }
2014
2015
2016 /// Parses a property name, and returns an error if it's unknown or isn't
2017 /// allowed in this context.
2018 #[inline]
parse(name: &str, context: &ParserContext) -> Result<Self, ()>2019 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
2020 let id = Self::parse_unchecked(name, context.use_counters)?;
2021
2022 if !id.allowed_in(context) {
2023 return Err(());
2024 }
2025
2026 Ok(id)
2027 }
2028
2029 /// Parses a property name, and returns an error if it's unknown or isn't
2030 /// allowed in this context, ignoring the rule_type checks.
2031 ///
2032 /// This is useful for parsing stuff from CSS values, for example.
2033 #[inline]
parse_ignoring_rule_type( name: &str, context: &ParserContext, ) -> Result<Self, ()>2034 pub fn parse_ignoring_rule_type(
2035 name: &str,
2036 context: &ParserContext,
2037 ) -> Result<Self, ()> {
2038 let id = Self::parse_unchecked(name, None)?;
2039
2040 if !id.allowed_in_ignoring_rule_type(context) {
2041 return Err(());
2042 }
2043
2044 Ok(id)
2045 }
2046
2047 /// Returns a property id from Gecko's nsCSSPropertyID.
2048 #[cfg(feature = "gecko")]
2049 #[allow(non_upper_case_globals)]
2050 #[inline]
from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()>2051 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
2052 Ok(NonCustomPropertyId::from_nscsspropertyid(id)?.to_property_id())
2053 }
2054
2055 /// Returns true if the property is a shorthand or shorthand alias.
2056 #[inline]
is_shorthand(&self) -> bool2057 pub fn is_shorthand(&self) -> bool {
2058 self.as_shorthand().is_ok()
2059 }
2060
2061 /// Given this property id, get it either as a shorthand or as a
2062 /// `PropertyDeclarationId`.
as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId>2063 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId> {
2064 match *self {
2065 PropertyId::ShorthandAlias(id, _) |
2066 PropertyId::Shorthand(id) => Ok(id),
2067 PropertyId::LonghandAlias(id, _) |
2068 PropertyId::Longhand(id) => Err(PropertyDeclarationId::Longhand(id)),
2069 PropertyId::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
2070 }
2071 }
2072
2073 /// Returns the `NonCustomPropertyId` corresponding to this property id.
non_custom_id(&self) -> Option<NonCustomPropertyId>2074 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
2075 Some(match *self {
2076 PropertyId::Custom(_) => return None,
2077 PropertyId::Shorthand(shorthand_id) => shorthand_id.into(),
2078 PropertyId::Longhand(longhand_id) => longhand_id.into(),
2079 PropertyId::ShorthandAlias(_, alias_id) => alias_id.into(),
2080 PropertyId::LonghandAlias(_, alias_id) => alias_id.into(),
2081 })
2082 }
2083
2084 /// Returns non-alias NonCustomPropertyId corresponding to this
2085 /// property id.
non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId>2086 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
2087 Some(match *self {
2088 PropertyId::Custom(_) => return None,
2089 PropertyId::Shorthand(id) => id.into(),
2090 PropertyId::Longhand(id) => id.into(),
2091 PropertyId::ShorthandAlias(id, _) => id.into(),
2092 PropertyId::LonghandAlias(id, _) => id.into(),
2093 })
2094 }
2095
2096 /// Whether the property is enabled for all content regardless of the
2097 /// stylesheet it was declared on (that is, in practice only checks prefs).
2098 #[inline]
enabled_for_all_content(&self) -> bool2099 pub fn enabled_for_all_content(&self) -> bool {
2100 let id = match self.non_custom_id() {
2101 // Custom properties are allowed everywhere
2102 None => return true,
2103 Some(id) => id,
2104 };
2105
2106 id.enabled_for_all_content()
2107 }
2108
2109 /// Converts this PropertyId in nsCSSPropertyID, resolving aliases to the
2110 /// resolved property, and returning eCSSPropertyExtra_variable for custom
2111 /// properties.
2112 #[cfg(feature = "gecko")]
2113 #[inline]
to_nscsspropertyid_resolving_aliases(&self) -> nsCSSPropertyID2114 pub fn to_nscsspropertyid_resolving_aliases(&self) -> nsCSSPropertyID {
2115 match self.non_custom_non_alias_id() {
2116 Some(id) => id.to_nscsspropertyid(),
2117 None => nsCSSPropertyID::eCSSPropertyExtra_variable,
2118 }
2119 }
2120
allowed_in(&self, context: &ParserContext) -> bool2121 fn allowed_in(&self, context: &ParserContext) -> bool {
2122 let id = match self.non_custom_id() {
2123 // Custom properties are allowed everywhere
2124 None => return true,
2125 Some(id) => id,
2126 };
2127 id.allowed_in(context)
2128 }
2129
2130 #[inline]
allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool2131 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
2132 let id = match self.non_custom_id() {
2133 // Custom properties are allowed everywhere
2134 None => return true,
2135 Some(id) => id,
2136 };
2137 id.allowed_in_ignoring_rule_type(context)
2138 }
2139
2140 /// Whether the property supports the given CSS type.
2141 /// `ty` should a bitflags of constants in style_traits::CssType.
supports_type(&self, ty: u8) -> bool2142 pub fn supports_type(&self, ty: u8) -> bool {
2143 let id = self.non_custom_non_alias_id();
2144 id.map_or(0, |id| id.supported_types()) & ty != 0
2145 }
2146
2147 /// Collect supported starting word of values of this property.
2148 ///
2149 /// See style_traits::SpecifiedValueInfo::collect_completion_keywords for more
2150 /// details.
collect_property_completion_keywords(&self, f: KeywordsCollectFn)2151 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
2152 if let Some(id) = self.non_custom_non_alias_id() {
2153 id.collect_property_completion_keywords(f);
2154 }
2155 CSSWideKeyword::collect_completion_keywords(f);
2156 }
2157 }
2158
2159 /// A declaration using a CSS-wide keyword.
2160 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2161 #[derive(Clone, PartialEq, ToCss, ToShmem)]
2162 pub struct WideKeywordDeclaration {
2163 #[css(skip)]
2164 id: LonghandId,
2165 /// The CSS-wide keyword.
2166 pub keyword: CSSWideKeyword,
2167 }
2168
2169 /// An unparsed declaration that contains `var()` functions.
2170 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2171 #[derive(Clone, PartialEq, ToCss, ToShmem)]
2172 pub struct VariableDeclaration {
2173 #[css(skip)]
2174 id: LonghandId,
2175 #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
2176 value: Arc<UnparsedValue>,
2177 }
2178
2179 /// A custom property declaration value is either an unparsed value or a CSS
2180 /// wide-keyword.
2181 #[derive(Clone, PartialEq, ToCss, ToShmem)]
2182 pub enum CustomDeclarationValue {
2183 /// A value.
2184 Value(Arc<crate::custom_properties::SpecifiedValue>),
2185 /// A wide keyword.
2186 CSSWideKeyword(CSSWideKeyword),
2187 }
2188
2189 /// A custom property declaration with the property name and the declared value.
2190 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2191 #[derive(Clone, PartialEq, ToCss, ToShmem)]
2192 pub struct CustomDeclaration {
2193 /// The name of the custom property.
2194 #[css(skip)]
2195 pub name: crate::custom_properties::Name,
2196 /// The value of the custom property.
2197 #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
2198 pub value: CustomDeclarationValue,
2199 }
2200
2201 impl fmt::Debug for PropertyDeclaration {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result2202 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2203 self.id().to_css(&mut CssWriter::new(f))?;
2204 f.write_str(": ")?;
2205
2206 // Because PropertyDeclaration::to_css requires CssStringWriter, we can't write
2207 // it directly to f, and need to allocate an intermediate string. This is
2208 // fine for debug-only code.
2209 let mut s = CssString::new();
2210 self.to_css(&mut s)?;
2211 write!(f, "{}", s)
2212 }
2213 }
2214
2215 impl PropertyDeclaration {
2216 /// Given a property declaration, return the property declaration id.
2217 #[inline]
id(&self) -> PropertyDeclarationId2218 pub fn id(&self) -> PropertyDeclarationId {
2219 match *self {
2220 PropertyDeclaration::Custom(ref declaration) => {
2221 return PropertyDeclarationId::Custom(&declaration.name)
2222 }
2223 PropertyDeclaration::CSSWideKeyword(ref declaration) => {
2224 return PropertyDeclarationId::Longhand(declaration.id);
2225 }
2226 PropertyDeclaration::WithVariables(ref declaration) => {
2227 return PropertyDeclarationId::Longhand(declaration.id);
2228 }
2229 _ => {}
2230 }
2231 // This is just fine because PropertyDeclaration and LonghandId
2232 // have corresponding discriminants.
2233 let id = unsafe { *(self as *const _ as *const LonghandId) };
2234 debug_assert_eq!(id, match *self {
2235 % for property in data.longhands:
2236 PropertyDeclaration::${property.camel_case}(..) => LonghandId::${property.camel_case},
2237 % endfor
2238 _ => id,
2239 });
2240 PropertyDeclarationId::Longhand(id)
2241 }
2242
2243 /// Given a declaration, convert it into a declaration for a corresponding
2244 /// physical property.
2245 #[inline]
to_physical(&self, wm: WritingMode) -> Self2246 pub fn to_physical(&self, wm: WritingMode) -> Self {
2247 match *self {
2248 PropertyDeclaration::WithVariables(VariableDeclaration {
2249 id,
2250 ref value,
2251 }) => {
2252 return PropertyDeclaration::WithVariables(VariableDeclaration {
2253 id: id.to_physical(wm),
2254 value: value.clone(),
2255 })
2256 }
2257 PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
2258 id,
2259 keyword,
2260 }) => {
2261 return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
2262 id: id.to_physical(wm),
2263 keyword,
2264 })
2265 }
2266 PropertyDeclaration::Custom(..) => return self.clone(),
2267 % for prop in data.longhands:
2268 PropertyDeclaration::${prop.camel_case}(..) => {},
2269 % endfor
2270 }
2271
2272 let mut ret = self.clone();
2273
2274 % for prop in data.longhands:
2275 % for physical_property in prop.all_physical_mapped_properties(data):
2276 % if physical_property.specified_type() != prop.specified_type():
2277 <% raise "Logical property %s should share specified value with physical property %s" % \
2278 (prop.name, physical_property.name) %>
2279 % endif
2280 % endfor
2281 % endfor
2282
2283 unsafe {
2284 let longhand_id = *(&mut ret as *mut _ as *mut LonghandId);
2285
2286 debug_assert_eq!(
2287 PropertyDeclarationId::Longhand(longhand_id),
2288 ret.id()
2289 );
2290
2291 // This is just fine because PropertyDeclaration and LonghandId
2292 // have corresponding discriminants.
2293 *(&mut ret as *mut _ as *mut LonghandId) = longhand_id.to_physical(wm);
2294
2295 debug_assert_eq!(
2296 PropertyDeclarationId::Longhand(longhand_id.to_physical(wm)),
2297 ret.id()
2298 );
2299 }
2300
2301 ret
2302 }
2303
with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str>2304 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str> {
2305 match *self {
2306 PropertyDeclaration::WithVariables(ref declaration) => {
2307 let s = declaration.value.from_shorthand?;
2308 if s != shorthand {
2309 return None;
2310 }
2311 Some(&*declaration.value.css)
2312 },
2313 _ => None,
2314 }
2315 }
2316
2317 /// Returns a CSS-wide keyword declaration for a given property.
2318 #[inline]
css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self2319 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
2320 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
2321 }
2322
2323 /// Returns a CSS-wide keyword if the declaration's value is one.
2324 #[inline]
get_css_wide_keyword(&self) -> Option<CSSWideKeyword>2325 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
2326 match *self {
2327 PropertyDeclaration::CSSWideKeyword(ref declaration) => {
2328 Some(declaration.keyword)
2329 },
2330 _ => None,
2331 }
2332 }
2333
2334 /// Returns whether or not the property is set by a system font
get_system(&self) -> Option<SystemFont>2335 pub fn get_system(&self) -> Option<SystemFont> {
2336 match *self {
2337 % if engine == "gecko":
2338 % for prop in SYSTEM_FONT_LONGHANDS:
2339 PropertyDeclaration::${to_camel_case(prop)}(ref prop) => {
2340 prop.get_system()
2341 }
2342 % endfor
2343 % endif
2344 _ => None,
2345 }
2346 }
2347
2348 /// Is it the default value of line-height?
is_default_line_height(&self) -> bool2349 pub fn is_default_line_height(&self) -> bool {
2350 match *self {
2351 PropertyDeclaration::LineHeight(LineHeight::Normal) => true,
2352 _ => false
2353 }
2354 }
2355
2356 /// Returns whether the declaration may be serialized as part of a shorthand.
2357 ///
2358 /// This method returns false if this declaration contains variable or has a
2359 /// CSS-wide keyword value, since these values cannot be serialized as part
2360 /// of a shorthand.
2361 ///
2362 /// Caller should check `with_variables_from_shorthand()` and whether all
2363 /// needed declarations has the same CSS-wide keyword first.
2364 ///
2365 /// Note that, serialization of a shorthand may still fail because of other
2366 /// property-specific requirement even when this method returns true for all
2367 /// the longhand declarations.
may_serialize_as_part_of_shorthand(&self) -> bool2368 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
2369 match *self {
2370 PropertyDeclaration::CSSWideKeyword(..) |
2371 PropertyDeclaration::WithVariables(..) => false,
2372 PropertyDeclaration::Custom(..) =>
2373 unreachable!("Serializing a custom property as part of shorthand?"),
2374 _ => true,
2375 }
2376 }
2377
2378 /// Return whether the value is stored as it was in the CSS source,
2379 /// preserving whitespace (as opposed to being parsed into a more abstract
2380 /// data structure).
2381 ///
2382 /// This is the case of custom properties and values that contain
2383 /// unsubstituted variables.
value_is_unparsed(&self) -> bool2384 pub fn value_is_unparsed(&self) -> bool {
2385 match *self {
2386 PropertyDeclaration::WithVariables(..) => true,
2387 PropertyDeclaration::Custom(ref declaration) => {
2388 matches!(declaration.value, CustomDeclarationValue::Value(..))
2389 }
2390 _ => false,
2391 }
2392 }
2393
2394 /// Returns true if this property declaration is for one of the animatable
2395 /// properties.
is_animatable(&self) -> bool2396 pub fn is_animatable(&self) -> bool {
2397 match self.id() {
2398 PropertyDeclarationId::Longhand(id) => id.is_animatable(),
2399 PropertyDeclarationId::Custom(..) => false,
2400 }
2401 }
2402
2403 /// Returns true if this property is a custom property, false
2404 /// otherwise.
is_custom(&self) -> bool2405 pub fn is_custom(&self) -> bool {
2406 matches!(*self, PropertyDeclaration::Custom(..))
2407 }
2408
2409 /// The `context` parameter controls this:
2410 ///
2411 /// <https://drafts.csswg.org/css-animations/#keyframes>
2412 /// > The <declaration-list> inside of <keyframe-block> accepts any CSS property
2413 /// > except those defined in this specification,
2414 /// > but does accept the `animation-play-state` property and interprets it specially.
2415 ///
2416 /// This will not actually parse Importance values, and will always set things
2417 /// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
2418 /// we only set them here so that we don't have to reallocate
parse_into<'i, 't>( declarations: &mut SourcePropertyDeclaration, id: PropertyId, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>2419 pub fn parse_into<'i, 't>(
2420 declarations: &mut SourcePropertyDeclaration,
2421 id: PropertyId,
2422 context: &ParserContext,
2423 input: &mut Parser<'i, 't>,
2424 ) -> Result<(), ParseError<'i>> {
2425 assert!(declarations.is_empty());
2426 debug_assert!(id.allowed_in(context), "{:?}", id);
2427
2428 let non_custom_id = id.non_custom_id();
2429 input.skip_whitespace();
2430
2431 let start = input.state();
2432 match id {
2433 PropertyId::Custom(property_name) => {
2434 let value = match input.try_parse(CSSWideKeyword::parse) {
2435 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
2436 Err(()) => CustomDeclarationValue::Value(
2437 crate::custom_properties::SpecifiedValue::parse(input)?
2438 ),
2439 };
2440 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
2441 name: property_name,
2442 value,
2443 }));
2444 return Ok(());
2445 }
2446 PropertyId::LonghandAlias(id, _) |
2447 PropertyId::Longhand(id) => {
2448 input.try_parse(CSSWideKeyword::parse).map(|keyword| {
2449 PropertyDeclaration::css_wide_keyword(id, keyword)
2450 }).or_else(|()| {
2451 input.look_for_var_or_env_functions();
2452 input.parse_entirely(|input| id.parse_value(context, input))
2453 .or_else(|err| {
2454 while let Ok(_) = input.next() {} // Look for var() after the error.
2455 if !input.seen_var_or_env_functions() {
2456 return Err(err);
2457 }
2458 input.reset(&start);
2459 let (first_token_type, css) =
2460 crate::custom_properties::parse_non_custom_with_var(input)?;
2461 Ok(PropertyDeclaration::WithVariables(VariableDeclaration {
2462 id,
2463 value: Arc::new(UnparsedValue {
2464 css: css.into_owned(),
2465 first_token_type,
2466 url_data: context.url_data.clone(),
2467 from_shorthand: None,
2468 }),
2469 }))
2470 })
2471 }).map(|declaration| {
2472 declarations.push(declaration)
2473 })?;
2474 }
2475 PropertyId::ShorthandAlias(id, _) |
2476 PropertyId::Shorthand(id) => {
2477 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
2478 if id == ShorthandId::All {
2479 declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
2480 } else {
2481 for longhand in id.longhands() {
2482 declarations.push(PropertyDeclaration::css_wide_keyword(longhand, keyword));
2483 }
2484 }
2485 } else {
2486 input.look_for_var_or_env_functions();
2487 // Not using parse_entirely here: each
2488 // ${shorthand.ident}::parse_into function needs to do so
2489 // *before* pushing to `declarations`.
2490 id.parse_into(declarations, context, input).or_else(|err| {
2491 while let Ok(_) = input.next() {} // Look for var() after the error.
2492 if !input.seen_var_or_env_functions() {
2493 return Err(err);
2494 }
2495
2496 input.reset(&start);
2497 let (first_token_type, css) =
2498 crate::custom_properties::parse_non_custom_with_var(input)?;
2499 let unparsed = Arc::new(UnparsedValue {
2500 css: css.into_owned(),
2501 first_token_type,
2502 url_data: context.url_data.clone(),
2503 from_shorthand: Some(id),
2504 });
2505 if id == ShorthandId::All {
2506 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
2507 } else {
2508 for id in id.longhands() {
2509 declarations.push(
2510 PropertyDeclaration::WithVariables(VariableDeclaration {
2511 id,
2512 value: unparsed.clone(),
2513 })
2514 )
2515 }
2516 }
2517 Ok(())
2518 })?;
2519 }
2520 }
2521 }
2522 debug_assert!(non_custom_id.is_some(), "Custom properties should've returned earlier");
2523 if let Some(use_counters) = context.use_counters {
2524 use_counters.non_custom_properties.record(non_custom_id.unwrap());
2525 }
2526 Ok(())
2527 }
2528 }
2529
2530 type SubpropertiesArray<T> =
2531 [T; ${max(len(s.sub_properties) for s in data.shorthands_except_all()) \
2532 if data.shorthands_except_all() else 0}];
2533
2534 type SubpropertiesVec<T> = ArrayVec<SubpropertiesArray<T>>;
2535
2536 /// A stack-allocated vector of `PropertyDeclaration`
2537 /// large enough to parse one CSS `key: value` declaration.
2538 /// (Shorthands expand to multiple `PropertyDeclaration`s.)
2539 pub struct SourcePropertyDeclaration {
2540 declarations: SubpropertiesVec<PropertyDeclaration>,
2541
2542 /// Stored separately to keep SubpropertiesVec smaller.
2543 all_shorthand: AllShorthand,
2544 }
2545
2546 impl SourcePropertyDeclaration {
2547 /// Create one. It’s big, try not to move it around.
2548 #[inline]
new() -> Self2549 pub fn new() -> Self {
2550 SourcePropertyDeclaration {
2551 declarations: ::arrayvec::ArrayVec::new(),
2552 all_shorthand: AllShorthand::NotSet,
2553 }
2554 }
2555
2556 /// Create one with a single PropertyDeclaration.
2557 #[inline]
with_one(decl: PropertyDeclaration) -> Self2558 pub fn with_one(decl: PropertyDeclaration) -> Self {
2559 let mut result = Self::new();
2560 result.declarations.push(decl);
2561 result
2562 }
2563
2564 /// Similar to Vec::drain: leaves this empty when the return value is dropped.
drain(&mut self) -> SourcePropertyDeclarationDrain2565 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain {
2566 SourcePropertyDeclarationDrain {
2567 declarations: self.declarations.drain(..),
2568 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
2569 }
2570 }
2571
2572 /// Reset to initial state
clear(&mut self)2573 pub fn clear(&mut self) {
2574 self.declarations.clear();
2575 self.all_shorthand = AllShorthand::NotSet;
2576 }
2577
is_empty(&self) -> bool2578 fn is_empty(&self) -> bool {
2579 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
2580 }
2581
push(&mut self, declaration: PropertyDeclaration)2582 fn push(&mut self, declaration: PropertyDeclaration) {
2583 let _result = self.declarations.try_push(declaration);
2584 debug_assert!(_result.is_ok());
2585 }
2586 }
2587
2588 /// Return type of SourcePropertyDeclaration::drain
2589 pub struct SourcePropertyDeclarationDrain<'a> {
2590 declarations: ArrayVecDrain<'a, SubpropertiesArray<PropertyDeclaration>>,
2591 all_shorthand: AllShorthand,
2592 }
2593
2594 enum AllShorthand {
2595 NotSet,
2596 CSSWideKeyword(CSSWideKeyword),
2597 WithVariables(Arc<UnparsedValue>)
2598 }
2599
2600 impl AllShorthand {
2601 /// Iterates property declarations from the given all shorthand value.
2602 #[inline]
declarations(&self) -> AllShorthandDeclarationIterator2603 fn declarations(&self) -> AllShorthandDeclarationIterator {
2604 AllShorthandDeclarationIterator {
2605 all_shorthand: self,
2606 longhands: ShorthandId::All.longhands(),
2607 }
2608 }
2609 }
2610
2611 struct AllShorthandDeclarationIterator<'a> {
2612 all_shorthand: &'a AllShorthand,
2613 longhands: NonCustomPropertyIterator<LonghandId>,
2614 }
2615
2616 impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
2617 type Item = PropertyDeclaration;
2618
2619 #[inline]
next(&mut self) -> Option<Self::Item>2620 fn next(&mut self) -> Option<Self::Item> {
2621 match *self.all_shorthand {
2622 AllShorthand::NotSet => None,
2623 AllShorthand::CSSWideKeyword(ref keyword) => {
2624 Some(PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword))
2625 }
2626 AllShorthand::WithVariables(ref unparsed) => {
2627 Some(PropertyDeclaration::WithVariables(
2628 VariableDeclaration {
2629 id: self.longhands.next()?,
2630 value: unparsed.clone()
2631 }
2632 ))
2633 }
2634 }
2635 }
2636 }
2637
2638 #[cfg(feature = "gecko")]
2639 pub use crate::gecko_properties::style_structs;
2640
2641 /// The module where all the style structs are defined.
2642 #[cfg(feature = "servo")]
2643 pub mod style_structs {
2644 use fxhash::FxHasher;
2645 use super::longhands;
2646 use std::hash::{Hash, Hasher};
2647 use crate::logical_geometry::WritingMode;
2648 use crate::media_queries::Device;
2649 use crate::values::computed::NonNegativeLength;
2650
2651 % for style_struct in data.active_style_structs():
2652 % if style_struct.name == "Font":
2653 #[derive(Clone, Debug, MallocSizeOf)]
2654 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
2655 % else:
2656 #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
2657 % endif
2658 /// The ${style_struct.name} style struct.
2659 pub struct ${style_struct.name} {
2660 % for longhand in style_struct.longhands:
2661 % if not longhand.logical:
2662 /// The ${longhand.name} computed value.
2663 pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
2664 % endif
2665 % endfor
2666 % if style_struct.name == "InheritedText":
2667 /// The "used" text-decorations that apply to this box.
2668 ///
2669 /// FIXME(emilio): This is technically a box-tree concept, and
2670 /// would be nice to move away from style.
2671 pub text_decorations_in_effect: crate::values::computed::text::TextDecorationsInEffect,
2672 % endif
2673 % if style_struct.name == "Font":
2674 /// The font hash, used for font caching.
2675 pub hash: u64,
2676 % endif
2677 % if style_struct.name == "Box":
2678 /// The display value specified by the CSS stylesheets (without any style adjustments),
2679 /// which is needed for hypothetical layout boxes.
2680 pub original_display: longhands::display::computed_value::T,
2681 % endif
2682 }
2683 % if style_struct.name == "Font":
2684 impl PartialEq for Font {
eq(&self, other: &Font) -> bool2685 fn eq(&self, other: &Font) -> bool {
2686 self.hash == other.hash
2687 % for longhand in style_struct.longhands:
2688 && self.${longhand.ident} == other.${longhand.ident}
2689 % endfor
2690 }
2691 }
2692 % endif
2693
2694 impl ${style_struct.name} {
2695 % for longhand in style_struct.longhands:
2696 % if longhand.logical:
2697 ${helpers.logical_setter(name=longhand.name)}
2698 % else:
2699 % if longhand.ident == "display":
2700 /// Set `display`.
2701 ///
2702 /// We need to keep track of the original display for hypothetical boxes,
2703 /// so we need to special-case this.
2704 #[allow(non_snake_case)]
2705 #[inline]
set_display(&mut self, v: longhands::display::computed_value::T)2706 pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
2707 self.display = v;
2708 self.original_display = v;
2709 }
2710 % else:
2711 /// Set ${longhand.name}.
2712 #[allow(non_snake_case)]
2713 #[inline]
2714 pub fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
2715 self.${longhand.ident} = v;
2716 }
2717 % endif
2718 % if longhand.ident == "display":
2719 /// Set `display` from other struct.
2720 ///
2721 /// Same as `set_display` above.
2722 /// Thus, we need to special-case this.
2723 #[allow(non_snake_case)]
2724 #[inline]
copy_display_from(&mut self, other: &Self)2725 pub fn copy_display_from(&mut self, other: &Self) {
2726 self.display = other.display.clone();
2727 self.original_display = other.display.clone();
2728 }
2729 % else:
2730 /// Set ${longhand.name} from other struct.
2731 #[allow(non_snake_case)]
2732 #[inline]
2733 pub fn copy_${longhand.ident}_from(&mut self, other: &Self) {
2734 self.${longhand.ident} = other.${longhand.ident}.clone();
2735 }
2736 % endif
2737 /// Reset ${longhand.name} from the initial struct.
2738 #[allow(non_snake_case)]
2739 #[inline]
2740 pub fn reset_${longhand.ident}(&mut self, other: &Self) {
2741 self.copy_${longhand.ident}_from(other)
2742 }
2743
2744 /// Get the computed value for ${longhand.name}.
2745 #[allow(non_snake_case)]
2746 #[inline]
2747 pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
2748 self.${longhand.ident}.clone()
2749 }
2750 % endif
2751 % if longhand.need_index:
2752 /// If this longhand is indexed, get the number of elements.
2753 #[allow(non_snake_case)]
2754 pub fn ${longhand.ident}_count(&self) -> usize {
2755 self.${longhand.ident}.0.len()
2756 }
2757
2758 /// If this longhand is indexed, get the element at given
2759 /// index.
2760 #[allow(non_snake_case)]
2761 pub fn ${longhand.ident}_at(&self, index: usize)
2762 -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
2763 self.${longhand.ident}.0[index].clone()
2764 }
2765 % endif
2766 % endfor
2767 % if style_struct.name == "Border":
2768 % for side in ["top", "right", "bottom", "left"]:
2769 /// Whether the border-${side} property has nonzero width.
2770 #[allow(non_snake_case)]
2771 pub fn border_${side}_has_nonzero_width(&self) -> bool {
2772 use crate::Zero;
2773 !self.border_${side}_width.is_zero()
2774 }
2775 % endfor
2776 % elif style_struct.name == "Font":
2777 /// Computes a font hash in order to be able to cache fonts
2778 /// effectively in GFX and layout.
compute_font_hash(&mut self)2779 pub fn compute_font_hash(&mut self) {
2780 // Corresponds to the fields in
2781 // `gfx::font_template::FontTemplateDescriptor`.
2782 let mut hasher: FxHasher = Default::default();
2783 self.font_weight.hash(&mut hasher);
2784 self.font_stretch.hash(&mut hasher);
2785 self.font_style.hash(&mut hasher);
2786 self.font_family.hash(&mut hasher);
2787 self.hash = hasher.finish()
2788 }
2789
2790 /// (Servo does not handle MathML, so this just calls copy_font_size_from)
inherit_font_size_from(&mut self, parent: &Self, _: Option<NonNegativeLength>, _: &Device)2791 pub fn inherit_font_size_from(&mut self, parent: &Self,
2792 _: Option<NonNegativeLength>,
2793 _: &Device) {
2794 self.copy_font_size_from(parent);
2795 }
2796 /// (Servo does not handle MathML, so this just calls set_font_size)
apply_font_size(&mut self, v: longhands::font_size::computed_value::T, _: &Self, _: &Device) -> Option<NonNegativeLength>2797 pub fn apply_font_size(&mut self,
2798 v: longhands::font_size::computed_value::T,
2799 _: &Self,
2800 _: &Device) -> Option<NonNegativeLength> {
2801 self.set_font_size(v);
2802 None
2803 }
2804 /// (Servo does not handle MathML, so this does nothing)
apply_unconstrained_font_size(&mut self, _: NonNegativeLength)2805 pub fn apply_unconstrained_font_size(&mut self, _: NonNegativeLength) {
2806 }
2807
2808 % elif style_struct.name == "Outline":
2809 /// Whether the outline-width property is non-zero.
2810 #[inline]
outline_has_nonzero_width(&self) -> bool2811 pub fn outline_has_nonzero_width(&self) -> bool {
2812 use crate::Zero;
2813 !self.outline_width.is_zero()
2814 }
2815 % elif style_struct.name == "Box":
2816 /// Sets the display property, but without touching original_display,
2817 /// except when the adjustment comes from root or item display fixups.
set_adjusted_display( &mut self, dpy: longhands::display::computed_value::T, is_item_or_root: bool )2818 pub fn set_adjusted_display(
2819 &mut self,
2820 dpy: longhands::display::computed_value::T,
2821 is_item_or_root: bool
2822 ) {
2823 self.display = dpy;
2824 if is_item_or_root {
2825 self.original_display = dpy;
2826 }
2827 }
2828 % endif
2829 }
2830
2831 % endfor
2832 }
2833
2834 % for style_struct in data.active_style_structs():
2835 impl style_structs::${style_struct.name} {
2836 % for longhand in style_struct.longhands:
2837 % if longhand.need_index:
2838 /// Iterate over the values of ${longhand.name}.
2839 #[allow(non_snake_case)]
2840 #[inline]
2841 pub fn ${longhand.ident}_iter(&self) -> ${longhand.camel_case}Iter {
2842 ${longhand.camel_case}Iter {
2843 style_struct: self,
2844 current: 0,
2845 max: self.${longhand.ident}_count(),
2846 }
2847 }
2848
2849 /// Get a value mod `index` for the property ${longhand.name}.
2850 #[allow(non_snake_case)]
2851 #[inline]
2852 pub fn ${longhand.ident}_mod(&self, index: usize)
2853 -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
2854 self.${longhand.ident}_at(index % self.${longhand.ident}_count())
2855 }
2856
2857 /// Clone the computed value for the property.
2858 #[allow(non_snake_case)]
2859 #[inline]
2860 #[cfg(feature = "gecko")]
2861 pub fn clone_${longhand.ident}(
2862 &self,
2863 ) -> longhands::${longhand.ident}::computed_value::T {
2864 longhands::${longhand.ident}::computed_value::List(
2865 self.${longhand.ident}_iter().collect()
2866 )
2867 }
2868 % endif
2869 % endfor
2870
2871 % if style_struct.name == "Box":
2872 /// Returns whether there is any animation specified with
2873 /// animation-name other than `none`.
specifies_animations(&self) -> bool2874 pub fn specifies_animations(&self) -> bool {
2875 self.animation_name_iter().any(|name| name.0.is_some())
2876 }
2877
2878 /// Returns whether there are any transitions specified.
2879 #[cfg(feature = "servo")]
specifies_transitions(&self) -> bool2880 pub fn specifies_transitions(&self) -> bool {
2881 (0..self.transition_property_count()).any(|index| {
2882 let combined_duration =
2883 self.transition_duration_mod(index).seconds().max(0.) +
2884 self.transition_delay_mod(index).seconds();
2885 combined_duration > 0.
2886 })
2887 }
2888
2889 /// Returns true if animation properties are equal between styles, but without
2890 /// considering keyframe data.
2891 #[cfg(feature = "servo")]
animations_equals(&self, other: &Self) -> bool2892 pub fn animations_equals(&self, other: &Self) -> bool {
2893 self.animation_name_iter().eq(other.animation_name_iter()) &&
2894 self.animation_delay_iter().eq(other.animation_delay_iter()) &&
2895 self.animation_direction_iter().eq(other.animation_direction_iter()) &&
2896 self.animation_duration_iter().eq(other.animation_duration_iter()) &&
2897 self.animation_fill_mode_iter().eq(other.animation_fill_mode_iter()) &&
2898 self.animation_iteration_count_iter().eq(other.animation_iteration_count_iter()) &&
2899 self.animation_play_state_iter().eq(other.animation_play_state_iter()) &&
2900 self.animation_timing_function_iter().eq(other.animation_timing_function_iter())
2901 }
2902
2903 % elif style_struct.name == "Column":
2904 /// Whether this is a multicol style.
2905 #[cfg(feature = "servo")]
is_multicol(&self) -> bool2906 pub fn is_multicol(&self) -> bool {
2907 !self.column_width.is_auto() || !self.column_count.is_auto()
2908 }
2909 % endif
2910 }
2911
2912 % for longhand in style_struct.longhands:
2913 % if longhand.need_index:
2914 /// An iterator over the values of the ${longhand.name} properties.
2915 pub struct ${longhand.camel_case}Iter<'a> {
2916 style_struct: &'a style_structs::${style_struct.name},
2917 current: usize,
2918 max: usize,
2919 }
2920
2921 impl<'a> Iterator for ${longhand.camel_case}Iter<'a> {
2922 type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue;
2923
next(&mut self) -> Option<Self::Item>2924 fn next(&mut self) -> Option<Self::Item> {
2925 self.current += 1;
2926 if self.current <= self.max {
2927 Some(self.style_struct.${longhand.ident}_at(self.current - 1))
2928 } else {
2929 None
2930 }
2931 }
2932 }
2933 % endif
2934 % endfor
2935 % endfor
2936
2937
2938 #[cfg(feature = "gecko")]
2939 pub use crate::gecko_properties::{ComputedValues, ComputedValuesInner};
2940
2941 #[cfg(feature = "servo")]
2942 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
2943 /// Actual data of ComputedValues, to match up with Gecko
2944 pub struct ComputedValuesInner {
2945 % for style_struct in data.active_style_structs():
2946 ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
2947 % endfor
2948 custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
2949 /// The writing mode of this computed values struct.
2950 pub writing_mode: WritingMode,
2951
2952 /// A set of flags we use to store misc information regarding this style.
2953 pub flags: ComputedValueFlags,
2954
2955 /// The rule node representing the ordered list of rules matched for this
2956 /// node. Can be None for default values and text nodes. This is
2957 /// essentially an optimization to avoid referencing the root rule node.
2958 pub rules: Option<StrongRuleNode>,
2959
2960 /// The element's computed values if visited, only computed if there's a
2961 /// relevant link for this element. A element's "relevant link" is the
2962 /// element being matched if it is a link or the nearest ancestor link.
2963 visited_style: Option<Arc<ComputedValues>>,
2964 }
2965
2966 /// The struct that Servo uses to represent computed values.
2967 ///
2968 /// This struct contains an immutable atomically-reference-counted pointer to
2969 /// every kind of style struct.
2970 ///
2971 /// When needed, the structs may be copied in order to get mutated.
2972 #[cfg(feature = "servo")]
2973 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
2974 pub struct ComputedValues {
2975 /// The actual computed values
2976 ///
2977 /// In Gecko the outer ComputedValues is actually a ComputedStyle, whereas
2978 /// ComputedValuesInner is the core set of computed values.
2979 ///
2980 /// We maintain this distinction in servo to reduce the amount of special
2981 /// casing.
2982 inner: ComputedValuesInner,
2983
2984 /// The pseudo-element that we're using.
2985 pseudo: Option<PseudoElement>,
2986 }
2987
2988 impl ComputedValues {
2989 /// Returns the pseudo-element that this style represents.
2990 #[cfg(feature = "servo")]
pseudo(&self) -> Option<<&PseudoElement>2991 pub fn pseudo(&self) -> Option<<&PseudoElement> {
2992 self.pseudo.as_ref()
2993 }
2994
2995 /// Returns true if this is the style for a pseudo-element.
2996 #[cfg(feature = "servo")]
is_pseudo_style(&self) -> bool2997 pub fn is_pseudo_style(&self) -> bool {
2998 self.pseudo().is_some()
2999 }
3000
3001 /// Returns whether this style's display value is equal to contents.
is_display_contents(&self) -> bool3002 pub fn is_display_contents(&self) -> bool {
3003 self.get_box().clone_display().is_contents()
3004 }
3005
3006 /// Gets a reference to the rule node. Panic if no rule node exists.
rules(&self) -> &StrongRuleNode3007 pub fn rules(&self) -> &StrongRuleNode {
3008 self.rules.as_ref().unwrap()
3009 }
3010
3011 /// Returns the visited style, if any.
visited_style(&self) -> Option<<&ComputedValues>3012 pub fn visited_style(&self) -> Option<<&ComputedValues> {
3013 self.visited_style.as_deref()
3014 }
3015
3016 /// Returns the visited rules, if applicable.
visited_rules(&self) -> Option<<&StrongRuleNode>3017 pub fn visited_rules(&self) -> Option<<&StrongRuleNode> {
3018 self.visited_style.as_ref().and_then(|s| s.rules.as_ref())
3019 }
3020
3021 /// Gets a reference to the custom properties map (if one exists).
custom_properties(&self) -> Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>3022 pub fn custom_properties(&self) -> Option<<&Arc<crate::custom_properties::CustomPropertiesMap>> {
3023 self.custom_properties.as_ref()
3024 }
3025
3026 /// Returns whether we have the same custom properties as another style.
3027 ///
3028 /// This should effectively be just:
3029 ///
3030 /// self.custom_properties() == other.custom_properties()
3031 ///
3032 /// But that's not really the case because IndexMap equality doesn't
3033 /// consider ordering, which we have to account for. Also, for the same
3034 /// reason, IndexMap equality comparisons are slower than needed.
3035 ///
3036 /// See https://github.com/bluss/indexmap/issues/153
custom_properties_equal(&self, other: &Self) -> bool3037 pub fn custom_properties_equal(&self, other: &Self) -> bool {
3038 match (self.custom_properties(), other.custom_properties()) {
3039 (Some(l), Some(r)) => {
3040 l.len() == r.len() && l.iter().zip(r.iter()).all(|((k1, v1), (k2, v2))| k1 == k2 && v1 == v2)
3041 },
3042 (None, None) => true,
3043 _ => false,
3044 }
3045 }
3046
3047 % for prop in data.longhands:
3048 /// Gets the computed value of a given property.
3049 #[inline(always)]
3050 #[allow(non_snake_case)]
3051 pub fn clone_${prop.ident}(
3052 &self,
3053 ) -> longhands::${prop.ident}::computed_value::T {
3054 self.get_${prop.style_struct.ident.strip("_")}()
3055 % if prop.logical:
3056 .clone_${prop.ident}(self.writing_mode)
3057 % else:
3058 .clone_${prop.ident}()
3059 % endif
3060 }
3061 % endfor
3062
3063 /// Writes the value of the given longhand as a string in `dest`.
3064 ///
3065 /// Note that the value will usually be the computed value, except for
3066 /// colors, where it's resolved.
get_longhand_property_value<W>( &self, property_id: LonghandId, dest: &mut CssWriter<W> ) -> fmt::Result where W: Write,3067 pub fn get_longhand_property_value<W>(
3068 &self,
3069 property_id: LonghandId,
3070 dest: &mut CssWriter<W>
3071 ) -> fmt::Result
3072 where
3073 W: Write,
3074 {
3075 use crate::values::resolved::ToResolvedValue;
3076
3077 let context = resolved::Context {
3078 style: self,
3079 };
3080
3081 // TODO(emilio): Is it worth to merge branches here just like
3082 // PropertyDeclaration::to_css does?
3083 match property_id {
3084 % for prop in data.longhands:
3085 LonghandId::${prop.camel_case} => {
3086 let value = self.clone_${prop.ident}();
3087 value.to_resolved_value(&context).to_css(dest)
3088 }
3089 % endfor
3090 }
3091 }
3092
3093 /// Resolves the currentColor keyword.
3094 ///
3095 /// Any color value from computed values (except for the 'color' property
3096 /// itself) should go through this method.
3097 ///
3098 /// Usage example:
3099 /// let top_color =
3100 /// style.resolve_color(style.get_border().clone_border_top_color());
3101 #[inline]
resolve_color(&self, color: computed::Color) -> RGBA3102 pub fn resolve_color(&self, color: computed::Color) -> RGBA {
3103 color.to_rgba(self.get_inherited_text().clone_color())
3104 }
3105
3106 /// Returns which longhand properties have different values in the two
3107 /// ComputedValues.
3108 #[cfg(feature = "gecko_debug")]
differing_properties(&self, other: &ComputedValues) -> LonghandIdSet3109 pub fn differing_properties(&self, other: &ComputedValues) -> LonghandIdSet {
3110 let mut set = LonghandIdSet::new();
3111 % for prop in data.longhands:
3112 if self.clone_${prop.ident}() != other.clone_${prop.ident}() {
3113 set.insert(LonghandId::${prop.camel_case});
3114 }
3115 % endfor
3116 set
3117 }
3118
3119 /// Create a `TransitionPropertyIterator` for this styles transition properties.
transition_properties<'a>( &'a self ) -> animated_properties::TransitionPropertyIterator<'a>3120 pub fn transition_properties<'a>(
3121 &'a self
3122 ) -> animated_properties::TransitionPropertyIterator<'a> {
3123 animated_properties::TransitionPropertyIterator::from_style(self)
3124 }
3125 }
3126
3127 #[cfg(feature = "servo")]
3128 impl ComputedValues {
3129 /// Create a new refcounted `ComputedValues`
3130 pub fn new(
3131 pseudo: Option<<&PseudoElement>,
3132 custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
3133 writing_mode: WritingMode,
3134 flags: ComputedValueFlags,
3135 rules: Option<StrongRuleNode>,
3136 visited_style: Option<Arc<ComputedValues>>,
3137 % for style_struct in data.active_style_structs():
3138 ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
3139 % endfor
3140 ) -> Arc<Self> {
3141 Arc::new(Self {
3142 inner: ComputedValuesInner {
3143 custom_properties,
3144 writing_mode,
3145 rules,
3146 visited_style,
3147 flags,
3148 % for style_struct in data.active_style_structs():
3149 ${style_struct.ident},
3150 % endfor
3151 },
3152 pseudo: pseudo.cloned(),
3153 })
3154 }
3155
3156 /// Get the initial computed values.
initial_values() -> &'static Self3157 pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES }
3158
3159 /// Serializes the computed value of this property as a string.
computed_value_to_string(&self, property: PropertyDeclarationId) -> String3160 pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String {
3161 match property {
3162 PropertyDeclarationId::Longhand(id) => {
3163 let mut s = String::new();
3164 self.get_longhand_property_value(
3165 id,
3166 &mut CssWriter::new(&mut s)
3167 ).unwrap();
3168 s
3169 }
3170 PropertyDeclarationId::Custom(name) => {
3171 self.custom_properties
3172 .as_ref()
3173 .and_then(|map| map.get(name))
3174 .map_or(String::new(), |value| value.to_css_string())
3175 }
3176 }
3177 }
3178 }
3179
3180 #[cfg(feature = "servo")]
3181 impl ops::Deref for ComputedValues {
3182 type Target = ComputedValuesInner;
deref(&self) -> &ComputedValuesInner3183 fn deref(&self) -> &ComputedValuesInner {
3184 &self.inner
3185 }
3186 }
3187
3188 #[cfg(feature = "servo")]
3189 impl ops::DerefMut for ComputedValues {
deref_mut(&mut self) -> &mut ComputedValuesInner3190 fn deref_mut(&mut self) -> &mut ComputedValuesInner {
3191 &mut self.inner
3192 }
3193 }
3194
3195 #[cfg(feature = "servo")]
3196 impl ComputedValuesInner {
3197 % for style_struct in data.active_style_structs():
3198 /// Clone the ${style_struct.name} struct.
3199 #[inline]
3200 pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
3201 self.${style_struct.ident}.clone()
3202 }
3203
3204 /// Get a immutable reference to the ${style_struct.name} struct.
3205 #[inline]
3206 pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
3207 &self.${style_struct.ident}
3208 }
3209
3210 /// Gets an immutable reference to the refcounted value that wraps
3211 /// `${style_struct.name}`.
3212 pub fn ${style_struct.name_lower}_arc(&self) -> &Arc<style_structs::${style_struct.name}> {
3213 &self.${style_struct.ident}
3214 }
3215
3216 /// Get a mutable reference to the ${style_struct.name} struct.
3217 #[inline]
3218 pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
3219 Arc::make_mut(&mut self.${style_struct.ident})
3220 }
3221 % endfor
3222
3223 /// Gets a reference to the rule node. Panic if no rule node exists.
rules(&self) -> &StrongRuleNode3224 pub fn rules(&self) -> &StrongRuleNode {
3225 self.rules.as_ref().unwrap()
3226 }
3227
3228 #[inline]
3229 /// Returns whether the "content" property for the given style is completely
3230 /// ineffective, and would yield an empty `::before` or `::after`
3231 /// pseudo-element.
ineffective_content_property(&self) -> bool3232 pub fn ineffective_content_property(&self) -> bool {
3233 use crate::values::generics::counters::Content;
3234 match self.get_counters().content {
3235 Content::Normal | Content::None => true,
3236 Content::Items(ref items) => items.is_empty(),
3237 }
3238 }
3239
3240 /// Whether the current style or any of its ancestors is multicolumn.
3241 #[inline]
can_be_fragmented(&self) -> bool3242 pub fn can_be_fragmented(&self) -> bool {
3243 self.flags.contains(ComputedValueFlags::CAN_BE_FRAGMENTED)
3244 }
3245
3246 /// Whether the current style is multicolumn.
3247 #[inline]
is_multicol(&self) -> bool3248 pub fn is_multicol(&self) -> bool {
3249 self.get_column().is_multicol()
3250 }
3251
3252 /// Get the logical computed inline size.
3253 #[inline]
content_inline_size(&self) -> &computed::Size3254 pub fn content_inline_size(&self) -> &computed::Size {
3255 let position_style = self.get_position();
3256 if self.writing_mode.is_vertical() {
3257 &position_style.height
3258 } else {
3259 &position_style.width
3260 }
3261 }
3262
3263 /// Get the logical computed block size.
3264 #[inline]
content_block_size(&self) -> &computed::Size3265 pub fn content_block_size(&self) -> &computed::Size {
3266 let position_style = self.get_position();
3267 if self.writing_mode.is_vertical() { &position_style.width } else { &position_style.height }
3268 }
3269
3270 /// Get the logical computed min inline size.
3271 #[inline]
min_inline_size(&self) -> &computed::Size3272 pub fn min_inline_size(&self) -> &computed::Size {
3273 let position_style = self.get_position();
3274 if self.writing_mode.is_vertical() { &position_style.min_height } else { &position_style.min_width }
3275 }
3276
3277 /// Get the logical computed min block size.
3278 #[inline]
min_block_size(&self) -> &computed::Size3279 pub fn min_block_size(&self) -> &computed::Size {
3280 let position_style = self.get_position();
3281 if self.writing_mode.is_vertical() { &position_style.min_width } else { &position_style.min_height }
3282 }
3283
3284 /// Get the logical computed max inline size.
3285 #[inline]
max_inline_size(&self) -> &computed::MaxSize3286 pub fn max_inline_size(&self) -> &computed::MaxSize {
3287 let position_style = self.get_position();
3288 if self.writing_mode.is_vertical() { &position_style.max_height } else { &position_style.max_width }
3289 }
3290
3291 /// Get the logical computed max block size.
3292 #[inline]
max_block_size(&self) -> &computed::MaxSize3293 pub fn max_block_size(&self) -> &computed::MaxSize {
3294 let position_style = self.get_position();
3295 if self.writing_mode.is_vertical() { &position_style.max_width } else { &position_style.max_height }
3296 }
3297
3298 /// Get the logical computed padding for this writing mode.
3299 #[inline]
logical_padding(&self) -> LogicalMargin<<&computed::LengthPercentage>3300 pub fn logical_padding(&self) -> LogicalMargin<<&computed::LengthPercentage> {
3301 let padding_style = self.get_padding();
3302 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
3303 &padding_style.padding_top.0,
3304 &padding_style.padding_right.0,
3305 &padding_style.padding_bottom.0,
3306 &padding_style.padding_left.0,
3307 ))
3308 }
3309
3310 /// Get the logical border width
3311 #[inline]
border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au>3312 pub fn border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au> {
3313 let border_style = self.get_border();
3314 LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
3315 Au::from(border_style.border_top_width),
3316 Au::from(border_style.border_right_width),
3317 Au::from(border_style.border_bottom_width),
3318 Au::from(border_style.border_left_width),
3319 ))
3320 }
3321
3322 /// Gets the logical computed border widths for this style.
3323 #[inline]
logical_border_width(&self) -> LogicalMargin<Au>3324 pub fn logical_border_width(&self) -> LogicalMargin<Au> {
3325 self.border_width_for_writing_mode(self.writing_mode)
3326 }
3327
3328 /// Gets the logical computed margin from this style.
3329 #[inline]
logical_margin(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto>3330 pub fn logical_margin(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> {
3331 let margin_style = self.get_margin();
3332 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
3333 &margin_style.margin_top,
3334 &margin_style.margin_right,
3335 &margin_style.margin_bottom,
3336 &margin_style.margin_left,
3337 ))
3338 }
3339
3340 /// Gets the logical position from this style.
3341 #[inline]
logical_position(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto>3342 pub fn logical_position(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> {
3343 // FIXME(SimonSapin): should be the writing mode of the containing block, maybe?
3344 let position_style = self.get_position();
3345 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
3346 &position_style.top,
3347 &position_style.right,
3348 &position_style.bottom,
3349 &position_style.left,
3350 ))
3351 }
3352
3353 /// Return true if the effects force the transform style to be Flat
overrides_transform_style(&self) -> bool3354 pub fn overrides_transform_style(&self) -> bool {
3355 use crate::computed_values::mix_blend_mode::T as MixBlendMode;
3356
3357 let effects = self.get_effects();
3358 // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
3359 effects.opacity < 1.0 ||
3360 !effects.filter.0.is_empty() ||
3361 !effects.clip.is_auto() ||
3362 effects.mix_blend_mode != MixBlendMode::Normal
3363 }
3364
3365 /// <https://drafts.csswg.org/css-transforms/#grouping-property-values>
get_used_transform_style(&self) -> computed_values::transform_style::T3366 pub fn get_used_transform_style(&self) -> computed_values::transform_style::T {
3367 use crate::computed_values::transform_style::T as TransformStyle;
3368
3369 let box_ = self.get_box();
3370
3371 if self.overrides_transform_style() {
3372 TransformStyle::Flat
3373 } else {
3374 // Return the computed value if not overridden by the above exceptions
3375 box_.transform_style
3376 }
3377 }
3378
3379 /// Whether given this transform value, the compositor would require a
3380 /// layer.
transform_requires_layer(&self) -> bool3381 pub fn transform_requires_layer(&self) -> bool {
3382 use crate::values::generics::transform::TransformOperation;
3383 // Check if the transform matrix is 2D or 3D
3384 for transform in &*self.get_box().transform.0 {
3385 match *transform {
3386 TransformOperation::Perspective(..) => {
3387 return true;
3388 }
3389 TransformOperation::Matrix3D(m) => {
3390 // See http://dev.w3.org/csswg/css-transforms/#2d-matrix
3391 if m.m31 != 0.0 || m.m32 != 0.0 ||
3392 m.m13 != 0.0 || m.m23 != 0.0 ||
3393 m.m43 != 0.0 || m.m14 != 0.0 ||
3394 m.m24 != 0.0 || m.m34 != 0.0 ||
3395 m.m33 != 1.0 || m.m44 != 1.0 {
3396 return true;
3397 }
3398 }
3399 TransformOperation::Translate3D(_, _, z) |
3400 TransformOperation::TranslateZ(z) => {
3401 if z.px() != 0. {
3402 return true;
3403 }
3404 }
3405 _ => {}
3406 }
3407 }
3408
3409 // Neither perspective nor transform present
3410 false
3411 }
3412 }
3413
3414 % if engine == "gecko":
3415 pub use crate::servo_arc::RawOffsetArc as BuilderArc;
3416 /// Clone an arc, returning a regular arc
clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T>3417 fn clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T> {
3418 Arc::from_raw_offset(x.clone())
3419 }
3420 % else:
3421 pub use crate::servo_arc::Arc as BuilderArc;
3422 /// Clone an arc, returning a regular arc
clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T>3423 fn clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T> {
3424 x.clone()
3425 }
3426 % endif
3427
3428 /// A reference to a style struct of the parent, or our own style struct.
3429 pub enum StyleStructRef<'a, T: 'static> {
3430 /// A borrowed struct from the parent, for example, for inheriting style.
3431 Borrowed(&'a BuilderArc<T>),
3432 /// An owned struct, that we've already mutated.
3433 Owned(UniqueArc<T>),
3434 /// Temporarily vacated, will panic if accessed
3435 Vacated,
3436 }
3437
3438 impl<'a, T: 'a> StyleStructRef<'a, T>
3439 where
3440 T: Clone,
3441 {
3442 /// Ensure a mutable reference of this value exists, either cloning the
3443 /// borrowed value, or returning the owned one.
mutate(&mut self) -> &mut T3444 pub fn mutate(&mut self) -> &mut T {
3445 if let StyleStructRef::Borrowed(v) = *self {
3446 *self = StyleStructRef::Owned(UniqueArc::new((**v).clone()));
3447 }
3448
3449 match *self {
3450 StyleStructRef::Owned(ref mut v) => v,
3451 StyleStructRef::Borrowed(..) => unreachable!(),
3452 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3453 }
3454 }
3455
3456 /// Whether this is pointer-equal to the struct we're going to copy the
3457 /// value from.
3458 ///
3459 /// This is used to avoid allocations when people write stuff like `font:
3460 /// inherit` or such `all: initial`.
3461 #[inline]
ptr_eq(&self, struct_to_copy_from: &T) -> bool3462 pub fn ptr_eq(&self, struct_to_copy_from: &T) -> bool {
3463 match *self {
3464 StyleStructRef::Owned(..) => false,
3465 StyleStructRef::Borrowed(arc) => {
3466 &**arc as *const T == struct_to_copy_from as *const T
3467 }
3468 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3469 }
3470 }
3471
3472 /// Extract a unique Arc from this struct, vacating it.
3473 ///
3474 /// The vacated state is a transient one, please put the Arc back
3475 /// when done via `put()`. This function is to be used to separate
3476 /// the struct being mutated from the computed context
take(&mut self) -> UniqueArc<T>3477 pub fn take(&mut self) -> UniqueArc<T> {
3478 use std::mem::replace;
3479 let inner = replace(self, StyleStructRef::Vacated);
3480
3481 match inner {
3482 StyleStructRef::Owned(arc) => arc,
3483 StyleStructRef::Borrowed(arc) => UniqueArc::new((**arc).clone()),
3484 StyleStructRef::Vacated => panic!("Accessed vacated style struct"),
3485 }
3486 }
3487
3488 /// Replace vacated ref with an arc
put(&mut self, arc: UniqueArc<T>)3489 pub fn put(&mut self, arc: UniqueArc<T>) {
3490 debug_assert!(matches!(*self, StyleStructRef::Vacated));
3491 *self = StyleStructRef::Owned(arc);
3492 }
3493
3494 /// Get a mutable reference to the owned struct, or `None` if the struct
3495 /// hasn't been mutated.
get_if_mutated(&mut self) -> Option<<&mut T>3496 pub fn get_if_mutated(&mut self) -> Option<<&mut T> {
3497 match *self {
3498 StyleStructRef::Owned(ref mut v) => Some(v),
3499 StyleStructRef::Borrowed(..) => None,
3500 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3501 }
3502 }
3503
3504 /// Returns an `Arc` to the internal struct, constructing one if
3505 /// appropriate.
build(self) -> Arc<T>3506 pub fn build(self) -> Arc<T> {
3507 match self {
3508 StyleStructRef::Owned(v) => v.shareable(),
3509 StyleStructRef::Borrowed(v) => clone_arc(v),
3510 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3511 }
3512 }
3513 }
3514
3515 impl<'a, T: 'a> ops::Deref for StyleStructRef<'a, T> {
3516 type Target = T;
3517
deref(&self) -> &T3518 fn deref(&self) -> &T {
3519 match *self {
3520 StyleStructRef::Owned(ref v) => &**v,
3521 StyleStructRef::Borrowed(v) => &**v,
3522 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3523 }
3524 }
3525 }
3526
3527 /// A type used to compute a struct with minimal overhead.
3528 ///
3529 /// This allows holding references to the parent/default computed values without
3530 /// actually cloning them, until we either build the style, or mutate the
3531 /// inherited value.
3532 pub struct StyleBuilder<'a> {
3533 /// The device we're using to compute style.
3534 ///
3535 /// This provides access to viewport unit ratios, etc.
3536 pub device: &'a Device,
3537
3538 /// The style we're inheriting from.
3539 ///
3540 /// This is effectively
3541 /// `parent_style.unwrap_or(device.default_computed_values())`.
3542 inherited_style: &'a ComputedValues,
3543
3544 /// The style we're inheriting from for properties that don't inherit from
3545 /// ::first-line. This is the same as inherited_style, unless
3546 /// inherited_style is a ::first-line style.
3547 inherited_style_ignoring_first_line: &'a ComputedValues,
3548
3549 /// The style we're getting reset structs from.
3550 reset_style: &'a ComputedValues,
3551
3552 /// The rule node representing the ordered list of rules matched for this
3553 /// node.
3554 pub rules: Option<StrongRuleNode>,
3555
3556 custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
3557
3558 /// The pseudo-element this style will represent.
3559 pub pseudo: Option<<&'a PseudoElement>,
3560
3561 /// Whether we have mutated any reset structs since the the last time
3562 /// `clear_modified_reset` was called. This is used to tell whether the
3563 /// `StyleAdjuster` did any work.
3564 modified_reset: bool,
3565
3566 /// Whether this is the style for the root element.
3567 pub is_root_element: bool,
3568
3569 /// The writing mode flags.
3570 ///
3571 /// TODO(emilio): Make private.
3572 pub writing_mode: WritingMode,
3573
3574 /// Flags for the computed value.
3575 pub flags: Cell<ComputedValueFlags>,
3576
3577 /// The element's style if visited, only computed if there's a relevant link
3578 /// for this element. A element's "relevant link" is the element being
3579 /// matched if it is a link or the nearest ancestor link.
3580 visited_style: Option<Arc<ComputedValues>>,
3581 % for style_struct in data.active_style_structs():
3582 ${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>,
3583 % endfor
3584 }
3585
3586 impl<'a> StyleBuilder<'a> {
3587 /// Trivially construct a `StyleBuilder`.
3588 fn new(
3589 device: &'a Device,
3590 parent_style: Option<<&'a ComputedValues>,
3591 parent_style_ignoring_first_line: Option<<&'a ComputedValues>,
3592 pseudo: Option<<&'a PseudoElement>,
3593 rules: Option<StrongRuleNode>,
3594 custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
3595 is_root_element: bool,
3596 ) -> Self {
3597 debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
3598 #[cfg(feature = "gecko")]
3599 debug_assert!(parent_style.is_none() ||
3600 std::ptr::eq(parent_style.unwrap(),
3601 parent_style_ignoring_first_line.unwrap()) ||
3602 parent_style.unwrap().is_first_line_style());
3603 let reset_style = device.default_computed_values();
3604 let inherited_style = parent_style.unwrap_or(reset_style);
3605 let inherited_style_ignoring_first_line = parent_style_ignoring_first_line.unwrap_or(reset_style);
3606
3607 let flags = inherited_style.flags.inherited();
3608
3609 StyleBuilder {
3610 device,
3611 inherited_style,
3612 inherited_style_ignoring_first_line,
3613 reset_style,
3614 pseudo,
3615 rules,
3616 modified_reset: false,
3617 is_root_element,
3618 custom_properties,
3619 writing_mode: inherited_style.writing_mode,
3620 flags: Cell::new(flags),
3621 visited_style: None,
3622 % for style_struct in data.active_style_structs():
3623 % if style_struct.inherited:
3624 ${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.${style_struct.name_lower}_arc()),
3625 % else:
3626 ${style_struct.ident}: StyleStructRef::Borrowed(reset_style.${style_struct.name_lower}_arc()),
3627 % endif
3628 % endfor
3629 }
3630 }
3631
3632 /// NOTE(emilio): This is done so we can compute relative units with respect
3633 /// to the parent style, but all the early properties / writing-mode / etc
3634 /// are already set to the right ones on the kid.
3635 ///
3636 /// Do _not_ actually call this to construct a style, this should mostly be
3637 /// used for animations.
3638 pub fn for_animation(
3639 device: &'a Device,
3640 style_to_derive_from: &'a ComputedValues,
3641 parent_style: Option<<&'a ComputedValues>,
3642 ) -> Self {
3643 let reset_style = device.default_computed_values();
3644 let inherited_style = parent_style.unwrap_or(reset_style);
3645 #[cfg(feature = "gecko")]
3646 debug_assert!(parent_style.is_none() ||
3647 !parent_style.unwrap().is_first_line_style());
3648 StyleBuilder {
3649 device,
3650 inherited_style,
3651 // None of our callers pass in ::first-line parent styles.
3652 inherited_style_ignoring_first_line: inherited_style,
3653 reset_style,
3654 pseudo: None,
3655 modified_reset: false,
3656 is_root_element: false,
3657 rules: None,
3658 custom_properties: style_to_derive_from.custom_properties().cloned(),
3659 writing_mode: style_to_derive_from.writing_mode,
3660 flags: Cell::new(style_to_derive_from.flags),
3661 visited_style: None,
3662 % for style_struct in data.active_style_structs():
3663 ${style_struct.ident}: StyleStructRef::Borrowed(
3664 style_to_derive_from.${style_struct.name_lower}_arc()
3665 ),
3666 % endfor
3667 }
3668 }
3669
3670 /// Copy the reset properties from `style`.
3671 pub fn copy_reset_from(&mut self, style: &'a ComputedValues) {
3672 % for style_struct in data.active_style_structs():
3673 % if not style_struct.inherited:
3674 self.${style_struct.ident} =
3675 StyleStructRef::Borrowed(style.${style_struct.name_lower}_arc());
3676 % endif
3677 % endfor
3678 }
3679
3680 % for property in data.longhands:
3681 % if not property.style_struct.inherited:
3682 /// Inherit `${property.ident}` from our parent style.
3683 #[allow(non_snake_case)]
3684 pub fn inherit_${property.ident}(&mut self) {
3685 let inherited_struct =
3686 self.inherited_style_ignoring_first_line
3687 .get_${property.style_struct.name_lower}();
3688
3689 self.modified_reset = true;
3690 self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
3691
3692 % if property.ident == "content":
3693 self.add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
3694 % endif
3695
3696 % if property.ident == "display":
3697 self.add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
3698 % endif
3699
3700 if self.${property.style_struct.ident}.ptr_eq(inherited_struct) {
3701 return;
3702 }
3703
3704 self.${property.style_struct.ident}.mutate()
3705 .copy_${property.ident}_from(
3706 inherited_struct,
3707 % if property.logical:
3708 self.writing_mode,
3709 % endif
3710 );
3711 }
3712 % else:
3713 /// Reset `${property.ident}` to the initial value.
3714 #[allow(non_snake_case)]
3715 pub fn reset_${property.ident}(&mut self) {
3716 let reset_struct =
3717 self.reset_style.get_${property.style_struct.name_lower}();
3718
3719 if self.${property.style_struct.ident}.ptr_eq(reset_struct) {
3720 return;
3721 }
3722
3723 self.${property.style_struct.ident}.mutate()
3724 .reset_${property.ident}(
3725 reset_struct,
3726 % if property.logical:
3727 self.writing_mode,
3728 % endif
3729 );
3730 }
3731 % endif
3732
3733 % if not property.is_vector or property.simple_vector_bindings or engine in ["servo-2013", "servo-2020"]:
3734 /// Set the `${property.ident}` to the computed value `value`.
3735 #[allow(non_snake_case)]
3736 pub fn set_${property.ident}(
3737 &mut self,
3738 value: longhands::${property.ident}::computed_value::T
3739 ) {
3740 % if not property.style_struct.inherited:
3741 self.modified_reset = true;
3742 % endif
3743
3744 self.${property.style_struct.ident}.mutate()
3745 .set_${property.ident}(
3746 value,
3747 % if property.logical:
3748 self.writing_mode,
3749 % endif
3750 );
3751 }
3752 % endif
3753 % endfor
3754 <% del property %>
3755
3756 /// Inherits style from the parent element, accounting for the default
3757 /// computed values that need to be provided as well.
3758 pub fn for_inheritance(
3759 device: &'a Device,
3760 parent: Option<<&'a ComputedValues>,
3761 pseudo: Option<<&'a PseudoElement>,
3762 ) -> Self {
3763 // Rebuild the visited style from the parent, ensuring that it will also
3764 // not have rules. This matches the unvisited style that will be
3765 // produced by this builder. This assumes that the caller doesn't need
3766 // to adjust or process visited style, so we can just build visited
3767 // style here for simplicity.
3768 let visited_style = parent.and_then(|parent| {
3769 parent.visited_style().map(|style| {
3770 Self::for_inheritance(
3771 device,
3772 Some(style),
3773 pseudo,
3774 ).build()
3775 })
3776 });
3777 let mut ret = Self::new(
3778 device,
3779 parent,
3780 parent,
3781 pseudo,
3782 /* rules = */ None,
3783 parent.and_then(|p| p.custom_properties().cloned()),
3784 /* is_root_element = */ false,
3785 );
3786 ret.visited_style = visited_style;
3787 ret
3788 }
3789
3790 /// Returns whether we have a visited style.
3791 pub fn has_visited_style(&self) -> bool {
3792 self.visited_style.is_some()
3793 }
3794
3795 /// Returns whether we're a pseudo-elements style.
3796 pub fn is_pseudo_element(&self) -> bool {
3797 self.pseudo.map_or(false, |p| !p.is_anon_box())
3798 }
3799
3800 /// Returns the style we're getting reset properties from.
3801 pub fn default_style(&self) -> &'a ComputedValues {
3802 self.reset_style
3803 }
3804
3805 % for style_struct in data.active_style_structs():
3806 /// Gets an immutable view of the current `${style_struct.name}` style.
3807 pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
3808 &self.${style_struct.ident}
3809 }
3810
3811 /// Gets a mutable view of the current `${style_struct.name}` style.
3812 pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
3813 % if not style_struct.inherited:
3814 self.modified_reset = true;
3815 % endif
3816 self.${style_struct.ident}.mutate()
3817 }
3818
3819 /// Gets a mutable view of the current `${style_struct.name}` style.
3820 pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc<style_structs::${style_struct.name}> {
3821 % if not style_struct.inherited:
3822 self.modified_reset = true;
3823 % endif
3824 self.${style_struct.ident}.take()
3825 }
3826
3827 /// Gets a mutable view of the current `${style_struct.name}` style.
3828 pub fn put_${style_struct.name_lower}(&mut self, s: UniqueArc<style_structs::${style_struct.name}>) {
3829 self.${style_struct.ident}.put(s)
3830 }
3831
3832 /// Gets a mutable view of the current `${style_struct.name}` style,
3833 /// only if it's been mutated before.
3834 pub fn get_${style_struct.name_lower}_if_mutated(&mut self)
3835 -> Option<<&mut style_structs::${style_struct.name}> {
3836 self.${style_struct.ident}.get_if_mutated()
3837 }
3838
3839 /// Reset the current `${style_struct.name}` style to its default value.
3840 pub fn reset_${style_struct.name_lower}_struct(&mut self) {
3841 self.${style_struct.ident} =
3842 StyleStructRef::Borrowed(self.reset_style.${style_struct.name_lower}_arc());
3843 }
3844 % endfor
3845 <% del style_struct %>
3846
3847 /// Returns whether this computed style represents a floated object.
3848 pub fn is_floating(&self) -> bool {
3849 self.get_box().clone_float().is_floating()
3850 }
3851
3852 /// Returns whether this computed style represents an absolutely-positioned
3853 /// object.
3854 pub fn is_absolutely_positioned(&self) -> bool {
3855 self.get_box().clone_position().is_absolutely_positioned()
3856 }
3857
3858 /// Whether this style has a top-layer style.
3859 #[cfg(feature = "servo")]
3860 pub fn in_top_layer(&self) -> bool {
3861 matches!(self.get_box().clone__servo_top_layer(),
3862 longhands::_servo_top_layer::computed_value::T::Top)
3863 }
3864
3865 /// Whether this style has a top-layer style.
3866 #[cfg(feature = "gecko")]
3867 pub fn in_top_layer(&self) -> bool {
3868 matches!(self.get_box().clone__moz_top_layer(),
3869 longhands::_moz_top_layer::computed_value::T::Top)
3870 }
3871
3872 /// Clears the "have any reset structs been modified" flag.
3873 fn clear_modified_reset(&mut self) {
3874 self.modified_reset = false;
3875 }
3876
3877 /// Returns whether we have mutated any reset structs since the the last
3878 /// time `clear_modified_reset` was called.
3879 fn modified_reset(&self) -> bool {
3880 self.modified_reset
3881 }
3882
3883 /// Return the current flags.
3884 #[inline]
3885 pub fn flags(&self) -> ComputedValueFlags {
3886 self.flags.get()
3887 }
3888
3889 /// Add a flag to the current builder.
3890 #[inline]
3891 pub fn add_flags(&self, flag: ComputedValueFlags) {
3892 let flags = self.flags() | flag;
3893 self.flags.set(flags);
3894 }
3895
3896 /// Removes a flag to the current builder.
3897 #[inline]
3898 pub fn remove_flags(&self, flag: ComputedValueFlags) {
3899 let flags = self.flags() & !flag;
3900 self.flags.set(flags);
3901 }
3902
3903 /// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
3904 pub fn build(self) -> Arc<ComputedValues> {
3905 ComputedValues::new(
3906 self.pseudo,
3907 self.custom_properties,
3908 self.writing_mode,
3909 self.flags.get(),
3910 self.rules,
3911 self.visited_style,
3912 % for style_struct in data.active_style_structs():
3913 self.${style_struct.ident}.build(),
3914 % endfor
3915 )
3916 }
3917
3918 /// Get the custom properties map if necessary.
3919 ///
3920 /// Cloning the Arc here is fine because it only happens in the case where
3921 /// we have custom properties, and those are both rare and expensive.
3922 fn custom_properties(&self) -> Option<<&Arc<crate::custom_properties::CustomPropertiesMap>> {
3923 self.custom_properties.as_ref()
3924 }
3925
3926 /// Access to various information about our inherited styles. We don't
3927 /// expose an inherited ComputedValues directly, because in the
3928 /// ::first-line case some of the inherited information needs to come from
3929 /// one ComputedValues instance and some from a different one.
3930
3931 /// Inherited writing-mode.
3932 pub fn inherited_writing_mode(&self) -> &WritingMode {
3933 &self.inherited_style.writing_mode
3934 }
3935
3936 /// The computed value flags of our parent.
3937 #[inline]
3938 pub fn get_parent_flags(&self) -> ComputedValueFlags {
3939 self.inherited_style.flags
3940 }
3941
3942 /// And access to inherited style structs.
3943 % for style_struct in data.active_style_structs():
3944 /// Gets our inherited `${style_struct.name}`. We don't name these
3945 /// accessors `inherited_${style_struct.name_lower}` because we already
3946 /// have things like "box" vs "inherited_box" as struct names. Do the
3947 /// next-best thing and call them `parent_${style_struct.name_lower}`
3948 /// instead.
3949 pub fn get_parent_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
3950 % if style_struct.inherited:
3951 self.inherited_style.get_${style_struct.name_lower}()
3952 % else:
3953 self.inherited_style_ignoring_first_line.get_${style_struct.name_lower}()
3954 % endif
3955 }
3956 % endfor
3957 }
3958
3959 #[cfg(feature = "servo")]
3960 pub use self::lazy_static_module::INITIAL_SERVO_VALUES;
3961
3962 // Use a module to work around #[cfg] on lazy_static! not being applied to every generated item.
3963 #[cfg(feature = "servo")]
3964 #[allow(missing_docs)]
3965 mod lazy_static_module {
3966 use crate::logical_geometry::WritingMode;
3967 use crate::computed_value_flags::ComputedValueFlags;
3968 use servo_arc::Arc;
3969 use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};
3970
3971 lazy_static! {
3972 /// The initial values for all style structs as defined by the specification.
3973 pub static ref INITIAL_SERVO_VALUES: ComputedValues = ComputedValues {
3974 inner: ComputedValuesInner {
3975 % for style_struct in data.active_style_structs():
3976 ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {
3977 % for longhand in style_struct.longhands:
3978 % if not longhand.logical:
3979 ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
3980 % endif
3981 % endfor
3982 % if style_struct.name == "InheritedText":
3983 text_decorations_in_effect:
3984 crate::values::computed::text::TextDecorationsInEffect::default(),
3985 % endif
3986 % if style_struct.name == "Font":
3987 hash: 0,
3988 % endif
3989 % if style_struct.name == "Box":
3990 original_display: longhands::display::get_initial_value(),
3991 % endif
3992 }),
3993 % endfor
3994 custom_properties: None,
3995 writing_mode: WritingMode::empty(),
3996 rules: None,
3997 visited_style: None,
3998 flags: ComputedValueFlags::empty(),
3999 },
4000 pseudo: None,
4001 };
4002 }
4003 }
4004
4005 /// A per-longhand function that performs the CSS cascade for that longhand.
4006 pub type CascadePropertyFn =
4007 extern "Rust" fn(
4008 declaration: &PropertyDeclaration,
4009 context: &mut computed::Context,
4010 );
4011
4012 /// A per-longhand array of functions to perform the CSS cascade on each of
4013 /// them, effectively doing virtual dispatch.
4014 pub static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
4015 % for property in data.longhands:
4016 longhands::${property.ident}::cascade_property,
4017 % endfor
4018 ];
4019
4020
4021 /// See StyleAdjuster::adjust_for_border_width.
4022 pub fn adjust_border_width(style: &mut StyleBuilder) {
4023 % for side in ["top", "right", "bottom", "left"]:
4024 // Like calling to_computed_value, which wouldn't type check.
4025 if style.get_border().clone_border_${side}_style().none_or_hidden() &&
4026 style.get_border().border_${side}_has_nonzero_width() {
4027 style.set_border_${side}_width(NonNegativeLength::zero());
4028 }
4029 % endfor
4030 }
4031
4032 /// An identifier for a given alias property.
4033 #[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)]
4034 #[repr(u16)]
4035 pub enum AliasId {
4036 % for i, property in enumerate(data.all_aliases()):
4037 /// ${property.name}
4038 ${property.camel_case} = ${i},
4039 % endfor
4040 }
4041
4042 #[derive(Clone, Copy, Eq, PartialEq)]
4043 enum AliasedPropertyId {
4044 #[allow(dead_code)] // Servo doesn't have aliased shorthands.
4045 Shorthand(ShorthandId),
4046 Longhand(LonghandId),
4047 }
4048
4049 impl fmt::Debug for AliasId {
4050 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4051 let name = NonCustomPropertyId::from(*self).name();
4052 formatter.write_str(name)
4053 }
4054 }
4055
4056 impl AliasId {
4057 /// Returns the property we're aliasing, as a longhand or a shorthand.
4058 #[inline]
4059 fn aliased_property(self) -> AliasedPropertyId {
4060 static MAP: [AliasedPropertyId; ${len(data.all_aliases())}] = [
4061 % for alias in data.all_aliases():
4062 % if alias.original.type() == "longhand":
4063 AliasedPropertyId::Longhand(LonghandId::${alias.original.camel_case}),
4064 % else:
4065 <% assert alias.original.type() == "shorthand" %>
4066 AliasedPropertyId::Shorthand(ShorthandId::${alias.original.camel_case}),
4067 % endif
4068 % endfor
4069 ];
4070 MAP[self as usize]
4071 }
4072 }
4073
4074 /// Call the given macro with tokens like this for each longhand and shorthand properties
4075 /// that is enabled in content:
4076 ///
4077 /// ```
4078 /// [CamelCaseName, SetCamelCaseName, PropertyId::Longhand(LonghandId::CamelCaseName)],
4079 /// ```
4080 ///
4081 /// NOTE(emilio): Callers are responsible to deal with prefs.
4082 #[macro_export]
4083 macro_rules! css_properties_accessors {
4084 ($macro_name: ident) => {
4085 $macro_name! {
4086 % for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
4087 % for property in props:
4088 % if property.enabled_in_content():
4089 % for prop in [property] + property.aliases:
4090 % if '-' in prop.name:
4091 [${prop.ident.capitalize()}, Set${prop.ident.capitalize()},
4092 PropertyId::${kind}(${kind}Id::${property.camel_case})],
4093 % endif
4094 [${prop.camel_case}, Set${prop.camel_case},
4095 PropertyId::${kind}(${kind}Id::${property.camel_case})],
4096 % endfor
4097 % endif
4098 % endfor
4099 % endfor
4100 }
4101 }
4102 }
4103
4104 /// Call the given macro with tokens like this for each longhand properties:
4105 ///
4106 /// ```
4107 /// { snake_case_ident, true }
4108 /// ```
4109 ///
4110 /// … where the boolean indicates whether the property value type
4111 /// is wrapped in a `Box<_>` in the corresponding `PropertyDeclaration` variant.
4112 #[macro_export]
4113 macro_rules! longhand_properties_idents {
4114 ($macro_name: ident) => {
4115 $macro_name! {
4116 % for property in data.longhands:
4117 { ${property.ident}, ${"true" if property.boxed else "false"} }
4118 % endfor
4119 }
4120 }
4121 }
4122
4123 % if engine in ["servo-2013", "servo-2020"]:
4124 % for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]:
4125 macro_rules! restyle_damage_${effect_name} {
4126 ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ]) => ({
4127 if
4128 % for style_struct in data.active_style_structs():
4129 % for longhand in style_struct.longhands:
4130 % if effect_name in longhand.servo_restyle_damage.split() and not longhand.logical:
4131 $old.get_${style_struct.name_lower}().${longhand.ident} !=
4132 $new.get_${style_struct.name_lower}().${longhand.ident} ||
4133 % endif
4134 % endfor
4135 % endfor
4136
4137 false {
4138 $damage.insert($($effect)|*);
4139 true
4140 } else {
4141 false
4142 }
4143 })
4144 }
4145 % endfor
4146 % endif
4147