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 http://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 dom::TElement;
16 use custom_properties::CustomPropertiesBuilder;
17 use servo_arc::{Arc, UniqueArc};
18 use smallbitvec::SmallBitVec;
19 use std::borrow::Cow;
20 use std::{ops, ptr};
21 use std::cell::RefCell;
22 use std::fmt::{self, Write};
23 use std::mem::{self, ManuallyDrop};
24
25 #[cfg(feature = "servo")] use cssparser::RGBA;
26 use cssparser::{CowRcStr, Parser, TokenSerializationType, serialize_identifier};
27 use cssparser::ParserInput;
28 #[cfg(feature = "servo")] use euclid::SideOffsets2D;
29 use context::QuirksMode;
30 use font_metrics::FontMetricsProvider;
31 #[cfg(feature = "gecko")] use gecko_bindings::bindings;
32 #[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
33 #[cfg(feature = "servo")] use logical_geometry::LogicalMargin;
34 #[cfg(feature = "servo")] use computed_values;
35 use logical_geometry::WritingMode;
36 #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
37 use media_queries::Device;
38 use parser::ParserContext;
39 #[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont;
40 use rule_cache::{RuleCache, RuleCacheConditions};
41 use selector_parser::PseudoElement;
42 use selectors::parser::SelectorParseErrorKind;
43 #[cfg(feature = "servo")] use servo_config::prefs::PREFS;
44 use shared_lock::StylesheetGuards;
45 use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};
46 use stylesheets::{CssRuleType, Origin, UrlExtraData};
47 #[cfg(feature = "servo")] use values::Either;
48 use values::generics::text::LineHeight;
49 use values::computed;
50 use values::computed::NonNegativeLength;
51 use rule_tree::{CascadeLevel, StrongRuleNode};
52 use self::computed_value_flags::*;
53 use str::{CssString, CssStringBorrow, CssStringWriter};
54 use style_adjuster::StyleAdjuster;
55
56 pub use self::declaration_block::*;
57
58 #[cfg(feature = "gecko")]
59 #[macro_export]
60 macro_rules! property_name {
61 ($s: tt) => { atom!($s) }
62 }
63
64 <%!
65 from data import Method, Keyword, to_rust_ident, to_camel_case, SYSTEM_FONT_LONGHANDS
66 import os.path
67 %>
68
69 #[path="${repr(os.path.join(os.path.dirname(__file__), 'computed_value_flags.rs'))[1:-1]}"]
70 pub mod computed_value_flags;
71 #[path="${repr(os.path.join(os.path.dirname(__file__), 'declaration_block.rs'))[1:-1]}"]
72 pub mod declaration_block;
73
74 /// Conversion with fewer impls than From/Into
75 pub trait MaybeBoxed<Out> {
76 /// Convert
maybe_boxed(self) -> Out77 fn maybe_boxed(self) -> Out;
78 }
79
80 impl<T> MaybeBoxed<T> for T {
81 #[inline]
maybe_boxed(self) -> T82 fn maybe_boxed(self) -> T { self }
83 }
84
85 impl<T> MaybeBoxed<Box<T>> for T {
86 #[inline]
maybe_boxed(self) -> Box<T>87 fn maybe_boxed(self) -> Box<T> { Box::new(self) }
88 }
89
90 macro_rules! expanded {
91 ( $( $name: ident: $value: expr ),+ ) => {
92 expanded!( $( $name: $value, )+ )
93 };
94 ( $( $name: ident: $value: expr, )+ ) => {
95 Longhands {
96 $(
97 $name: MaybeBoxed::maybe_boxed($value),
98 )+
99 }
100 }
101 }
102
103 /// A module with all the code for longhand properties.
104 #[allow(missing_docs)]
105 pub mod longhands {
106 <%include file="/longhand/background.mako.rs" />
107 <%include file="/longhand/border.mako.rs" />
108 <%include file="/longhand/box.mako.rs" />
109 <%include file="/longhand/color.mako.rs" />
110 <%include file="/longhand/column.mako.rs" />
111 <%include file="/longhand/counters.mako.rs" />
112 <%include file="/longhand/effects.mako.rs" />
113 <%include file="/longhand/font.mako.rs" />
114 <%include file="/longhand/inherited_box.mako.rs" />
115 <%include file="/longhand/inherited_table.mako.rs" />
116 <%include file="/longhand/inherited_text.mako.rs" />
117 <%include file="/longhand/list.mako.rs" />
118 <%include file="/longhand/margin.mako.rs" />
119 <%include file="/longhand/outline.mako.rs" />
120 <%include file="/longhand/padding.mako.rs" />
121 <%include file="/longhand/pointing.mako.rs" />
122 <%include file="/longhand/position.mako.rs" />
123 <%include file="/longhand/table.mako.rs" />
124 <%include file="/longhand/text.mako.rs" />
125 <%include file="/longhand/ui.mako.rs" />
126 <%include file="/longhand/inherited_svg.mako.rs" />
127 <%include file="/longhand/svg.mako.rs" />
128 <%include file="/longhand/xul.mako.rs" />
129 }
130
131 macro_rules! unwrap_or_initial {
132 ($prop: ident) => (unwrap_or_initial!($prop, $prop));
133 ($prop: ident, $expr: expr) =>
134 ($expr.unwrap_or_else(|| $prop::get_initial_specified_value()));
135 }
136
137 /// A module with code for all the shorthand css properties, and a few
138 /// serialization helpers.
139 #[allow(missing_docs)]
140 pub mod shorthands {
141 use cssparser::Parser;
142 use parser::{Parse, ParserContext};
143 use style_traits::{ParseError, StyleParseErrorKind};
144 use values::specified;
145
146 <%include file="/shorthand/serialize.mako.rs" />
147 <%include file="/shorthand/background.mako.rs" />
148 <%include file="/shorthand/border.mako.rs" />
149 <%include file="/shorthand/box.mako.rs" />
150 <%include file="/shorthand/column.mako.rs" />
151 <%include file="/shorthand/font.mako.rs" />
152 <%include file="/shorthand/inherited_text.mako.rs" />
153 <%include file="/shorthand/list.mako.rs" />
154 <%include file="/shorthand/margin.mako.rs" />
155 <%include file="/shorthand/mask.mako.rs" />
156 <%include file="/shorthand/outline.mako.rs" />
157 <%include file="/shorthand/padding.mako.rs" />
158 <%include file="/shorthand/position.mako.rs" />
159 <%include file="/shorthand/inherited_svg.mako.rs" />
160 <%include file="/shorthand/text.mako.rs" />
161
162 // We don't defined the 'all' shorthand using the regular helpers:shorthand
163 // mechanism, since it causes some very large types to be generated.
164 //
165 // Also, make sure logical properties appear before its physical
166 // counter-parts, in order to prevent bugs like:
167 //
168 // https://bugzilla.mozilla.org/show_bug.cgi?id=1410028
169 //
170 // FIXME(emilio): Adopt the resolution from:
171 //
172 // https://github.com/w3c/csswg-drafts/issues/1898
173 //
174 // when there is one, whatever that is.
175 <%
176 logical_longhands = []
177 other_longhands = []
178
179 for p in data.longhands:
180 if p.name in ['direction', 'unicode-bidi']:
181 continue;
182 if not p.enabled_in_content() and not p.experimental(product):
183 continue;
184 if p.logical:
185 logical_longhands.append(p.name)
186 else:
187 other_longhands.append(p.name)
188
189 data.declare_shorthand(
190 "all",
191 logical_longhands + other_longhands,
192 gecko_pref="layout.css.all-shorthand.enabled",
193 spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
194 )
195 %>
196
197 /// The max amount of longhands that the `all` shorthand will ever contain.
198 pub const ALL_SHORTHAND_MAX_LEN: usize = ${len(logical_longhands + other_longhands)};
199 }
200
201 <%
202 from itertools import groupby
203
204 # After this code, `data.longhands` is sorted in the following order:
205 # - first all keyword variants and all variants known to be Copy,
206 # - second all the other variants, such as all variants with the same field
207 # have consecutive discriminants.
208 # The variable `variants` contain the same entries as `data.longhands` in
209 # the same order, but must exist separately to the data source, because
210 # we then need to add three additional variants `WideKeywordDeclaration`,
211 # `VariableDeclaration` and `CustomDeclaration`.
212
213 variants = []
214 for property in data.longhands:
215 variants.append({
216 "name": property.camel_case,
217 "type": property.specified_type(),
218 "doc": "`" + property.name + "`",
219 "copy": property.specified_is_copy(),
220 })
221
222 groups = {}
223 keyfunc = lambda x: x["type"]
224 sortkeys = {}
225 for ty, group in groupby(sorted(variants, key=keyfunc), keyfunc):
226 group = list(group)
227 groups[ty] = group
228 for v in group:
229 if len(group) == 1:
230 sortkeys[v["name"]] = (not v["copy"], 1, v["name"], "")
231 else:
232 sortkeys[v["name"]] = (not v["copy"], len(group), ty, v["name"])
233 variants.sort(key=lambda x: sortkeys[x["name"]])
234
235 # It is extremely important to sort the `data.longhands` array here so
236 # that it is in the same order as `variants`, for `LonghandId` and
237 # `PropertyDeclarationId` to coincide.
238 data.longhands.sort(key=lambda x: sortkeys[x.camel_case])
239 %>
240
241 // WARNING: It is *really* important for the variants of `LonghandId`
242 // and `PropertyDeclaration` to be defined in the exact same order,
243 // with the exception of `CSSWideKeyword`, `WithVariables` and `Custom`,
244 // which don't exist in `LonghandId`.
245
246 <%
247 extra = [
248 {
249 "name": "CSSWideKeyword",
250 "type": "WideKeywordDeclaration",
251 "doc": "A CSS-wide keyword.",
252 "copy": False,
253 },
254 {
255 "name": "WithVariables",
256 "type": "VariableDeclaration",
257 "doc": "An unparsed declaration.",
258 "copy": False,
259 },
260 {
261 "name": "Custom",
262 "type": "CustomDeclaration",
263 "doc": "A custom property declaration.",
264 "copy": False,
265 },
266 ]
267 for v in extra:
268 variants.append(v)
269 groups[v["type"]] = [v]
270 %>
271
272 /// Servo's representation for a property declaration.
273 #[repr(u16)]
274 pub enum PropertyDeclaration {
275 % for variant in variants:
276 /// ${variant["doc"]}
277 ${variant["name"]}(${variant["type"]}),
278 % endfor
279 }
280
281 #[repr(C)]
282 struct PropertyDeclarationVariantRepr<T> {
283 tag: u16,
284 value: T
285 }
286
287 impl Clone for PropertyDeclaration {
288 #[inline]
clone(&self) -> Self289 fn clone(&self) -> Self {
290 use self::PropertyDeclaration::*;
291
292 <%
293 [copy, others] = [list(g) for _, g in groupby(variants, key=lambda x: not x["copy"])]
294 %>
295
296 let self_tag = unsafe {
297 (*(self as *const _ as *const PropertyDeclarationVariantRepr<()>)).tag
298 };
299 if self_tag <= LonghandId::${copy[-1]["name"]} as u16 {
300 #[derive(Clone, Copy)]
301 #[repr(u16)]
302 enum CopyVariants {
303 % for v in copy:
304 _${v["name"]}(${v["type"]}),
305 % endfor
306 }
307
308 unsafe {
309 let mut out = mem::uninitialized();
310 ptr::write(
311 &mut out as *mut _ as *mut CopyVariants,
312 *(self as *const _ as *const CopyVariants),
313 );
314 return out;
315 }
316 }
317
318 match *self {
319 ${" |\n".join("{}(..)".format(v["name"]) for v in copy)} => {
320 unsafe { debug_unreachable!() }
321 }
322 % for ty, vs in groupby(others, key=lambda x: x["type"]):
323 <%
324 vs = list(vs)
325 %>
326 % if len(vs) == 1:
327 ${vs[0]["name"]}(ref value) => {
328 ${vs[0]["name"]}(value.clone())
329 }
330 % else:
331 ${" |\n".join("{}(ref value)".format(v["name"]) for v in vs)} => {
332 unsafe {
333 let mut out = ManuallyDrop::new(mem::uninitialized());
334 ptr::write(
335 &mut out as *mut _ as *mut PropertyDeclarationVariantRepr<${ty}>,
336 PropertyDeclarationVariantRepr {
337 tag: *(self as *const _ as *const u16),
338 value: value.clone(),
339 },
340 );
341 ManuallyDrop::into_inner(out)
342 }
343 }
344 % endif
345 % endfor
346 }
347 }
348 }
349
350 impl PartialEq for PropertyDeclaration {
351 #[inline]
eq(&self, other: &Self) -> bool352 fn eq(&self, other: &Self) -> bool {
353 use self::PropertyDeclaration::*;
354
355 unsafe {
356 let this_repr =
357 &*(self as *const _ as *const PropertyDeclarationVariantRepr<()>);
358 let other_repr =
359 &*(other as *const _ as *const PropertyDeclarationVariantRepr<()>);
360 if this_repr.tag != other_repr.tag {
361 return false;
362 }
363 match *self {
364 % for ty, vs in groupby(variants, key=lambda x: x["type"]):
365 ${" |\n".join("{}(ref this)".format(v["name"]) for v in vs)} => {
366 let other_repr =
367 &*(other as *const _ as *const PropertyDeclarationVariantRepr<${ty}>);
368 *this == other_repr.value
369 }
370 % endfor
371 }
372 }
373 }
374 }
375
376 #[cfg(feature = "gecko")]
377 impl MallocSizeOf for PropertyDeclaration {
378 #[inline]
size_of(&self, ops: &mut MallocSizeOfOps) -> usize379 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
380 use self::PropertyDeclaration::*;
381
382 match *self {
383 % for ty, vs in groupby(variants, key=lambda x: x["type"]):
384 ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
385 value.size_of(ops)
386 }
387 % endfor
388 }
389 }
390 }
391
392
393 impl PropertyDeclaration {
394 /// Like the method on ToCss, but without the type parameter to avoid
395 /// accidentally monomorphizing this large function multiple times for
396 /// different writers.
to_css(&self, dest: &mut CssStringWriter) -> fmt::Result397 pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
398 use self::PropertyDeclaration::*;
399
400 let mut dest = CssWriter::new(dest);
401 match *self {
402 % for ty, vs in groupby(variants, key=lambda x: x["type"]):
403 ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
404 value.to_css(&mut dest)
405 }
406 % endfor
407 }
408 }
409 }
410
411 /// A module with all the code related to animated properties.
412 ///
413 /// This needs to be "included" by mako at least after all longhand modules,
414 /// given they populate the global data.
415 pub mod animated_properties {
416 <%include file="/helpers/animated_properties.mako.rs" />
417 }
418
419 /// A longhand or shorthand property.
420 #[derive(Clone, Copy, Debug)]
421 pub struct NonCustomPropertyId(usize);
422
423 impl NonCustomPropertyId {
424 #[cfg(feature = "gecko")]
to_nscsspropertyid(self) -> nsCSSPropertyID425 fn to_nscsspropertyid(self) -> nsCSSPropertyID {
426 static MAP: [nsCSSPropertyID; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [
427 % for property in data.longhands:
428 ${helpers.to_nscsspropertyid(property.ident)},
429 % endfor
430 % for property in data.shorthands:
431 ${helpers.to_nscsspropertyid(property.ident)},
432 % endfor
433 % for property in data.all_aliases():
434 ${helpers.alias_to_nscsspropertyid(property.ident)},
435 % endfor
436 ];
437
438 MAP[self.0]
439 }
440
441 #[inline]
enabled_for_all_content(self) -> bool442 fn enabled_for_all_content(self) -> bool {
443 ${static_non_custom_property_id_set(
444 "EXPERIMENTAL",
445 lambda p: p.experimental(product)
446 )}
447
448 ${static_non_custom_property_id_set(
449 "ALWAYS_ENABLED",
450 lambda p: (not p.experimental(product)) and p.enabled_in_content()
451 )}
452
453 let passes_pref_check = || {
454 % if product == "servo":
455 static PREF_NAME: [Option< &str>; ${len(data.longhands) + len(data.shorthands)}] = [
456 % for property in data.longhands + data.shorthands:
457 % if property.servo_pref:
458 Some("${property.servo_pref}"),
459 % else:
460 None,
461 % endif
462 % endfor
463 ];
464 let pref = match PREF_NAME[self.0] {
465 None => return true,
466 Some(pref) => pref,
467 };
468
469 PREFS.get(pref).as_boolean().unwrap_or(false)
470 % else:
471 unsafe { structs::nsCSSProps_gPropertyEnabled[self.to_nscsspropertyid() as usize] }
472 % endif
473 };
474
475 if ALWAYS_ENABLED.contains(self) {
476 return true
477 }
478
479 if EXPERIMENTAL.contains(self) && passes_pref_check() {
480 return true
481 }
482
483 false
484 }
485
allowed_in(self, context: &ParserContext) -> bool486 fn allowed_in(self, context: &ParserContext) -> bool {
487 debug_assert!(
488 matches!(
489 context.rule_type(),
490 CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style
491 ),
492 "Declarations are only expected inside a keyframe, page, or style rule."
493 );
494
495 ${static_non_custom_property_id_set(
496 "DISALLOWED_IN_KEYFRAME_BLOCK",
497 lambda p: not p.allowed_in_keyframe_block
498 )}
499 ${static_non_custom_property_id_set(
500 "DISALLOWED_IN_PAGE_RULE",
501 lambda p: not p.allowed_in_page_rule
502 )}
503 match context.rule_type() {
504 CssRuleType::Keyframe if DISALLOWED_IN_KEYFRAME_BLOCK.contains(self) => {
505 return false;
506 }
507 CssRuleType::Page if DISALLOWED_IN_PAGE_RULE.contains(self) => {
508 return false;
509 }
510 _ => {}
511 }
512
513 // The semantics of these are kinda hard to reason about, what follows
514 // is a description of the different combinations that can happen with
515 // these three sets.
516 //
517 // Experimental properties are generally controlled by prefs, but an
518 // experimental property explicitly enabled in certain context (UA or
519 // chrome sheets) is always usable in the context regardless of the
520 // pref value.
521 //
522 // Non-experimental properties are either normal properties which are
523 // usable everywhere, or internal-only properties which are only usable
524 // in certain context they are explicitly enabled in.
525 if self.enabled_for_all_content() {
526 return true;
527 }
528
529 ${static_non_custom_property_id_set(
530 "ENABLED_IN_UA_SHEETS",
531 lambda p: p.explicitly_enabled_in_ua_sheets()
532 )}
533 ${static_non_custom_property_id_set(
534 "ENABLED_IN_CHROME",
535 lambda p: p.explicitly_enabled_in_chrome()
536 )}
537
538 if context.stylesheet_origin == Origin::UserAgent &&
539 ENABLED_IN_UA_SHEETS.contains(self)
540 {
541 return true
542 }
543
544 if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) {
545 return true
546 }
547
548 false
549 }
550 }
551
552 impl From<LonghandId> for NonCustomPropertyId {
from(id: LonghandId) -> Self553 fn from(id: LonghandId) -> Self {
554 NonCustomPropertyId(id as usize)
555 }
556 }
557
558 impl From<ShorthandId> for NonCustomPropertyId {
from(id: ShorthandId) -> Self559 fn from(id: ShorthandId) -> Self {
560 NonCustomPropertyId((id as usize) + ${len(data.longhands)})
561 }
562 }
563
564 impl From<AliasId> for NonCustomPropertyId {
from(id: AliasId) -> Self565 fn from(id: AliasId) -> Self {
566 NonCustomPropertyId(id as usize + ${len(data.longhands) + len(data.shorthands)})
567 }
568 }
569
570 /// A set of all properties
571 #[derive(Clone, PartialEq)]
572 pub struct NonCustomPropertyIdSet {
573 storage: [u32; (${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())} - 1 + 32) / 32]
574 }
575
576 impl NonCustomPropertyIdSet {
577 /// Creates an empty `NonCustomPropertyIdSet`.
new() -> Self578 pub fn new() -> Self {
579 Self {
580 storage: Default::default(),
581 }
582 }
583
584 /// Insert a non-custom-property in the set.
585 #[inline]
insert(&mut self, id: NonCustomPropertyId)586 pub fn insert(&mut self, id: NonCustomPropertyId) {
587 let bit = id.0;
588 self.storage[bit / 32] |= 1 << (bit % 32);
589 }
590
591 /// Return whether the given property is in the set
592 #[inline]
contains(&self, id: NonCustomPropertyId) -> bool593 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
594 let bit = id.0;
595 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
596 }
597 }
598
599 <%def name="static_non_custom_property_id_set(name, is_member)">
600 static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {
601 <%
602 storage = [0] * ((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)
603 for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
604 if is_member(property):
605 storage[i / 32] |= 1 << (i % 32)
606 %>
607 storage: [${", ".join("0x%x" % word for word in storage)}]
608 };
609 </%def>
610
611 <%def name="static_longhand_id_set(name, is_member)">
612 static ${name}: LonghandIdSet = LonghandIdSet {
613 <%
614 storage = [0] * ((len(data.longhands) - 1 + 32) / 32)
615 for i, property in enumerate(data.longhands):
616 if is_member(property):
617 storage[i / 32] |= 1 << (i % 32)
618 %>
619 storage: [${", ".join("0x%x" % word for word in storage)}]
620 };
621 </%def>
622
623 /// A set of longhand properties
624 #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
625 pub struct LonghandIdSet {
626 storage: [u32; (${len(data.longhands)} - 1 + 32) / 32]
627 }
628
629 /// An iterator over a set of longhand ids.
630 pub struct LonghandIdSetIterator<'a> {
631 longhands: &'a LonghandIdSet,
632 cur: usize,
633 }
634
635 impl<'a> Iterator for LonghandIdSetIterator<'a> {
636 type Item = LonghandId;
637
next(&mut self) -> Option<Self::Item>638 fn next(&mut self) -> Option<Self::Item> {
639 use std::mem;
640
641 loop {
642 if self.cur >= ${len(data.longhands)} {
643 return None;
644 }
645
646 let id: LonghandId = unsafe { mem::transmute(self.cur as u16) };
647 self.cur += 1;
648
649 if self.longhands.contains(id) {
650 return Some(id);
651 }
652 }
653 }
654 }
655
656 impl LonghandIdSet {
657 /// Iterate over the current longhand id set.
iter(&self) -> LonghandIdSetIterator658 pub fn iter(&self) -> LonghandIdSetIterator {
659 LonghandIdSetIterator { longhands: self, cur: 0, }
660 }
661
662 /// Returns whether this set contains at least every longhand that `other`
663 /// also contains.
contains_all(&self, other: &Self) -> bool664 pub fn contains_all(&self, other: &Self) -> bool {
665 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
666 if (*self_cell & *other_cell) != *other_cell {
667 return false;
668 }
669 }
670 true
671 }
672
673 /// Returns whether this set contains any longhand that `other` also contains.
contains_any(&self, other: &Self) -> bool674 pub fn contains_any(&self, other: &Self) -> bool {
675 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
676 if (*self_cell & *other_cell) != 0 {
677 return true;
678 }
679 }
680 false
681 }
682
683 /// Create an empty set
684 #[inline]
new() -> LonghandIdSet685 pub fn new() -> LonghandIdSet {
686 LonghandIdSet { storage: [0; (${len(data.longhands)} - 1 + 32) / 32] }
687 }
688
689 /// Return whether the given property is in the set
690 #[inline]
contains(&self, id: LonghandId) -> bool691 pub fn contains(&self, id: LonghandId) -> bool {
692 let bit = id as usize;
693 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
694 }
695
696 /// Return whether this set contains any reset longhand.
697 #[inline]
contains_any_reset(&self) -> bool698 pub fn contains_any_reset(&self) -> bool {
699 ${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
700 self.contains_any(&RESET)
701 }
702
703 /// Add the given property to the set
704 #[inline]
insert(&mut self, id: LonghandId)705 pub fn insert(&mut self, id: LonghandId) {
706 let bit = id as usize;
707 self.storage[bit / 32] |= 1 << (bit % 32);
708 }
709
710 /// Remove the given property from the set
711 #[inline]
remove(&mut self, id: LonghandId)712 pub fn remove(&mut self, id: LonghandId) {
713 let bit = id as usize;
714 self.storage[bit / 32] &= !(1 << (bit % 32));
715 }
716
717 /// Clear all bits
718 #[inline]
clear(&mut self)719 pub fn clear(&mut self) {
720 for cell in &mut self.storage {
721 *cell = 0
722 }
723 }
724
725 /// Returns whether the set is empty.
726 #[inline]
is_empty(&self) -> bool727 pub fn is_empty(&self) -> bool {
728 self.storage.iter().all(|c| *c == 0)
729 }
730 }
731
732 /// An enum to represent a CSS Wide keyword.
733 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
734 pub enum CSSWideKeyword {
735 /// The `initial` keyword.
736 Initial,
737 /// The `inherit` keyword.
738 Inherit,
739 /// The `unset` keyword.
740 Unset,
741 }
742
743 impl CSSWideKeyword {
to_str(&self) -> &'static str744 fn to_str(&self) -> &'static str {
745 match *self {
746 CSSWideKeyword::Initial => "initial",
747 CSSWideKeyword::Inherit => "inherit",
748 CSSWideKeyword::Unset => "unset",
749 }
750 }
751
752 /// Takes the result of cssparser::Parser::expect_ident() and converts it
753 /// to a CSSWideKeyword.
from_ident<'i>(ident: &str) -> Option<Self>754 pub fn from_ident<'i>(ident: &str) -> Option<Self> {
755 match_ignore_ascii_case! { ident,
756 // If modifying this set of keyword, also update values::CustomIdent::from_ident
757 "initial" => Some(CSSWideKeyword::Initial),
758 "inherit" => Some(CSSWideKeyword::Inherit),
759 "unset" => Some(CSSWideKeyword::Unset),
760 _ => None
761 }
762 }
763 }
764
765 impl CSSWideKeyword {
parse(input: &mut Parser) -> Result<Self, ()>766 fn parse(input: &mut Parser) -> Result<Self, ()> {
767 let ident = input.expect_ident().map_err(|_| ())?.clone();
768 input.expect_exhausted().map_err(|_| ())?;
769 CSSWideKeyword::from_ident(&ident).ok_or(())
770 }
771 }
772
773 bitflags! {
774 /// A set of flags for properties.
775 pub struct PropertyFlags: u8 {
776 /// This property requires a stacking context.
777 const CREATES_STACKING_CONTEXT = 1 << 0;
778 /// This property has values that can establish a containing block for
779 /// fixed positioned and absolutely positioned elements.
780 const FIXPOS_CB = 1 << 1;
781 /// This property has values that can establish a containing block for
782 /// absolutely positioned elements.
783 const ABSPOS_CB = 1 << 2;
784 /// This longhand property applies to ::first-letter.
785 const APPLIES_TO_FIRST_LETTER = 1 << 3;
786 /// This longhand property applies to ::first-line.
787 const APPLIES_TO_FIRST_LINE = 1 << 4;
788 /// This longhand property applies to ::placeholder.
789 const APPLIES_TO_PLACEHOLDER = 1 << 5;
790 }
791 }
792
793 /// An identifier for a given longhand property.
794 #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq)]
795 #[repr(u16)]
796 pub enum LonghandId {
797 % for i, property in enumerate(data.longhands):
798 /// ${property.name}
799 ${property.camel_case} = ${i},
800 % endfor
801 }
802
803 impl ToCss for LonghandId {
804 #[inline]
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,805 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
806 where
807 W: Write,
808 {
809 dest.write_str(self.name())
810 }
811 }
812
813 impl fmt::Debug for LonghandId {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result814 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
815 formatter.write_str(self.name())
816 }
817 }
818
819 impl LonghandId {
820 /// Get the name of this longhand property.
name(&self) -> &'static str821 pub fn name(&self) -> &'static str {
822 match *self {
823 % for property in data.longhands:
824 LonghandId::${property.camel_case} => "${property.name}",
825 % endfor
826 }
827 }
828
inherited(&self) -> bool829 fn inherited(&self) -> bool {
830 ${static_longhand_id_set("INHERITED", lambda p: p.style_struct.inherited)}
831 INHERITED.contains(*self)
832 }
833
shorthands(&self) -> NonCustomPropertyIterator<ShorthandId>834 fn shorthands(&self) -> NonCustomPropertyIterator<ShorthandId> {
835 // first generate longhand to shorthands lookup map
836 //
837 // NOTE(emilio): This currently doesn't exclude the "all" shorthand. It
838 // could potentially do so, which would speed up serialization
839 // algorithms and what not, I guess.
840 <%
841 longhand_to_shorthand_map = {}
842 num_sub_properties = {}
843 for shorthand in data.shorthands:
844 num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties)
845 for sub_property in shorthand.sub_properties:
846 if sub_property.ident not in longhand_to_shorthand_map:
847 longhand_to_shorthand_map[sub_property.ident] = []
848
849 longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)
850
851 def preferred_order(x, y):
852 # Since we want properties in order from most subproperties to least,
853 # reverse the arguments to cmp from the expected order.
854 result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0))
855 if result:
856 return result
857 # Fall back to lexicographic comparison.
858 return cmp(x, y)
859
860 # Sort the lists of shorthand properties according to preferred order:
861 # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order
862 for shorthand_list in longhand_to_shorthand_map.itervalues():
863 shorthand_list.sort(cmp=preferred_order)
864 %>
865
866 // based on lookup results for each longhand, create result arrays
867 % for property in data.longhands:
868 static ${property.ident.upper()}: &'static [ShorthandId] = &[
869 % for shorthand in longhand_to_shorthand_map.get(property.ident, []):
870 ShorthandId::${shorthand},
871 % endfor
872 ];
873 % endfor
874
875 NonCustomPropertyIterator {
876 filter: NonCustomPropertyId::from(*self).enabled_for_all_content(),
877 iter: match *self {
878 % for property in data.longhands:
879 LonghandId::${property.camel_case} => ${property.ident.upper()},
880 % endfor
881 }.iter(),
882 }
883 }
884
parse_value<'i, 't>( &self, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<PropertyDeclaration, ParseError<'i>>885 fn parse_value<'i, 't>(
886 &self,
887 context: &ParserContext,
888 input: &mut Parser<'i, 't>,
889 ) -> Result<PropertyDeclaration, ParseError<'i>> {
890 match *self {
891 % for property in data.longhands:
892 LonghandId::${property.camel_case} => {
893 longhands::${property.ident}::parse_declared(context, input)
894 }
895 % endfor
896 }
897 }
898
899 /// Returns whether this property is animatable.
is_animatable(self) -> bool900 pub fn is_animatable(self) -> bool {
901 match self {
902 % for property in data.longhands:
903 LonghandId::${property.camel_case} => {
904 ${str(property.animatable).lower()}
905 }
906 % endfor
907 }
908 }
909
910 /// Returns whether this property is animatable in a discrete way.
is_discrete_animatable(self) -> bool911 pub fn is_discrete_animatable(self) -> bool {
912 match self {
913 % for property in data.longhands:
914 LonghandId::${property.camel_case} => {
915 ${str(property.animation_value_type == "discrete").lower()}
916 }
917 % endfor
918 }
919 }
920
921 /// Converts from a LonghandId to an adequate nsCSSPropertyID.
922 #[cfg(feature = "gecko")]
923 #[inline]
to_nscsspropertyid(self) -> nsCSSPropertyID924 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
925 NonCustomPropertyId::from(self).to_nscsspropertyid()
926 }
927
928 #[cfg(feature = "gecko")]
929 #[allow(non_upper_case_globals)]
930 /// Returns a longhand id from Gecko's nsCSSPropertyID.
from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()>931 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
932 match PropertyId::from_nscsspropertyid(id) {
933 Ok(PropertyId::Longhand(id)) |
934 Ok(PropertyId::LonghandAlias(id, _)) => Ok(id),
935 _ => Err(()),
936 }
937 }
938
939 /// If this is a logical property, return the corresponding physical one in the given writing mode.
940 /// Otherwise, return unchanged.
to_physical(&self, wm: WritingMode) -> Self941 pub fn to_physical(&self, wm: WritingMode) -> Self {
942 match *self {
943 % for property in data.longhands:
944 % if property.logical:
945 LonghandId::${property.camel_case} => {
946 <%helpers:logical_setter_helper name="${property.name}">
947 <%def name="inner(physical_ident)">
948 LonghandId::${to_camel_case(physical_ident)}
949 </%def>
950 </%helpers:logical_setter_helper>
951 }
952 % endif
953 % endfor
954 _ => *self
955 }
956 }
957
958 /// Returns PropertyFlags for given longhand property.
flags(&self) -> PropertyFlags959 pub fn flags(&self) -> PropertyFlags {
960 match *self {
961 % for property in data.longhands:
962 LonghandId::${property.camel_case} =>
963 % for flag in property.flags:
964 PropertyFlags::${flag} |
965 % endfor
966 PropertyFlags::empty(),
967 % endfor
968 }
969 }
970
971 /// Only a few properties are allowed to depend on the visited state of
972 /// links. When cascading visited styles, we can save time by only
973 /// processing these properties.
is_visited_dependent(&self) -> bool974 fn is_visited_dependent(&self) -> bool {
975 matches!(*self,
976 % if product == "gecko":
977 LonghandId::ColumnRuleColor |
978 LonghandId::TextEmphasisColor |
979 LonghandId::WebkitTextFillColor |
980 LonghandId::WebkitTextStrokeColor |
981 LonghandId::TextDecorationColor |
982 LonghandId::Fill |
983 LonghandId::Stroke |
984 LonghandId::CaretColor |
985 % endif
986 LonghandId::Color |
987 LonghandId::BackgroundColor |
988 LonghandId::BorderTopColor |
989 LonghandId::BorderRightColor |
990 LonghandId::BorderBottomColor |
991 LonghandId::BorderLeftColor |
992 LonghandId::OutlineColor
993 )
994 }
995
996 /// Returns true if the property is one that is ignored when document
997 /// colors are disabled.
is_ignored_when_document_colors_disabled( &self, cascade_level: CascadeLevel, pseudo: Option<<&PseudoElement>, ) -> bool998 fn is_ignored_when_document_colors_disabled(
999 &self,
1000 cascade_level: CascadeLevel,
1001 pseudo: Option<<&PseudoElement>,
1002 ) -> bool {
1003 let is_ua_or_user_rule = matches!(
1004 cascade_level,
1005 CascadeLevel::UANormal |
1006 CascadeLevel::UserNormal |
1007 CascadeLevel::UserImportant |
1008 CascadeLevel::UAImportant
1009 );
1010
1011 if is_ua_or_user_rule {
1012 return false;
1013 }
1014
1015 let is_style_attribute = matches!(
1016 cascade_level,
1017 CascadeLevel::StyleAttributeNormal |
1018 CascadeLevel::StyleAttributeImportant
1019 );
1020 // Don't override colors on pseudo-element's style attributes. The
1021 // background-color on ::-moz-color-swatch is an example. Those are set
1022 // as an author style (via the style attribute), but it's pretty
1023 // important for it to show up for obvious reasons :)
1024 if pseudo.is_some() && is_style_attribute {
1025 return false;
1026 }
1027
1028 matches!(*self,
1029 ${" | ".join([("LonghandId::" + p.camel_case)
1030 for p in data.longhands if p.ignored_when_colors_disabled])}
1031 )
1032 }
1033
1034 /// The computed value of some properties depends on the (sometimes
1035 /// computed) value of *other* properties.
1036 ///
1037 /// So we classify properties into "early" and "other", such that the only
1038 /// dependencies can be from "other" to "early".
1039 ///
1040 /// Unfortunately, it’s not easy to check that this classification is
1041 /// correct.
is_early_property(&self) -> bool1042 fn is_early_property(&self) -> bool {
1043 matches!(*self,
1044 % if product == 'gecko':
1045
1046 // Needed to properly compute the writing mode, to resolve logical
1047 // properties, and similar stuff. In this block instead of along
1048 // `WritingMode` and `Direction` just for convenience, since it's
1049 // Gecko-only (for now at least).
1050 //
1051 // see WritingMode::new.
1052 LonghandId::TextOrientation |
1053
1054 // Needed to properly compute the zoomed font-size.
1055 //
1056 // FIXME(emilio): This could probably just be a cascade flag like
1057 // IN_SVG_SUBTREE or such, and we could nuke this property.
1058 LonghandId::XTextZoom |
1059
1060 // Needed to do font-size computation in a language-dependent way.
1061 LonghandId::XLang |
1062 // Needed for ruby to respect language-dependent min-font-size
1063 // preferences properly, see bug 1165538.
1064 LonghandId::MozMinFontSizeRatio |
1065
1066 // Needed to do font-size for MathML. :(
1067 LonghandId::MozScriptLevel |
1068 % endif
1069
1070 // Needed to compute font-relative lengths correctly.
1071 LonghandId::FontSize |
1072 LonghandId::FontFamily |
1073
1074 // Needed to resolve currentcolor at computed value time properly.
1075 //
1076 // FIXME(emilio): All the properties should be moved to currentcolor
1077 // as a computed-value (and thus resolving it at used-value time).
1078 //
1079 // This would allow this property to go away from this list.
1080 LonghandId::Color |
1081
1082 // FIXME(emilio): There's no reason for this afaict, nuke it.
1083 LonghandId::TextDecorationLine |
1084
1085 // Needed to properly compute the writing mode, to resolve logical
1086 // properties, and similar stuff.
1087 LonghandId::WritingMode |
1088 LonghandId::Direction
1089 )
1090 }
1091
1092 /// Whether computed values of this property lossily convert any complex
1093 /// colors into RGBA colors.
1094 ///
1095 /// In Gecko, there are some properties still that compute currentcolor
1096 /// down to an RGBA color at computed value time, instead of as
1097 /// `StyleComplexColor`s. For these properties, we must return `false`,
1098 /// so that we correctly avoid caching style data in the rule tree.
stores_complex_colors_lossily(&self) -> bool1099 pub fn stores_complex_colors_lossily(&self) -> bool {
1100 % if product == "gecko":
1101 matches!(*self,
1102 % for property in data.longhands:
1103 % if property.predefined_type == "RGBAColor":
1104 LonghandId::${property.camel_case} |
1105 % endif
1106 % endfor
1107 LonghandId::BackgroundImage |
1108 LonghandId::BorderImageSource |
1109 LonghandId::BoxShadow |
1110 LonghandId::MaskImage |
1111 LonghandId::TextShadow
1112 )
1113 % else:
1114 false
1115 % endif
1116 }
1117 }
1118
1119 /// An iterator over all the property ids that are enabled for a given
1120 /// shorthand, if that shorthand is enabled for all content too.
1121 pub struct NonCustomPropertyIterator<Item: 'static> {
1122 filter: bool,
1123 iter: ::std::slice::Iter<'static, Item>,
1124 }
1125
1126 impl<Item> Iterator for NonCustomPropertyIterator<Item>
1127 where
1128 Item: 'static + Copy + Into<NonCustomPropertyId>,
1129 {
1130 type Item = Item;
1131
next(&mut self) -> Option<Self::Item>1132 fn next(&mut self) -> Option<Self::Item> {
1133 loop {
1134 let id = *self.iter.next()?;
1135 if !self.filter || id.into().enabled_for_all_content() {
1136 return Some(id)
1137 }
1138 }
1139 }
1140 }
1141
1142 /// An identifier for a given shorthand property.
1143 #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
1144 pub enum ShorthandId {
1145 % for property in data.shorthands:
1146 /// ${property.name}
1147 ${property.camel_case},
1148 % endfor
1149 }
1150
1151 impl ToCss for ShorthandId {
1152 #[inline]
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1153 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1154 where
1155 W: Write,
1156 {
1157 dest.write_str(self.name())
1158 }
1159 }
1160
1161 impl ShorthandId {
1162 /// Get the name for this shorthand property.
name(&self) -> &'static str1163 pub fn name(&self) -> &'static str {
1164 match *self {
1165 % for property in data.shorthands:
1166 ShorthandId::${property.camel_case} => "${property.name}",
1167 % endfor
1168 }
1169 }
1170
1171 /// Converts from a ShorthandId to an adequate nsCSSPropertyID.
1172 #[cfg(feature = "gecko")]
1173 #[inline]
to_nscsspropertyid(self) -> nsCSSPropertyID1174 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
1175 NonCustomPropertyId::from(self).to_nscsspropertyid()
1176 }
1177
1178 /// Get the longhand ids that form this shorthand.
longhands(&self) -> NonCustomPropertyIterator<LonghandId>1179 pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> {
1180 % for property in data.shorthands:
1181 static ${property.ident.upper()}: &'static [LonghandId] = &[
1182 % for sub in property.sub_properties:
1183 LonghandId::${sub.camel_case},
1184 % endfor
1185 ];
1186 % endfor
1187 NonCustomPropertyIterator {
1188 filter: NonCustomPropertyId::from(*self).enabled_for_all_content(),
1189 iter: match *self {
1190 % for property in data.shorthands:
1191 ShorthandId::${property.camel_case} => ${property.ident.upper()},
1192 % endfor
1193 }.iter()
1194 }
1195 }
1196
1197 /// Try to serialize the given declarations as this shorthand.
1198 ///
1199 /// Returns an error if writing to the stream fails, or if the declarations
1200 /// do not map to a shorthand.
longhands_to_css<'a, W, I>( &self, declarations: I, dest: &mut CssWriter<W>, ) -> fmt::Result where W: Write, I: Iterator<Item=&'a PropertyDeclaration>,1201 pub fn longhands_to_css<'a, W, I>(
1202 &self,
1203 declarations: I,
1204 dest: &mut CssWriter<W>,
1205 ) -> fmt::Result
1206 where
1207 W: Write,
1208 I: Iterator<Item=&'a PropertyDeclaration>,
1209 {
1210 match *self {
1211 ShorthandId::All => {
1212 // No need to try to serialize the declarations as the 'all'
1213 // shorthand, since it only accepts CSS-wide keywords (and
1214 // variable references), which will be handled in
1215 // get_shorthand_appendable_value.
1216 Err(fmt::Error)
1217 }
1218 % for property in data.shorthands_except_all():
1219 ShorthandId::${property.camel_case} => {
1220 match shorthands::${property.ident}::LonghandsToSerialize::from_iter(declarations) {
1221 Ok(longhands) => longhands.to_css(dest),
1222 Err(_) => Err(fmt::Error)
1223 }
1224 },
1225 % endfor
1226 }
1227 }
1228
1229 /// Finds and returns an appendable value for the given declarations.
1230 ///
1231 /// Returns the optional appendable value.
get_shorthand_appendable_value<'a, I>( self, declarations: I, ) -> Option<AppendableValue<'a, I::IntoIter>> where I: IntoIterator<Item=&'a PropertyDeclaration>, I::IntoIter: Clone,1232 pub fn get_shorthand_appendable_value<'a, I>(
1233 self,
1234 declarations: I,
1235 ) -> Option<AppendableValue<'a, I::IntoIter>>
1236 where
1237 I: IntoIterator<Item=&'a PropertyDeclaration>,
1238 I::IntoIter: Clone,
1239 {
1240 let declarations = declarations.into_iter();
1241
1242 // Only cloning iterators (a few pointers each) not declarations.
1243 let mut declarations2 = declarations.clone();
1244 let mut declarations3 = declarations.clone();
1245
1246 let first_declaration = declarations2.next()?;
1247
1248 // https://drafts.csswg.org/css-variables/#variables-in-shorthands
1249 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
1250 if declarations2.all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
1251 return Some(AppendableValue::Css {
1252 css: CssStringBorrow::from(css),
1253 with_variables: true,
1254 });
1255 }
1256 return None;
1257 }
1258
1259 // Check whether they are all the same CSS-wide keyword.
1260 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
1261 if declarations2.all(|d| d.get_css_wide_keyword() == Some(keyword)) {
1262 return Some(AppendableValue::Css {
1263 css: CssStringBorrow::from(keyword.to_str()),
1264 with_variables: false,
1265 });
1266 }
1267 return None;
1268 }
1269
1270 // Check whether all declarations can be serialized as part of shorthand.
1271 if declarations3.all(|d| d.may_serialize_as_part_of_shorthand()) {
1272 return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
1273 }
1274
1275 None
1276 }
1277
1278 /// Returns PropertyFlags for given shorthand property.
flags(&self) -> PropertyFlags1279 pub fn flags(&self) -> PropertyFlags {
1280 match *self {
1281 % for property in data.shorthands:
1282 ShorthandId::${property.camel_case} =>
1283 % for flag in property.flags:
1284 PropertyFlags::${flag} |
1285 % endfor
1286 PropertyFlags::empty(),
1287 % endfor
1288 }
1289 }
1290
parse_into<'i, 't>( &self, declarations: &mut SourcePropertyDeclaration, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>1291 fn parse_into<'i, 't>(
1292 &self,
1293 declarations: &mut SourcePropertyDeclaration,
1294 context: &ParserContext,
1295 input: &mut Parser<'i, 't>,
1296 ) -> Result<(), ParseError<'i>> {
1297 match *self {
1298 % for shorthand in data.shorthands_except_all():
1299 ShorthandId::${shorthand.camel_case} => {
1300 shorthands::${shorthand.ident}::parse_into(declarations, context, input)
1301 }
1302 % endfor
1303 // 'all' accepts no value other than CSS-wide keywords
1304 ShorthandId::All => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1305 }
1306 }
1307 }
1308
1309 /// Servo's representation of a declared value for a given `T`, which is the
1310 /// declared value for that property.
1311 #[derive(Clone, Debug, Eq, PartialEq)]
1312 pub enum DeclaredValue<'a, T: 'a> {
1313 /// A known specified value from the stylesheet.
1314 Value(&'a T),
1315 /// An unparsed value that contains `var()` functions.
1316 WithVariables(&'a Arc<UnparsedValue>),
1317 /// An CSS-wide keyword.
1318 CSSWideKeyword(CSSWideKeyword),
1319 }
1320
1321 /// A variant of DeclaredValue that owns its data. This separation exists so
1322 /// that PropertyDeclaration can avoid embedding a DeclaredValue (and its
1323 /// extra discriminant word) and synthesize dependent DeclaredValues for
1324 /// PropertyDeclaration instances as needed.
1325 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1326 #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
1327 pub enum DeclaredValueOwned<T> {
1328 /// A known specified value from the stylesheet.
1329 Value(T),
1330 /// An unparsed value that contains `var()` functions.
1331 WithVariables(
1332 #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
1333 Arc<UnparsedValue>
1334 ),
1335 /// An CSS-wide keyword.
1336 CSSWideKeyword(CSSWideKeyword),
1337 }
1338
1339 impl<T> DeclaredValueOwned<T> {
1340 /// Creates a dependent DeclaredValue from this DeclaredValueOwned.
borrow(&self) -> DeclaredValue<T>1341 fn borrow(&self) -> DeclaredValue<T> {
1342 match *self {
1343 DeclaredValueOwned::Value(ref v) => DeclaredValue::Value(v),
1344 DeclaredValueOwned::WithVariables(ref v) => DeclaredValue::WithVariables(v),
1345 DeclaredValueOwned::CSSWideKeyword(v) => DeclaredValue::CSSWideKeyword(v),
1346 }
1347 }
1348 }
1349
1350 /// An unparsed property value that contains `var()` functions.
1351 #[derive(Debug, Eq, PartialEq)]
1352 pub struct UnparsedValue {
1353 /// The css serialization for this value.
1354 css: String,
1355 /// The first token type for this serialization.
1356 first_token_type: TokenSerializationType,
1357 /// The url data for resolving url values.
1358 url_data: UrlExtraData,
1359 /// The shorthand this came from.
1360 from_shorthand: Option<ShorthandId>,
1361 }
1362
1363 impl ToCss for UnparsedValue {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1364 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1365 where
1366 W: Write,
1367 {
1368 // https://drafts.csswg.org/css-variables/#variables-in-shorthands
1369 if self.from_shorthand.is_none() {
1370 dest.write_str(&*self.css)?;
1371 }
1372 Ok(())
1373 }
1374 }
1375
1376 impl UnparsedValue {
substitute_variables( &self, longhand_id: LonghandId, custom_properties: Option<<&Arc<::custom_properties::CustomPropertiesMap>>, quirks_mode: QuirksMode, ) -> PropertyDeclaration1377 fn substitute_variables(
1378 &self,
1379 longhand_id: LonghandId,
1380 custom_properties: Option<<&Arc<::custom_properties::CustomPropertiesMap>>,
1381 quirks_mode: QuirksMode,
1382 ) -> PropertyDeclaration {
1383 ::custom_properties::substitute(&self.css, self.first_token_type, custom_properties)
1384 .ok()
1385 .and_then(|css| {
1386 // As of this writing, only the base URL is used for property
1387 // values.
1388 let context = ParserContext::new(
1389 Origin::Author,
1390 &self.url_data,
1391 None,
1392 ParsingMode::DEFAULT,
1393 quirks_mode,
1394 );
1395
1396 let mut input = ParserInput::new(&css);
1397 Parser::new(&mut input).parse_entirely(|input| {
1398 match self.from_shorthand {
1399 None => longhand_id.parse_value(&context, input),
1400 Some(ShorthandId::All) => {
1401 // No need to parse the 'all' shorthand as anything other than a CSS-wide
1402 // keyword, after variable substitution.
1403 Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("all".into())))
1404 }
1405 % for shorthand in data.shorthands_except_all():
1406 Some(ShorthandId::${shorthand.camel_case}) => {
1407 shorthands::${shorthand.ident}::parse_value(&context, input)
1408 .map(|longhands| {
1409 match longhand_id {
1410 % for property in shorthand.sub_properties:
1411 LonghandId::${property.camel_case} => {
1412 PropertyDeclaration::${property.camel_case}(
1413 longhands.${property.ident}
1414 )
1415 }
1416 % endfor
1417 _ => unreachable!()
1418 }
1419 })
1420 }
1421 % endfor
1422 }
1423 })
1424 .ok()
1425 })
1426 .unwrap_or_else(|| {
1427 // Invalid at computed-value time.
1428 let keyword = if longhand_id.inherited() {
1429 CSSWideKeyword::Inherit
1430 } else {
1431 CSSWideKeyword::Initial
1432 };
1433 PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
1434 id: longhand_id,
1435 keyword,
1436 })
1437 })
1438 }
1439 }
1440
1441 /// An identifier for a given property declaration, which can be either a
1442 /// longhand or a custom property.
1443 #[derive(Clone, Copy, PartialEq)]
1444 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
1445 pub enum PropertyDeclarationId<'a> {
1446 /// A longhand.
1447 Longhand(LonghandId),
1448 /// A custom property declaration.
1449 Custom(&'a ::custom_properties::Name),
1450 }
1451
1452 impl<'a> ToCss for PropertyDeclarationId<'a> {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1453 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1454 where
1455 W: Write,
1456 {
1457 match *self {
1458 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1459 PropertyDeclarationId::Custom(_) => {
1460 serialize_identifier(&self.name(), dest)
1461 }
1462 }
1463 }
1464 }
1465
1466 impl<'a> PropertyDeclarationId<'a> {
1467 /// Whether a given declaration id is either the same as `other`, or a
1468 /// longhand of it.
is_or_is_longhand_of(&self, other: &PropertyId) -> bool1469 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1470 match *self {
1471 PropertyDeclarationId::Longhand(id) => {
1472 match *other {
1473 PropertyId::Longhand(other_id) |
1474 PropertyId::LonghandAlias(other_id, _) => id == other_id,
1475 PropertyId::Shorthand(shorthand) |
1476 PropertyId::ShorthandAlias(shorthand, _) => self.is_longhand_of(shorthand),
1477 PropertyId::Custom(_) => false,
1478 }
1479 }
1480 PropertyDeclarationId::Custom(name) => {
1481 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1482 }
1483 }
1484 }
1485
1486 /// Whether a given declaration id is a longhand belonging to this
1487 /// shorthand.
is_longhand_of(&self, shorthand: ShorthandId) -> bool1488 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1489 match *self {
1490 PropertyDeclarationId::Longhand(ref id) => id.shorthands().any(|s| s == shorthand),
1491 _ => false,
1492 }
1493 }
1494
1495 /// Returns the name of the property without CSS escaping.
name(&self) -> Cow<'static, str>1496 pub fn name(&self) -> Cow<'static, str> {
1497 match *self {
1498 PropertyDeclarationId::Longhand(id) => id.name().into(),
1499 PropertyDeclarationId::Custom(name) => {
1500 let mut s = String::new();
1501 write!(&mut s, "--{}", name).unwrap();
1502 s.into()
1503 }
1504 }
1505 }
1506 }
1507
1508 /// Servo's representation of a CSS property, that is, either a longhand, a
1509 /// shorthand, or a custom property.
1510 #[derive(Clone, Eq, PartialEq)]
1511 pub enum PropertyId {
1512 /// A longhand property.
1513 Longhand(LonghandId),
1514 /// A shorthand property.
1515 Shorthand(ShorthandId),
1516 /// An alias for a longhand property.
1517 LonghandAlias(LonghandId, AliasId),
1518 /// An alias for a shorthand property.
1519 ShorthandAlias(ShorthandId, AliasId),
1520 /// A custom property.
1521 Custom(::custom_properties::Name),
1522 }
1523
1524 impl fmt::Debug for PropertyId {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result1525 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1526 self.to_css(&mut CssWriter::new(formatter))
1527 }
1528 }
1529
1530 impl ToCss for PropertyId {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1531 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1532 where
1533 W: Write,
1534 {
1535 match *self {
1536 PropertyId::Longhand(id) => dest.write_str(id.name()),
1537 PropertyId::Shorthand(id) => dest.write_str(id.name()),
1538 PropertyId::LonghandAlias(id, _) => dest.write_str(id.name()),
1539 PropertyId::ShorthandAlias(id, _) => dest.write_str(id.name()),
1540 PropertyId::Custom(_) => {
1541 serialize_identifier(&self.name(), dest)
1542 }
1543 }
1544 }
1545 }
1546
1547 impl PropertyId {
1548 /// Return the longhand id that this property id represents.
1549 #[inline]
longhand_id(&self) -> Option<LonghandId>1550 pub fn longhand_id(&self) -> Option<LonghandId> {
1551 Some(match *self {
1552 PropertyId::Longhand(id) => id,
1553 PropertyId::LonghandAlias(id, _) => id,
1554 _ => return None,
1555 })
1556 }
1557
1558 /// Returns a given property from the string `s`.
1559 ///
1560 /// Returns Err(()) for unknown non-custom properties.
parse(property_name: &str) -> Result<Self, ()>1561 pub fn parse(property_name: &str) -> Result<Self, ()> {
1562 // FIXME(https://github.com/rust-lang/rust/issues/33156): remove this
1563 // enum and use PropertyId when stable Rust allows destructors in
1564 // statics.
1565 //
1566 // ShorthandAlias is not used in the Servo build.
1567 // That's why we need to allow dead_code.
1568 #[allow(dead_code)]
1569 pub enum StaticId {
1570 Longhand(LonghandId),
1571 Shorthand(ShorthandId),
1572 LonghandAlias(LonghandId, AliasId),
1573 ShorthandAlias(ShorthandId, AliasId),
1574 }
1575 ascii_case_insensitive_phf_map! {
1576 static_id -> StaticId = {
1577 % for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
1578 % for property in properties:
1579 "${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}),
1580 % for name in property.alias:
1581 "${name}" => {
1582 StaticId::${kind}Alias(${kind}Id::${property.camel_case},
1583 AliasId::${to_camel_case(name)})
1584 },
1585 % endfor
1586 % endfor
1587 % endfor
1588 }
1589 }
1590
1591 Ok(match static_id(property_name) {
1592 Some(&StaticId::Longhand(id)) => {
1593 PropertyId::Longhand(id)
1594 },
1595 Some(&StaticId::Shorthand(id)) => {
1596 PropertyId::Shorthand(id)
1597 },
1598 Some(&StaticId::LonghandAlias(id, alias)) => {
1599 PropertyId::LonghandAlias(id, alias)
1600 },
1601 Some(&StaticId::ShorthandAlias(id, alias)) => {
1602 PropertyId::ShorthandAlias(id, alias)
1603 },
1604 None => {
1605 return ::custom_properties::parse_name(property_name).map(|name| {
1606 PropertyId::Custom(::custom_properties::Name::from(name))
1607 })
1608 },
1609 })
1610 }
1611
1612 /// Returns a property id from Gecko's nsCSSPropertyID.
1613 #[cfg(feature = "gecko")]
1614 #[allow(non_upper_case_globals)]
from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()>1615 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
1616 use gecko_bindings::structs::*;
1617 match id {
1618 % for property in data.longhands:
1619 ${helpers.to_nscsspropertyid(property.ident)} => {
1620 Ok(PropertyId::Longhand(LonghandId::${property.camel_case}))
1621 }
1622 % for alias in property.alias:
1623 ${helpers.alias_to_nscsspropertyid(alias)} => {
1624 Ok(PropertyId::LonghandAlias(
1625 LonghandId::${property.camel_case},
1626 AliasId::${to_camel_case(alias)}
1627 ))
1628 }
1629 % endfor
1630 % endfor
1631 % for property in data.shorthands:
1632 ${helpers.to_nscsspropertyid(property.ident)} => {
1633 Ok(PropertyId::Shorthand(ShorthandId::${property.camel_case}))
1634 }
1635 % for alias in property.alias:
1636 ${helpers.alias_to_nscsspropertyid(alias)} => {
1637 Ok(PropertyId::ShorthandAlias(
1638 ShorthandId::${property.camel_case},
1639 AliasId::${to_camel_case(alias)}
1640 ))
1641 }
1642 % endfor
1643 % endfor
1644 _ => Err(())
1645 }
1646 }
1647
1648 /// Given this property id, get it either as a shorthand or as a
1649 /// `PropertyDeclarationId`.
as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId>1650 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId> {
1651 match *self {
1652 PropertyId::ShorthandAlias(id, _) |
1653 PropertyId::Shorthand(id) => Ok(id),
1654 PropertyId::LonghandAlias(id, _) |
1655 PropertyId::Longhand(id) => Err(PropertyDeclarationId::Longhand(id)),
1656 PropertyId::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
1657 }
1658 }
1659
1660 /// Returns the name of the property without CSS escaping.
name(&self) -> Cow<'static, str>1661 pub fn name(&self) -> Cow<'static, str> {
1662 match *self {
1663 PropertyId::ShorthandAlias(id, _) |
1664 PropertyId::Shorthand(id) => id.name().into(),
1665 PropertyId::LonghandAlias(id, _) |
1666 PropertyId::Longhand(id) => id.name().into(),
1667 PropertyId::Custom(ref name) => {
1668 let mut s = String::new();
1669 write!(&mut s, "--{}", name).unwrap();
1670 s.into()
1671 }
1672 }
1673 }
1674
non_custom_id(&self) -> Option<NonCustomPropertyId>1675 fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
1676 Some(match *self {
1677 PropertyId::Custom(_) => return None,
1678 PropertyId::Shorthand(shorthand_id) => shorthand_id.into(),
1679 PropertyId::Longhand(longhand_id) => longhand_id.into(),
1680 PropertyId::ShorthandAlias(_, alias_id) => alias_id.into(),
1681 PropertyId::LonghandAlias(_, alias_id) => alias_id.into(),
1682 })
1683 }
1684
1685 /// Whether the property is enabled for all content regardless of the
1686 /// stylesheet it was declared on (that is, in practice only checks prefs).
1687 #[inline]
enabled_for_all_content(&self) -> bool1688 pub fn enabled_for_all_content(&self) -> bool {
1689 let id = match self.non_custom_id() {
1690 // Custom properties are allowed everywhere
1691 None => return true,
1692 Some(id) => id,
1693 };
1694
1695 id.enabled_for_all_content()
1696 }
1697
allowed_in(&self, context: &ParserContext) -> bool1698 fn allowed_in(&self, context: &ParserContext) -> bool {
1699 let id = match self.non_custom_id() {
1700 // Custom properties are allowed everywhere
1701 None => return true,
1702 Some(id) => id,
1703 };
1704 id.allowed_in(context)
1705 }
1706 }
1707
1708 /// A declaration using a CSS-wide keyword.
1709 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1710 #[derive(Clone, PartialEq, ToCss)]
1711 pub struct WideKeywordDeclaration {
1712 #[css(skip)]
1713 id: LonghandId,
1714 keyword: CSSWideKeyword,
1715 }
1716
1717 /// An unparsed declaration that contains `var()` functions.
1718 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1719 #[derive(Clone, PartialEq, ToCss)]
1720 pub struct VariableDeclaration {
1721 #[css(skip)]
1722 id: LonghandId,
1723 #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
1724 value: Arc<UnparsedValue>,
1725 }
1726
1727 /// A custom property declaration with the property name and the declared value.
1728 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1729 #[derive(Clone, PartialEq, ToCss)]
1730 pub struct CustomDeclaration {
1731 /// The name of the custom property.
1732 #[css(skip)]
1733 pub name: ::custom_properties::Name,
1734 /// The value of the custom property.
1735 #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
1736 pub value: DeclaredValueOwned<Arc<::custom_properties::SpecifiedValue>>,
1737 }
1738
1739 impl fmt::Debug for PropertyDeclaration {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1740 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1741 self.id().to_css(&mut CssWriter::new(f))?;
1742 f.write_str(": ")?;
1743
1744 // Because PropertyDeclaration::to_css requires CssStringWriter, we can't write
1745 // it directly to f, and need to allocate an intermediate string. This is
1746 // fine for debug-only code.
1747 let mut s = CssString::new();
1748 self.to_css(&mut s)?;
1749 write!(f, "{}", s)
1750 }
1751 }
1752
1753 impl PropertyDeclaration {
1754 /// Given a property declaration, return the property declaration id.
1755 #[inline]
id(&self) -> PropertyDeclarationId1756 pub fn id(&self) -> PropertyDeclarationId {
1757 match *self {
1758 PropertyDeclaration::Custom(ref declaration) => {
1759 return PropertyDeclarationId::Custom(&declaration.name)
1760 }
1761 PropertyDeclaration::CSSWideKeyword(ref declaration) => {
1762 return PropertyDeclarationId::Longhand(declaration.id);
1763 }
1764 PropertyDeclaration::WithVariables(ref declaration) => {
1765 return PropertyDeclarationId::Longhand(declaration.id);
1766 }
1767 _ => {}
1768 }
1769 // This is just fine because PropertyDeclarationId and LonghandId
1770 // have corresponding discriminants.
1771 let id = unsafe { *(self as *const _ as *const LonghandId) };
1772 debug_assert_eq!(id, match *self {
1773 % for property in data.longhands:
1774 PropertyDeclaration::${property.camel_case}(..) => LonghandId::${property.camel_case},
1775 % endfor
1776 _ => id,
1777 });
1778 PropertyDeclarationId::Longhand(id)
1779 }
1780
with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str>1781 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str> {
1782 match *self {
1783 PropertyDeclaration::WithVariables(ref declaration) => {
1784 let s = declaration.value.from_shorthand?;
1785 if s != shorthand {
1786 return None;
1787 }
1788 Some(&*declaration.value.css)
1789 },
1790 _ => None,
1791 }
1792 }
1793
1794 /// Returns a CSS-wide keyword if the declaration's value is one.
get_css_wide_keyword(&self) -> Option<CSSWideKeyword>1795 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
1796 match *self {
1797 PropertyDeclaration::CSSWideKeyword(ref declaration) => {
1798 Some(declaration.keyword)
1799 },
1800 _ => None,
1801 }
1802 }
1803
1804 /// Returns whether or not the property is set by a system font
1805 #[cfg(feature = "gecko")]
get_system(&self) -> Option<SystemFont>1806 pub fn get_system(&self) -> Option<SystemFont> {
1807 match *self {
1808 % for prop in SYSTEM_FONT_LONGHANDS:
1809 PropertyDeclaration::${to_camel_case(prop)}(ref prop) => {
1810 prop.get_system()
1811 }
1812 % endfor
1813 _ => None,
1814 }
1815 }
1816
1817 /// Is it the default value of line-height?
is_default_line_height(&self) -> bool1818 pub fn is_default_line_height(&self) -> bool {
1819 match *self {
1820 PropertyDeclaration::LineHeight(LineHeight::Normal) => true,
1821 _ => false
1822 }
1823 }
1824
1825 #[cfg(feature = "servo")]
1826 /// Dummy method to avoid cfg()s
get_system(&self) -> Option<()>1827 pub fn get_system(&self) -> Option<()> {
1828 None
1829 }
1830
1831 /// Returns whether the declaration may be serialized as part of a shorthand.
1832 ///
1833 /// This method returns false if this declaration contains variable or has a
1834 /// CSS-wide keyword value, since these values cannot be serialized as part
1835 /// of a shorthand.
1836 ///
1837 /// Caller should check `with_variables_from_shorthand()` and whether all
1838 /// needed declarations has the same CSS-wide keyword first.
1839 ///
1840 /// Note that, serialization of a shorthand may still fail because of other
1841 /// property-specific requirement even when this method returns true for all
1842 /// the longhand declarations.
may_serialize_as_part_of_shorthand(&self) -> bool1843 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
1844 match *self {
1845 PropertyDeclaration::CSSWideKeyword(..) |
1846 PropertyDeclaration::WithVariables(..) => false,
1847 PropertyDeclaration::Custom(..) =>
1848 unreachable!("Serializing a custom property as part of shorthand?"),
1849 _ => true,
1850 }
1851 }
1852
1853 /// Return whether the value is stored as it was in the CSS source,
1854 /// preserving whitespace (as opposed to being parsed into a more abstract
1855 /// data structure).
1856 ///
1857 /// This is the case of custom properties and values that contain
1858 /// unsubstituted variables.
value_is_unparsed(&self) -> bool1859 pub fn value_is_unparsed(&self) -> bool {
1860 match *self {
1861 PropertyDeclaration::WithVariables(..) => true,
1862 PropertyDeclaration::Custom(ref declaration) => {
1863 !matches!(declaration.value.borrow(), DeclaredValue::CSSWideKeyword(..))
1864 }
1865 _ => false,
1866 }
1867 }
1868
1869 /// Returns true if this property declaration is for one of the animatable
1870 /// properties.
is_animatable(&self) -> bool1871 pub fn is_animatable(&self) -> bool {
1872 match self.id() {
1873 PropertyDeclarationId::Longhand(id) => id.is_animatable(),
1874 PropertyDeclarationId::Custom(..) => false,
1875 }
1876 }
1877
1878 /// Returns true if this property is a custom property, false
1879 /// otherwise.
is_custom(&self) -> bool1880 pub fn is_custom(&self) -> bool {
1881 matches!(*self, PropertyDeclaration::Custom(..))
1882 }
1883
1884 /// The `context` parameter controls this:
1885 ///
1886 /// <https://drafts.csswg.org/css-animations/#keyframes>
1887 /// > The <declaration-list> inside of <keyframe-block> accepts any CSS property
1888 /// > except those defined in this specification,
1889 /// > but does accept the `animation-play-state` property and interprets it specially.
1890 ///
1891 /// This will not actually parse Importance values, and will always set things
1892 /// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
1893 /// we only set them here so that we don't have to reallocate
parse_into<'i, 't>( declarations: &mut SourcePropertyDeclaration, id: PropertyId, name: CowRcStr<'i>, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>1894 pub fn parse_into<'i, 't>(
1895 declarations: &mut SourcePropertyDeclaration,
1896 id: PropertyId,
1897 name: CowRcStr<'i>,
1898 context: &ParserContext,
1899 input: &mut Parser<'i, 't>,
1900 ) -> Result<(), ParseError<'i>> {
1901 assert!(declarations.is_empty());
1902
1903 if !id.allowed_in(context) {
1904 return Err(input.new_custom_error(
1905 StyleParseErrorKind::UnknownProperty(name)
1906 ));
1907 }
1908
1909 let start = input.state();
1910 match id {
1911 PropertyId::Custom(property_name) => {
1912 // FIXME: fully implement https://github.com/w3c/csswg-drafts/issues/774
1913 // before adding skip_whitespace here.
1914 // This probably affects some test results.
1915 let value = match input.try(|i| CSSWideKeyword::parse(i)) {
1916 Ok(keyword) => DeclaredValueOwned::CSSWideKeyword(keyword),
1917 Err(()) => match ::custom_properties::SpecifiedValue::parse(input) {
1918 Ok(value) => DeclaredValueOwned::Value(value),
1919 Err(e) => return Err(StyleParseErrorKind::new_invalid(name, e)),
1920 }
1921 };
1922 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
1923 name: property_name,
1924 value,
1925 }));
1926 Ok(())
1927 }
1928 PropertyId::LonghandAlias(id, _) |
1929 PropertyId::Longhand(id) => {
1930 input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
1931 input.try(|i| CSSWideKeyword::parse(i)).map(|keyword| {
1932 PropertyDeclaration::CSSWideKeyword(
1933 WideKeywordDeclaration { id, keyword },
1934 )
1935 }).or_else(|()| {
1936 input.look_for_var_functions();
1937 input.parse_entirely(|input| id.parse_value(context, input))
1938 .or_else(|err| {
1939 while let Ok(_) = input.next() {} // Look for var() after the error.
1940 if input.seen_var_functions() {
1941 input.reset(&start);
1942 let (first_token_type, css) =
1943 ::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
1944 StyleParseErrorKind::new_invalid(name, e)
1945 })?;
1946 Ok(PropertyDeclaration::WithVariables(VariableDeclaration {
1947 id,
1948 value: Arc::new(UnparsedValue {
1949 css: css.into_owned(),
1950 first_token_type: first_token_type,
1951 url_data: context.url_data.clone(),
1952 from_shorthand: None,
1953 }),
1954 }))
1955 } else {
1956 Err(StyleParseErrorKind::new_invalid(name, err))
1957 }
1958 })
1959 }).map(|declaration| {
1960 declarations.push(declaration)
1961 })
1962 }
1963 PropertyId::ShorthandAlias(id, _) |
1964 PropertyId::Shorthand(id) => {
1965 input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
1966 if let Ok(keyword) = input.try(|i| CSSWideKeyword::parse(i)) {
1967 if id == ShorthandId::All {
1968 declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
1969 } else {
1970 for longhand in id.longhands() {
1971 declarations.push(PropertyDeclaration::CSSWideKeyword(
1972 WideKeywordDeclaration {
1973 id: longhand,
1974 keyword,
1975 },
1976 ))
1977 }
1978 }
1979 Ok(())
1980 } else {
1981 input.look_for_var_functions();
1982 // Not using parse_entirely here: each ${shorthand.ident}::parse_into function
1983 // needs to do so *before* pushing to `declarations`.
1984 id.parse_into(declarations, context, input).or_else(|err| {
1985 while let Ok(_) = input.next() {} // Look for var() after the error.
1986 if input.seen_var_functions() {
1987 input.reset(&start);
1988 let (first_token_type, css) =
1989 ::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
1990 StyleParseErrorKind::new_invalid(name, e)
1991 })?;
1992 let unparsed = Arc::new(UnparsedValue {
1993 css: css.into_owned(),
1994 first_token_type: first_token_type,
1995 url_data: context.url_data.clone(),
1996 from_shorthand: Some(id),
1997 });
1998 if id == ShorthandId::All {
1999 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
2000 } else {
2001 for id in id.longhands() {
2002 declarations.push(
2003 PropertyDeclaration::WithVariables(VariableDeclaration {
2004 id,
2005 value: unparsed.clone(),
2006 })
2007 )
2008 }
2009 }
2010 Ok(())
2011 } else {
2012 Err(StyleParseErrorKind::new_invalid(name, err))
2013 }
2014 })
2015 }
2016 }
2017 }
2018 }
2019 }
2020
2021 const MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL: usize =
2022 ${max(len(s.sub_properties) for s in data.shorthands_except_all())};
2023
2024 type SourcePropertyDeclarationArray =
2025 [PropertyDeclaration; MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL];
2026
2027 /// A stack-allocated vector of `PropertyDeclaration`
2028 /// large enough to parse one CSS `key: value` declaration.
2029 /// (Shorthands expand to multiple `PropertyDeclaration`s.)
2030 pub struct SourcePropertyDeclaration {
2031 declarations: ::arrayvec::ArrayVec<SourcePropertyDeclarationArray>,
2032
2033 /// Stored separately to keep MAX_SUB_PROPERTIES_PER_SHORTHAND_EXCEPT_ALL smaller.
2034 all_shorthand: AllShorthand,
2035 }
2036
2037 impl SourcePropertyDeclaration {
2038 /// Create one. It’s big, try not to move it around.
2039 #[inline]
new() -> Self2040 pub fn new() -> Self {
2041 SourcePropertyDeclaration {
2042 declarations: ::arrayvec::ArrayVec::new(),
2043 all_shorthand: AllShorthand::NotSet,
2044 }
2045 }
2046
2047 /// Similar to Vec::drain: leaves this empty when the return value is dropped.
drain(&mut self) -> SourcePropertyDeclarationDrain2048 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain {
2049 SourcePropertyDeclarationDrain {
2050 declarations: self.declarations.drain(..),
2051 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
2052 }
2053 }
2054
2055 /// Reset to initial state
clear(&mut self)2056 pub fn clear(&mut self) {
2057 self.declarations.clear();
2058 self.all_shorthand = AllShorthand::NotSet;
2059 }
2060
is_empty(&self) -> bool2061 fn is_empty(&self) -> bool {
2062 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
2063 }
2064
push(&mut self, declaration: PropertyDeclaration)2065 fn push(&mut self, declaration: PropertyDeclaration) {
2066 let _result = self.declarations.try_push(declaration);
2067 debug_assert!(_result.is_ok());
2068 }
2069 }
2070
2071 /// Return type of SourcePropertyDeclaration::drain
2072 pub struct SourcePropertyDeclarationDrain<'a> {
2073 declarations: ::arrayvec::Drain<'a, SourcePropertyDeclarationArray>,
2074 all_shorthand: AllShorthand,
2075 }
2076
2077 enum AllShorthand {
2078 NotSet,
2079 CSSWideKeyword(CSSWideKeyword),
2080 WithVariables(Arc<UnparsedValue>)
2081 }
2082
2083 #[cfg(feature = "gecko")]
2084 pub use gecko_properties::style_structs;
2085
2086 /// The module where all the style structs are defined.
2087 #[cfg(feature = "servo")]
2088 pub mod style_structs {
2089 use fnv::FnvHasher;
2090 use super::longhands;
2091 use std::hash::{Hash, Hasher};
2092 use logical_geometry::WritingMode;
2093 use media_queries::Device;
2094 use values::computed::NonNegativeLength;
2095
2096 % for style_struct in data.active_style_structs():
2097 % if style_struct.name == "Font":
2098 #[derive(Clone, Debug, MallocSizeOf)]
2099 % else:
2100 #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
2101 % endif
2102 /// The ${style_struct.name} style struct.
2103 pub struct ${style_struct.name} {
2104 % for longhand in style_struct.longhands:
2105 /// The ${longhand.name} computed value.
2106 pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
2107 % endfor
2108 % if style_struct.name == "InheritedText":
2109 /// The "used" text-decorations that apply to this box.
2110 ///
2111 /// FIXME(emilio): This is technically a box-tree concept, and
2112 /// would be nice to move away from style.
2113 pub text_decorations_in_effect: ::values::computed::text::TextDecorationsInEffect,
2114 % endif
2115 % if style_struct.name == "Font":
2116 /// The font hash, used for font caching.
2117 pub hash: u64,
2118 % endif
2119 % if style_struct.name == "Box":
2120 /// The display value specified by the CSS stylesheets (without any style adjustments),
2121 /// which is needed for hypothetical layout boxes.
2122 pub original_display: longhands::display::computed_value::T,
2123 % endif
2124 }
2125 % if style_struct.name == "Font":
2126 impl PartialEq for Font {
eq(&self, other: &Font) -> bool2127 fn eq(&self, other: &Font) -> bool {
2128 self.hash == other.hash
2129 % for longhand in style_struct.longhands:
2130 && self.${longhand.ident} == other.${longhand.ident}
2131 % endfor
2132 }
2133 }
2134 % endif
2135
2136 impl ${style_struct.name} {
2137 % for longhand in style_struct.longhands:
2138 % if longhand.logical:
2139 ${helpers.logical_setter(name=longhand.name)}
2140 % else:
2141 % if longhand.is_vector:
2142 /// Set ${longhand.name}.
2143 #[allow(non_snake_case)]
2144 #[inline]
2145 pub fn set_${longhand.ident}<I>(&mut self, v: I)
2146 where I: IntoIterator<Item = longhands::${longhand.ident}
2147 ::computed_value::single_value::T>,
2148 I::IntoIter: ExactSizeIterator
2149 {
2150 self.${longhand.ident} = longhands::${longhand.ident}::computed_value
2151 ::T(v.into_iter().collect());
2152 }
2153 % elif longhand.ident == "display":
2154 /// Set `display`.
2155 ///
2156 /// We need to keep track of the original display for hypothetical boxes,
2157 /// so we need to special-case this.
2158 #[allow(non_snake_case)]
2159 #[inline]
set_display(&mut self, v: longhands::display::computed_value::T)2160 pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
2161 self.display = v;
2162 self.original_display = v;
2163 }
2164 % else:
2165 /// Set ${longhand.name}.
2166 #[allow(non_snake_case)]
2167 #[inline]
2168 pub fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
2169 self.${longhand.ident} = v;
2170 }
2171 % endif
2172 % if longhand.ident == "display":
2173 /// Set `display` from other struct.
2174 ///
2175 /// Same as `set_display` above.
2176 /// Thus, we need to special-case this.
2177 #[allow(non_snake_case)]
2178 #[inline]
copy_display_from(&mut self, other: &Self)2179 pub fn copy_display_from(&mut self, other: &Self) {
2180 self.display = other.display.clone();
2181 self.original_display = other.display.clone();
2182 }
2183 % else:
2184 /// Set ${longhand.name} from other struct.
2185 #[allow(non_snake_case)]
2186 #[inline]
2187 pub fn copy_${longhand.ident}_from(&mut self, other: &Self) {
2188 self.${longhand.ident} = other.${longhand.ident}.clone();
2189 }
2190 % endif
2191 /// Reset ${longhand.name} from the initial struct.
2192 #[allow(non_snake_case)]
2193 #[inline]
2194 pub fn reset_${longhand.ident}(&mut self, other: &Self) {
2195 self.copy_${longhand.ident}_from(other)
2196 }
2197
2198 /// Get the computed value for ${longhand.name}.
2199 #[allow(non_snake_case)]
2200 #[inline]
2201 pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
2202 self.${longhand.ident}.clone()
2203 }
2204 % endif
2205 % if longhand.need_index:
2206 /// If this longhand is indexed, get the number of elements.
2207 #[allow(non_snake_case)]
2208 pub fn ${longhand.ident}_count(&self) -> usize {
2209 self.${longhand.ident}.0.len()
2210 }
2211
2212 /// If this longhand is indexed, get the element at given
2213 /// index.
2214 #[allow(non_snake_case)]
2215 pub fn ${longhand.ident}_at(&self, index: usize)
2216 -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
2217 self.${longhand.ident}.0[index].clone()
2218 }
2219 % endif
2220 % endfor
2221 % if style_struct.name == "Border":
2222 % for side in ["top", "right", "bottom", "left"]:
2223 /// Whether the border-${side} property has nonzero width.
2224 #[allow(non_snake_case)]
2225 pub fn border_${side}_has_nonzero_width(&self) -> bool {
2226 self.border_${side}_width != NonNegativeLength::zero()
2227 }
2228 % endfor
2229 % elif style_struct.name == "Font":
2230 /// Computes a font hash in order to be able to cache fonts
2231 /// effectively in GFX and layout.
compute_font_hash(&mut self)2232 pub fn compute_font_hash(&mut self) {
2233 // Corresponds to the fields in
2234 // `gfx::font_template::FontTemplateDescriptor`.
2235 let mut hasher: FnvHasher = Default::default();
2236 hasher.write_u16(self.font_weight.0);
2237 self.font_stretch.hash(&mut hasher);
2238 self.font_family.hash(&mut hasher);
2239 self.hash = hasher.finish()
2240 }
2241
2242 /// (Servo does not handle MathML, so this just calls copy_font_size_from)
inherit_font_size_from(&mut self, parent: &Self, _: Option<NonNegativeLength>, _: &Device)2243 pub fn inherit_font_size_from(&mut self, parent: &Self,
2244 _: Option<NonNegativeLength>,
2245 _: &Device) {
2246 self.copy_font_size_from(parent);
2247 }
2248 /// (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>2249 pub fn apply_font_size(&mut self,
2250 v: longhands::font_size::computed_value::T,
2251 _: &Self,
2252 _: &Device) -> Option<NonNegativeLength> {
2253 self.set_font_size(v);
2254 None
2255 }
2256 /// (Servo does not handle MathML, so this does nothing)
apply_unconstrained_font_size(&mut self, _: NonNegativeLength)2257 pub fn apply_unconstrained_font_size(&mut self, _: NonNegativeLength) {
2258 }
2259
2260 % elif style_struct.name == "Outline":
2261 /// Whether the outline-width property is non-zero.
2262 #[inline]
outline_has_nonzero_width(&self) -> bool2263 pub fn outline_has_nonzero_width(&self) -> bool {
2264 self.outline_width != NonNegativeLength::zero()
2265 }
2266 % elif style_struct.name == "Text":
2267 /// Whether the text decoration has an underline.
2268 #[inline]
has_underline(&self) -> bool2269 pub fn has_underline(&self) -> bool {
2270 self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::UNDERLINE)
2271 }
2272
2273 /// Whether the text decoration has an overline.
2274 #[inline]
has_overline(&self) -> bool2275 pub fn has_overline(&self) -> bool {
2276 self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::OVERLINE)
2277 }
2278
2279 /// Whether the text decoration has a line through.
2280 #[inline]
has_line_through(&self) -> bool2281 pub fn has_line_through(&self) -> bool {
2282 self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::LINE_THROUGH)
2283 }
2284 % elif style_struct.name == "Box":
2285 /// Sets the display property, but without touching original_display,
2286 /// 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 )2287 pub fn set_adjusted_display(
2288 &mut self,
2289 dpy: longhands::display::computed_value::T,
2290 is_item_or_root: bool
2291 ) {
2292 self.display = dpy;
2293 if is_item_or_root {
2294 self.original_display = dpy;
2295 }
2296 }
2297 % endif
2298 }
2299
2300 % endfor
2301 }
2302
2303 % for style_struct in data.active_style_structs():
2304 impl style_structs::${style_struct.name} {
2305 % for longhand in style_struct.longhands:
2306 % if longhand.need_index:
2307 /// Iterate over the values of ${longhand.name}.
2308 #[allow(non_snake_case)]
2309 #[inline]
2310 pub fn ${longhand.ident}_iter(&self) -> ${longhand.camel_case}Iter {
2311 ${longhand.camel_case}Iter {
2312 style_struct: self,
2313 current: 0,
2314 max: self.${longhand.ident}_count(),
2315 }
2316 }
2317
2318 /// Get a value mod `index` for the property ${longhand.name}.
2319 #[allow(non_snake_case)]
2320 #[inline]
2321 pub fn ${longhand.ident}_mod(&self, index: usize)
2322 -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
2323 self.${longhand.ident}_at(index % self.${longhand.ident}_count())
2324 }
2325 % endif
2326 % endfor
2327
2328 % if style_struct.name == "Box":
2329 /// Returns whether there is any animation specified with
2330 /// animation-name other than `none`.
specifies_animations(&self) -> bool2331 pub fn specifies_animations(&self) -> bool {
2332 self.animation_name_iter().any(|name| name.0.is_some())
2333 }
2334
2335 /// Returns whether there are any transitions specified.
2336 #[cfg(feature = "servo")]
specifies_transitions(&self) -> bool2337 pub fn specifies_transitions(&self) -> bool {
2338 self.transition_duration_iter()
2339 .take(self.transition_property_count())
2340 .any(|t| t.seconds() > 0.)
2341 }
2342 % elif style_struct.name == "Column":
2343 /// Whether this is a multicol style.
2344 #[cfg(feature = "servo")]
is_multicol(&self) -> bool2345 pub fn is_multicol(&self) -> bool {
2346 match self.column_width {
2347 Either::First(_width) => true,
2348 Either::Second(_auto) => !self.column_count.is_auto(),
2349 }
2350 }
2351 % endif
2352 }
2353
2354 % for longhand in style_struct.longhands:
2355 % if longhand.need_index:
2356 /// An iterator over the values of the ${longhand.name} properties.
2357 pub struct ${longhand.camel_case}Iter<'a> {
2358 style_struct: &'a style_structs::${style_struct.name},
2359 current: usize,
2360 max: usize,
2361 }
2362
2363 impl<'a> Iterator for ${longhand.camel_case}Iter<'a> {
2364 type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue;
2365
next(&mut self) -> Option<Self::Item>2366 fn next(&mut self) -> Option<Self::Item> {
2367 self.current += 1;
2368 if self.current <= self.max {
2369 Some(self.style_struct.${longhand.ident}_at(self.current - 1))
2370 } else {
2371 None
2372 }
2373 }
2374 }
2375 % endif
2376 % endfor
2377 % endfor
2378
2379
2380 #[cfg(feature = "gecko")]
2381 pub use gecko_properties::{ComputedValues, ComputedValuesInner};
2382
2383 #[cfg(feature = "servo")]
2384 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
2385 /// Actual data of ComputedValues, to match up with Gecko
2386 pub struct ComputedValuesInner {
2387 % for style_struct in data.active_style_structs():
2388 ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
2389 % endfor
2390 custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
2391 /// The writing mode of this computed values struct.
2392 pub writing_mode: WritingMode,
2393
2394 /// A set of flags we use to store misc information regarding this style.
2395 pub flags: ComputedValueFlags,
2396
2397 /// The rule node representing the ordered list of rules matched for this
2398 /// node. Can be None for default values and text nodes. This is
2399 /// essentially an optimization to avoid referencing the root rule node.
2400 pub rules: Option<StrongRuleNode>,
2401
2402 /// The element's computed values if visited, only computed if there's a
2403 /// relevant link for this element. A element's "relevant link" is the
2404 /// element being matched if it is a link or the nearest ancestor link.
2405 visited_style: Option<Arc<ComputedValues>>,
2406 }
2407
2408 /// The struct that Servo uses to represent computed values.
2409 ///
2410 /// This struct contains an immutable atomically-reference-counted pointer to
2411 /// every kind of style struct.
2412 ///
2413 /// When needed, the structs may be copied in order to get mutated.
2414 #[cfg(feature = "servo")]
2415 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
2416 pub struct ComputedValues {
2417 /// The actual computed values
2418 ///
2419 /// In Gecko the outer ComputedValues is actually a style context,
2420 /// whereas ComputedValuesInner is the core set of computed values.
2421 ///
2422 /// We maintain this distinction in servo to reduce the amount of special casing.
2423 inner: ComputedValuesInner,
2424 }
2425
2426 impl ComputedValues {
2427 /// Returns whether this style's display value is equal to contents.
is_display_contents(&self) -> bool2428 pub fn is_display_contents(&self) -> bool {
2429 self.get_box().clone_display().is_contents()
2430 }
2431
2432 /// Whether we're a visited style.
is_style_if_visited(&self) -> bool2433 pub fn is_style_if_visited(&self) -> bool {
2434 self.flags.contains(ComputedValueFlags::IS_STYLE_IF_VISITED)
2435 }
2436
2437 /// Gets a reference to the rule node. Panic if no rule node exists.
rules(&self) -> &StrongRuleNode2438 pub fn rules(&self) -> &StrongRuleNode {
2439 self.rules.as_ref().unwrap()
2440 }
2441
2442 /// Returns the visited style, if any.
visited_style(&self) -> Option<<&ComputedValues>2443 pub fn visited_style(&self) -> Option<<&ComputedValues> {
2444 self.visited_style.as_ref().map(|s| &**s)
2445 }
2446
2447 /// Returns the visited rules, if applicable.
visited_rules(&self) -> Option<<&StrongRuleNode>2448 pub fn visited_rules(&self) -> Option<<&StrongRuleNode> {
2449 self.visited_style.as_ref().and_then(|s| s.rules.as_ref())
2450 }
2451
2452 /// Returns whether we're in a display: none subtree.
is_in_display_none_subtree(&self) -> bool2453 pub fn is_in_display_none_subtree(&self) -> bool {
2454 use properties::computed_value_flags::ComputedValueFlags;
2455
2456 self.flags.contains(ComputedValueFlags::IS_IN_DISPLAY_NONE_SUBTREE)
2457 }
2458
2459 /// Gets a reference to the custom properties map (if one exists).
custom_properties(&self) -> Option<<&Arc<::custom_properties::CustomPropertiesMap>>2460 pub fn custom_properties(&self) -> Option<<&Arc<::custom_properties::CustomPropertiesMap>> {
2461 self.custom_properties.as_ref()
2462 }
2463 }
2464
2465 #[cfg(feature = "servo")]
2466 impl ComputedValues {
2467 /// Create a new refcounted `ComputedValues`
2468 pub fn new(
2469 _: &Device,
2470 _: Option<<&ComputedValues>,
2471 _: Option<<&PseudoElement>,
2472 custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
2473 writing_mode: WritingMode,
2474 flags: ComputedValueFlags,
2475 rules: Option<StrongRuleNode>,
2476 visited_style: Option<Arc<ComputedValues>>,
2477 % for style_struct in data.active_style_structs():
2478 ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
2479 % endfor
2480 ) -> Arc<Self> {
2481 Arc::new(Self {
2482 inner: ComputedValuesInner {
2483 custom_properties,
2484 writing_mode,
2485 rules,
2486 visited_style,
2487 flags,
2488 % for style_struct in data.active_style_structs():
2489 ${style_struct.ident},
2490 % endfor
2491 }
2492 })
2493 }
2494
2495 /// Get the initial computed values.
initial_values() -> &'static Self2496 pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES }
2497 }
2498
2499 #[cfg(feature = "servo")]
2500 impl ops::Deref for ComputedValues {
2501 type Target = ComputedValuesInner;
deref(&self) -> &ComputedValuesInner2502 fn deref(&self) -> &ComputedValuesInner {
2503 &self.inner
2504 }
2505 }
2506
2507 #[cfg(feature = "servo")]
2508 impl ops::DerefMut for ComputedValues {
deref_mut(&mut self) -> &mut ComputedValuesInner2509 fn deref_mut(&mut self) -> &mut ComputedValuesInner {
2510 &mut self.inner
2511 }
2512 }
2513
2514 #[cfg(feature = "servo")]
2515 impl ComputedValuesInner {
2516 % for style_struct in data.active_style_structs():
2517 /// Clone the ${style_struct.name} struct.
2518 #[inline]
2519 pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
2520 self.${style_struct.ident}.clone()
2521 }
2522
2523 /// Get a immutable reference to the ${style_struct.name} struct.
2524 #[inline]
2525 pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
2526 &self.${style_struct.ident}
2527 }
2528
2529 /// Gets an immutable reference to the refcounted value that wraps
2530 /// `${style_struct.name}`.
2531 pub fn ${style_struct.name_lower}_arc(&self) -> &Arc<style_structs::${style_struct.name}> {
2532 &self.${style_struct.ident}
2533 }
2534
2535 /// Get a mutable reference to the ${style_struct.name} struct.
2536 #[inline]
2537 pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
2538 Arc::make_mut(&mut self.${style_struct.ident})
2539 }
2540 % endfor
2541
2542 /// Gets a reference to the rule node. Panic if no rule node exists.
rules(&self) -> &StrongRuleNode2543 pub fn rules(&self) -> &StrongRuleNode {
2544 self.rules.as_ref().unwrap()
2545 }
2546
2547 /// Whether this style has a -moz-binding value. This is always false for
2548 /// Servo for obvious reasons.
has_moz_binding(&self) -> bool2549 pub fn has_moz_binding(&self) -> bool { false }
2550
2551 #[inline]
2552 /// Returns whether the "content" property for the given style is completely
2553 /// ineffective, and would yield an empty `::before` or `::after`
2554 /// pseudo-element.
ineffective_content_property(&self) -> bool2555 pub fn ineffective_content_property(&self) -> bool {
2556 use properties::longhands::content::computed_value::T;
2557 match self.get_counters().content {
2558 T::Normal | T::None => true,
2559 T::Items(ref items) => items.is_empty(),
2560 }
2561 }
2562
2563 /// Whether the current style or any of its ancestors is multicolumn.
2564 #[inline]
can_be_fragmented(&self) -> bool2565 pub fn can_be_fragmented(&self) -> bool {
2566 self.flags.contains(ComputedValueFlags::CAN_BE_FRAGMENTED)
2567 }
2568
2569 /// Whether the current style is multicolumn.
2570 #[inline]
is_multicol(&self) -> bool2571 pub fn is_multicol(&self) -> bool {
2572 self.get_column().is_multicol()
2573 }
2574
2575 /// Resolves the currentColor keyword.
2576 ///
2577 /// Any color value from computed values (except for the 'color' property
2578 /// itself) should go through this method.
2579 ///
2580 /// Usage example:
2581 /// let top_color = style.resolve_color(style.Border.border_top_color);
2582 #[inline]
resolve_color(&self, color: computed::Color) -> RGBA2583 pub fn resolve_color(&self, color: computed::Color) -> RGBA {
2584 color.to_rgba(self.get_color().color)
2585 }
2586
2587 /// Get the logical computed inline size.
2588 #[inline]
content_inline_size(&self) -> computed::LengthOrPercentageOrAuto2589 pub fn content_inline_size(&self) -> computed::LengthOrPercentageOrAuto {
2590 let position_style = self.get_position();
2591 if self.writing_mode.is_vertical() {
2592 position_style.height
2593 } else {
2594 position_style.width
2595 }
2596 }
2597
2598 /// Get the logical computed block size.
2599 #[inline]
content_block_size(&self) -> computed::LengthOrPercentageOrAuto2600 pub fn content_block_size(&self) -> computed::LengthOrPercentageOrAuto {
2601 let position_style = self.get_position();
2602 if self.writing_mode.is_vertical() { position_style.width } else { position_style.height }
2603 }
2604
2605 /// Get the logical computed min inline size.
2606 #[inline]
min_inline_size(&self) -> computed::LengthOrPercentage2607 pub fn min_inline_size(&self) -> computed::LengthOrPercentage {
2608 let position_style = self.get_position();
2609 if self.writing_mode.is_vertical() { position_style.min_height } else { position_style.min_width }
2610 }
2611
2612 /// Get the logical computed min block size.
2613 #[inline]
min_block_size(&self) -> computed::LengthOrPercentage2614 pub fn min_block_size(&self) -> computed::LengthOrPercentage {
2615 let position_style = self.get_position();
2616 if self.writing_mode.is_vertical() { position_style.min_width } else { position_style.min_height }
2617 }
2618
2619 /// Get the logical computed max inline size.
2620 #[inline]
max_inline_size(&self) -> computed::LengthOrPercentageOrNone2621 pub fn max_inline_size(&self) -> computed::LengthOrPercentageOrNone {
2622 let position_style = self.get_position();
2623 if self.writing_mode.is_vertical() { position_style.max_height } else { position_style.max_width }
2624 }
2625
2626 /// Get the logical computed max block size.
2627 #[inline]
max_block_size(&self) -> computed::LengthOrPercentageOrNone2628 pub fn max_block_size(&self) -> computed::LengthOrPercentageOrNone {
2629 let position_style = self.get_position();
2630 if self.writing_mode.is_vertical() { position_style.max_width } else { position_style.max_height }
2631 }
2632
2633 /// Get the logical computed padding for this writing mode.
2634 #[inline]
logical_padding(&self) -> LogicalMargin<computed::LengthOrPercentage>2635 pub fn logical_padding(&self) -> LogicalMargin<computed::LengthOrPercentage> {
2636 let padding_style = self.get_padding();
2637 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
2638 padding_style.padding_top.0,
2639 padding_style.padding_right.0,
2640 padding_style.padding_bottom.0,
2641 padding_style.padding_left.0,
2642 ))
2643 }
2644
2645 /// Get the logical border width
2646 #[inline]
border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au>2647 pub fn border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au> {
2648 let border_style = self.get_border();
2649 LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
2650 Au::from(border_style.border_top_width),
2651 Au::from(border_style.border_right_width),
2652 Au::from(border_style.border_bottom_width),
2653 Au::from(border_style.border_left_width),
2654 ))
2655 }
2656
2657 /// Gets the logical computed border widths for this style.
2658 #[inline]
logical_border_width(&self) -> LogicalMargin<Au>2659 pub fn logical_border_width(&self) -> LogicalMargin<Au> {
2660 self.border_width_for_writing_mode(self.writing_mode)
2661 }
2662
2663 /// Gets the logical computed margin from this style.
2664 #[inline]
logical_margin(&self) -> LogicalMargin<computed::LengthOrPercentageOrAuto>2665 pub fn logical_margin(&self) -> LogicalMargin<computed::LengthOrPercentageOrAuto> {
2666 let margin_style = self.get_margin();
2667 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
2668 margin_style.margin_top,
2669 margin_style.margin_right,
2670 margin_style.margin_bottom,
2671 margin_style.margin_left,
2672 ))
2673 }
2674
2675 /// Gets the logical position from this style.
2676 #[inline]
logical_position(&self) -> LogicalMargin<computed::LengthOrPercentageOrAuto>2677 pub fn logical_position(&self) -> LogicalMargin<computed::LengthOrPercentageOrAuto> {
2678 // FIXME(SimonSapin): should be the writing mode of the containing block, maybe?
2679 let position_style = self.get_position();
2680 LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
2681 position_style.top,
2682 position_style.right,
2683 position_style.bottom,
2684 position_style.left,
2685 ))
2686 }
2687
2688 /// Return true if the effects force the transform style to be Flat
overrides_transform_style(&self) -> bool2689 pub fn overrides_transform_style(&self) -> bool {
2690 use computed_values::mix_blend_mode::T as MixBlendMode;
2691
2692 let effects = self.get_effects();
2693 // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
2694 effects.opacity < 1.0 ||
2695 !effects.filter.0.is_empty() ||
2696 !effects.clip.is_auto() ||
2697 effects.mix_blend_mode != MixBlendMode::Normal
2698 }
2699
2700 /// <https://drafts.csswg.org/css-transforms/#grouping-property-values>
get_used_transform_style(&self) -> computed_values::transform_style::T2701 pub fn get_used_transform_style(&self) -> computed_values::transform_style::T {
2702 use computed_values::transform_style::T as TransformStyle;
2703
2704 let box_ = self.get_box();
2705
2706 if self.overrides_transform_style() {
2707 TransformStyle::Flat
2708 } else {
2709 // Return the computed value if not overridden by the above exceptions
2710 box_.transform_style
2711 }
2712 }
2713
2714 /// Whether given this transform value, the compositor would require a
2715 /// layer.
transform_requires_layer(&self) -> bool2716 pub fn transform_requires_layer(&self) -> bool {
2717 use values::generics::transform::TransformOperation;
2718 // Check if the transform matrix is 2D or 3D
2719 for transform in &self.get_box().transform.0 {
2720 match *transform {
2721 TransformOperation::Perspective(..) => {
2722 return true;
2723 }
2724 TransformOperation::Matrix3D(m) => {
2725 // See http://dev.w3.org/csswg/css-transforms/#2d-matrix
2726 if m.m31 != 0.0 || m.m32 != 0.0 ||
2727 m.m13 != 0.0 || m.m23 != 0.0 ||
2728 m.m43 != 0.0 || m.m14 != 0.0 ||
2729 m.m24 != 0.0 || m.m34 != 0.0 ||
2730 m.m33 != 1.0 || m.m44 != 1.0 {
2731 return true;
2732 }
2733 }
2734 TransformOperation::Translate3D(_, _, z) |
2735 TransformOperation::TranslateZ(z) => {
2736 if z.px() != 0. {
2737 return true;
2738 }
2739 }
2740 _ => {}
2741 }
2742 }
2743
2744 // Neither perspective nor transform present
2745 false
2746 }
2747
2748 /// Serializes the computed value of this property as a string.
computed_value_to_string(&self, property: PropertyDeclarationId) -> String2749 pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String {
2750 match property {
2751 % for style_struct in data.active_style_structs():
2752 % for longhand in style_struct.longhands:
2753 PropertyDeclarationId::Longhand(LonghandId::${longhand.camel_case}) => {
2754 self.${style_struct.ident}.${longhand.ident}.to_css_string()
2755 }
2756 % endfor
2757 % endfor
2758 PropertyDeclarationId::Custom(name) => {
2759 self.custom_properties
2760 .as_ref()
2761 .and_then(|map| map.get(name))
2762 .map(|value| value.to_css_string())
2763 .unwrap_or(String::new())
2764 }
2765 }
2766 }
2767 }
2768
2769 % if product == "gecko":
2770 pub use ::servo_arc::RawOffsetArc as BuilderArc;
2771 /// Clone an arc, returning a regular arc
clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T>2772 fn clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T> {
2773 Arc::from_raw_offset(x.clone())
2774 }
2775 % else:
2776 pub use ::servo_arc::Arc as BuilderArc;
2777 /// Clone an arc, returning a regular arc
clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T>2778 fn clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T> {
2779 x.clone()
2780 }
2781 % endif
2782
2783 /// A reference to a style struct of the parent, or our own style struct.
2784 pub enum StyleStructRef<'a, T: 'static> {
2785 /// A borrowed struct from the parent, for example, for inheriting style.
2786 Borrowed(&'a BuilderArc<T>),
2787 /// An owned struct, that we've already mutated.
2788 Owned(UniqueArc<T>),
2789 /// Temporarily vacated, will panic if accessed
2790 Vacated,
2791 }
2792
2793 impl<'a, T: 'a> StyleStructRef<'a, T>
2794 where T: Clone,
2795 {
2796 /// Ensure a mutable reference of this value exists, either cloning the
2797 /// borrowed value, or returning the owned one.
mutate(&mut self) -> &mut T2798 pub fn mutate(&mut self) -> &mut T {
2799 if let StyleStructRef::Borrowed(v) = *self {
2800 *self = StyleStructRef::Owned(UniqueArc::new((**v).clone()));
2801 }
2802
2803 match *self {
2804 StyleStructRef::Owned(ref mut v) => v,
2805 StyleStructRef::Borrowed(..) => unreachable!(),
2806 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
2807 }
2808 }
2809
2810 /// Extract a unique Arc from this struct, vacating it.
2811 ///
2812 /// The vacated state is a transient one, please put the Arc back
2813 /// when done via `put()`. This function is to be used to separate
2814 /// the struct being mutated from the computed context
take(&mut self) -> UniqueArc<T>2815 pub fn take(&mut self) -> UniqueArc<T> {
2816 use std::mem::replace;
2817 let inner = replace(self, StyleStructRef::Vacated);
2818
2819 match inner {
2820 StyleStructRef::Owned(arc) => arc,
2821 StyleStructRef::Borrowed(arc) => UniqueArc::new((**arc).clone()),
2822 StyleStructRef::Vacated => panic!("Accessed vacated style struct"),
2823 }
2824 }
2825
2826 /// Replace vacated ref with an arc
put(&mut self, arc: UniqueArc<T>)2827 pub fn put(&mut self, arc: UniqueArc<T>) {
2828 debug_assert!(matches!(*self, StyleStructRef::Vacated));
2829 *self = StyleStructRef::Owned(arc);
2830 }
2831
2832 /// Get a mutable reference to the owned struct, or `None` if the struct
2833 /// hasn't been mutated.
get_if_mutated(&mut self) -> Option<<&mut T>2834 pub fn get_if_mutated(&mut self) -> Option<<&mut T> {
2835 match *self {
2836 StyleStructRef::Owned(ref mut v) => Some(v),
2837 StyleStructRef::Borrowed(..) => None,
2838 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
2839 }
2840 }
2841
2842 /// Returns an `Arc` to the internal struct, constructing one if
2843 /// appropriate.
build(self) -> Arc<T>2844 pub fn build(self) -> Arc<T> {
2845 match self {
2846 StyleStructRef::Owned(v) => v.shareable(),
2847 StyleStructRef::Borrowed(v) => clone_arc(v),
2848 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
2849 }
2850 }
2851 }
2852
2853 impl<'a, T: 'a> ops::Deref for StyleStructRef<'a, T> {
2854 type Target = T;
2855
deref(&self) -> &T2856 fn deref(&self) -> &T {
2857 match *self {
2858 StyleStructRef::Owned(ref v) => &**v,
2859 StyleStructRef::Borrowed(v) => &**v,
2860 StyleStructRef::Vacated => panic!("Accessed vacated style struct")
2861 }
2862 }
2863 }
2864
2865 /// A type used to compute a struct with minimal overhead.
2866 ///
2867 /// This allows holding references to the parent/default computed values without
2868 /// actually cloning them, until we either build the style, or mutate the
2869 /// inherited value.
2870 pub struct StyleBuilder<'a> {
2871 /// The device we're using to compute style.
2872 ///
2873 /// This provides access to viewport unit ratios, etc.
2874 pub device: &'a Device,
2875
2876 /// The style we're inheriting from.
2877 ///
2878 /// This is effectively
2879 /// `parent_style.unwrap_or(device.default_computed_values())`.
2880 inherited_style: &'a ComputedValues,
2881
2882 /// The style we're inheriting from for properties that don't inherit from
2883 /// ::first-line. This is the same as inherited_style, unless
2884 /// inherited_style is a ::first-line style.
2885 inherited_style_ignoring_first_line: &'a ComputedValues,
2886
2887 /// The style we're getting reset structs from.
2888 reset_style: &'a ComputedValues,
2889
2890 /// The style we're inheriting from explicitly, or none if we're the root of
2891 /// a subtree.
2892 parent_style: Option<<&'a ComputedValues>,
2893
2894 /// The rule node representing the ordered list of rules matched for this
2895 /// node.
2896 pub rules: Option<StrongRuleNode>,
2897
2898 custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
2899
2900 /// The pseudo-element this style will represent.
2901 pub pseudo: Option<<&'a PseudoElement>,
2902
2903 /// Whether we have mutated any reset structs since the the last time
2904 /// `clear_modified_reset` was called. This is used to tell whether the
2905 /// `StyleAdjuster` did any work.
2906 modified_reset: bool,
2907
2908 /// The writing mode flags.
2909 ///
2910 /// TODO(emilio): Make private.
2911 pub writing_mode: WritingMode,
2912 /// Flags for the computed value.
2913 pub flags: ComputedValueFlags,
2914 /// The element's style if visited, only computed if there's a relevant link
2915 /// for this element. A element's "relevant link" is the element being
2916 /// matched if it is a link or the nearest ancestor link.
2917 visited_style: Option<Arc<ComputedValues>>,
2918 % for style_struct in data.active_style_structs():
2919 ${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>,
2920 % endfor
2921 }
2922
2923 impl<'a> StyleBuilder<'a> {
2924 /// Trivially construct a `StyleBuilder`.
new( device: &'a Device, parent_style: Option<<&'a ComputedValues>, parent_style_ignoring_first_line: Option<<&'a ComputedValues>, pseudo: Option<<&'a PseudoElement>, cascade_flags: CascadeFlags, rules: Option<StrongRuleNode>, custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>, visited_style: Option<Arc<ComputedValues>>, ) -> Self2925 fn new(
2926 device: &'a Device,
2927 parent_style: Option<<&'a ComputedValues>,
2928 parent_style_ignoring_first_line: Option<<&'a ComputedValues>,
2929 pseudo: Option<<&'a PseudoElement>,
2930 cascade_flags: CascadeFlags,
2931 rules: Option<StrongRuleNode>,
2932 custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
2933 visited_style: Option<Arc<ComputedValues>>,
2934 ) -> Self {
2935 debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
2936 #[cfg(feature = "gecko")]
2937 debug_assert!(parent_style.is_none() ||
2938 ::std::ptr::eq(parent_style.unwrap(),
2939 parent_style_ignoring_first_line.unwrap()) ||
2940 parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine));
2941 let reset_style = device.default_computed_values();
2942 let inherited_style = parent_style.unwrap_or(reset_style);
2943 let inherited_style_ignoring_first_line = parent_style_ignoring_first_line.unwrap_or(reset_style);
2944 // FIXME(bz): INHERIT_ALL seems like a fundamentally broken idea. I'm
2945 // 99% sure it should give incorrect behavior for table anonymous box
2946 // backgrounds, for example. This code doesn't attempt to make it play
2947 // nice with inherited_style_ignoring_first_line.
2948 let reset_style = if cascade_flags.contains(CascadeFlags::INHERIT_ALL) {
2949 inherited_style
2950 } else {
2951 reset_style
2952 };
2953
2954 let mut flags = inherited_style.flags.inherited();
2955 if cascade_flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) {
2956 flags.insert(ComputedValueFlags::IS_STYLE_IF_VISITED);
2957 }
2958
2959 StyleBuilder {
2960 device,
2961 parent_style,
2962 inherited_style,
2963 inherited_style_ignoring_first_line,
2964 reset_style,
2965 pseudo,
2966 rules,
2967 modified_reset: false,
2968 custom_properties,
2969 writing_mode: inherited_style.writing_mode,
2970 flags,
2971 visited_style,
2972 % for style_struct in data.active_style_structs():
2973 % if style_struct.inherited:
2974 ${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.${style_struct.name_lower}_arc()),
2975 % else:
2976 ${style_struct.ident}: StyleStructRef::Borrowed(reset_style.${style_struct.name_lower}_arc()),
2977 % endif
2978 % endfor
2979 }
2980 }
2981
2982 /// Whether we're a visited style.
is_style_if_visited(&self) -> bool2983 pub fn is_style_if_visited(&self) -> bool {
2984 self.flags.contains(ComputedValueFlags::IS_STYLE_IF_VISITED)
2985 }
2986
2987 /// NOTE(emilio): This is done so we can compute relative units with respect
2988 /// to the parent style, but all the early properties / writing-mode / etc
2989 /// are already set to the right ones on the kid.
2990 ///
2991 /// Do _not_ actually call this to construct a style, this should mostly be
2992 /// used for animations.
for_animation( device: &'a Device, style_to_derive_from: &'a ComputedValues, parent_style: Option<<&'a ComputedValues>, ) -> Self2993 pub fn for_animation(
2994 device: &'a Device,
2995 style_to_derive_from: &'a ComputedValues,
2996 parent_style: Option<<&'a ComputedValues>,
2997 ) -> Self {
2998 let reset_style = device.default_computed_values();
2999 let inherited_style = parent_style.unwrap_or(reset_style);
3000 #[cfg(feature = "gecko")]
3001 debug_assert!(parent_style.is_none() ||
3002 parent_style.unwrap().pseudo() != Some(PseudoElement::FirstLine));
3003 StyleBuilder {
3004 device,
3005 parent_style,
3006 inherited_style,
3007 // None of our callers pass in ::first-line parent styles.
3008 inherited_style_ignoring_first_line: inherited_style,
3009 reset_style,
3010 pseudo: None,
3011 modified_reset: false,
3012 rules: None,
3013 custom_properties: style_to_derive_from.custom_properties().cloned(),
3014 writing_mode: style_to_derive_from.writing_mode,
3015 flags: style_to_derive_from.flags,
3016 visited_style: None,
3017 % for style_struct in data.active_style_structs():
3018 ${style_struct.ident}: StyleStructRef::Borrowed(
3019 style_to_derive_from.${style_struct.name_lower}_arc()
3020 ),
3021 % endfor
3022 }
3023 }
3024
3025 /// Copy the reset properties from `style`.
copy_reset_from(&mut self, style: &'a ComputedValues)3026 pub fn copy_reset_from(&mut self, style: &'a ComputedValues) {
3027 % for style_struct in data.active_style_structs():
3028 % if not style_struct.inherited:
3029 self.${style_struct.ident} =
3030 StyleStructRef::Borrowed(style.${style_struct.name_lower}_arc());
3031 % endif
3032 % endfor
3033 }
3034
3035 % for property in data.longhands:
3036 % if property.ident != "font_size":
3037 /// Inherit `${property.ident}` from our parent style.
3038 #[allow(non_snake_case)]
3039 pub fn inherit_${property.ident}(&mut self) {
3040 let inherited_struct =
3041 % if property.style_struct.inherited:
3042 self.inherited_style.get_${property.style_struct.name_lower}();
3043 % else:
3044 self.inherited_style_ignoring_first_line
3045 .get_${property.style_struct.name_lower}();
3046 % endif
3047
3048 % if not property.style_struct.inherited:
3049 self.flags.insert(ComputedValueFlags::INHERITS_RESET_STYLE);
3050 self.modified_reset = true;
3051 % endif
3052
3053 % if property.ident == "content":
3054 self.flags.insert(ComputedValueFlags::INHERITS_CONTENT);
3055 % endif
3056
3057 % if property.ident == "display":
3058 self.flags.insert(ComputedValueFlags::INHERITS_DISPLAY);
3059 % endif
3060
3061 self.${property.style_struct.ident}.mutate()
3062 .copy_${property.ident}_from(
3063 inherited_struct,
3064 % if property.logical:
3065 self.writing_mode,
3066 % endif
3067 );
3068 }
3069
3070 /// Reset `${property.ident}` to the initial value.
3071 #[allow(non_snake_case)]
3072 pub fn reset_${property.ident}(&mut self) {
3073 let reset_struct =
3074 self.reset_style.get_${property.style_struct.name_lower}();
3075
3076 % if not property.style_struct.inherited:
3077 self.modified_reset = true;
3078 % endif
3079
3080 self.${property.style_struct.ident}.mutate()
3081 .reset_${property.ident}(
3082 reset_struct,
3083 % if property.logical:
3084 self.writing_mode,
3085 % endif
3086 );
3087 }
3088
3089 % if not property.is_vector:
3090 /// Set the `${property.ident}` to the computed value `value`.
3091 #[allow(non_snake_case)]
3092 pub fn set_${property.ident}(
3093 &mut self,
3094 value: longhands::${property.ident}::computed_value::T
3095 ) {
3096 % if not property.style_struct.inherited:
3097 self.modified_reset = true;
3098 % endif
3099
3100 <% props_need_device = ["content", "list_style_type", "font_variant_alternates"] %>
3101 self.${property.style_struct.ident}.mutate()
3102 .set_${property.ident}(
3103 value,
3104 % if property.logical:
3105 self.writing_mode,
3106 % elif product == "gecko" and property.ident in props_need_device:
3107 self.device,
3108 % endif
3109 );
3110 }
3111 % endif
3112 % endif
3113 % endfor
3114
3115 /// Inherits style from the parent element, accounting for the default
3116 /// computed values that need to be provided as well.
for_inheritance( device: &'a Device, parent: Option<<&'a ComputedValues>, pseudo: Option<<&'a PseudoElement>, ) -> Self3117 pub fn for_inheritance(
3118 device: &'a Device,
3119 parent: Option<<&'a ComputedValues>,
3120 pseudo: Option<<&'a PseudoElement>,
3121 ) -> Self {
3122 // Rebuild the visited style from the parent, ensuring that it will also
3123 // not have rules. This matches the unvisited style that will be
3124 // produced by this builder. This assumes that the caller doesn't need
3125 // to adjust or process visited style, so we can just build visited
3126 // style here for simplicity.
3127 let visited_style = parent.and_then(|parent| {
3128 parent.visited_style().map(|style| {
3129 Self::for_inheritance(
3130 device,
3131 Some(style),
3132 pseudo,
3133 ).build()
3134 })
3135 });
3136 Self::new(
3137 device,
3138 parent,
3139 parent,
3140 pseudo,
3141 CascadeFlags::empty(),
3142 /* rules = */ None,
3143 parent.and_then(|p| p.custom_properties().cloned()),
3144 visited_style,
3145 )
3146 }
3147
3148 /// Returns whether we have a visited style.
has_visited_style(&self) -> bool3149 pub fn has_visited_style(&self) -> bool {
3150 self.visited_style.is_some()
3151 }
3152
3153 /// Returns whether we're a pseudo-elements style.
is_pseudo_element(&self) -> bool3154 pub fn is_pseudo_element(&self) -> bool {
3155 self.pseudo.map_or(false, |p| !p.is_anon_box())
3156 }
3157
3158 /// Returns the style we're getting reset properties from.
default_style(&self) -> &'a ComputedValues3159 pub fn default_style(&self) -> &'a ComputedValues {
3160 self.reset_style
3161 }
3162
3163 % for style_struct in data.active_style_structs():
3164 /// Gets an immutable view of the current `${style_struct.name}` style.
3165 pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
3166 &self.${style_struct.ident}
3167 }
3168
3169 /// Gets a mutable view of the current `${style_struct.name}` style.
3170 pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
3171 % if not property.style_struct.inherited:
3172 self.modified_reset = true;
3173 % endif
3174 self.${style_struct.ident}.mutate()
3175 }
3176
3177 /// Gets a mutable view of the current `${style_struct.name}` style.
3178 pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc<style_structs::${style_struct.name}> {
3179 % if not property.style_struct.inherited:
3180 self.modified_reset = true;
3181 % endif
3182 self.${style_struct.ident}.take()
3183 }
3184
3185 /// Gets a mutable view of the current `${style_struct.name}` style.
3186 pub fn put_${style_struct.name_lower}(&mut self, s: UniqueArc<style_structs::${style_struct.name}>) {
3187 self.${style_struct.ident}.put(s)
3188 }
3189
3190 /// Gets a mutable view of the current `${style_struct.name}` style,
3191 /// only if it's been mutated before.
3192 pub fn get_${style_struct.name_lower}_if_mutated(&mut self)
3193 -> Option<<&mut style_structs::${style_struct.name}> {
3194 self.${style_struct.ident}.get_if_mutated()
3195 }
3196
3197 /// Reset the current `${style_struct.name}` style to its default value.
3198 pub fn reset_${style_struct.name_lower}_struct(&mut self) {
3199 self.${style_struct.ident} =
3200 StyleStructRef::Borrowed(self.reset_style.${style_struct.name_lower}_arc());
3201 }
3202 % endfor
3203
3204 /// Returns whether this computed style represents a floated object.
floated(&self) -> bool3205 pub fn floated(&self) -> bool {
3206 self.get_box().clone_float() != longhands::float::computed_value::T::None
3207 }
3208
3209 /// Returns whether this computed style represents an out of flow-positioned
3210 /// object.
out_of_flow_positioned(&self) -> bool3211 pub fn out_of_flow_positioned(&self) -> bool {
3212 use properties::longhands::position::computed_value::T as Position;
3213 matches!(self.get_box().clone_position(),
3214 Position::Absolute | Position::Fixed)
3215 }
3216
3217 /// Whether this style has a top-layer style. That's implemented in Gecko
3218 /// via the -moz-top-layer property, but servo doesn't have any concept of a
3219 /// top layer (yet, it's needed for fullscreen).
3220 #[cfg(feature = "servo")]
in_top_layer(&self) -> bool3221 pub fn in_top_layer(&self) -> bool { false }
3222
3223 /// Whether this style has a top-layer style.
3224 #[cfg(feature = "gecko")]
in_top_layer(&self) -> bool3225 pub fn in_top_layer(&self) -> bool {
3226 matches!(self.get_box().clone__moz_top_layer(),
3227 longhands::_moz_top_layer::computed_value::T::Top)
3228 }
3229
3230 /// Clears the "have any reset structs been modified" flag.
clear_modified_reset(&mut self)3231 fn clear_modified_reset(&mut self) {
3232 self.modified_reset = false;
3233 }
3234
3235 /// Returns whether we have mutated any reset structs since the the last
3236 /// time `clear_modified_reset` was called.
modified_reset(&self) -> bool3237 fn modified_reset(&self) -> bool {
3238 self.modified_reset
3239 }
3240
3241 /// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
build(self) -> Arc<ComputedValues>3242 pub fn build(self) -> Arc<ComputedValues> {
3243 ComputedValues::new(
3244 self.device,
3245 self.parent_style,
3246 self.pseudo,
3247 self.custom_properties,
3248 self.writing_mode,
3249 self.flags,
3250 self.rules,
3251 self.visited_style,
3252 % for style_struct in data.active_style_structs():
3253 self.${style_struct.ident}.build(),
3254 % endfor
3255 )
3256 }
3257
3258 /// Get the custom properties map if necessary.
3259 ///
3260 /// Cloning the Arc here is fine because it only happens in the case where
3261 /// we have custom properties, and those are both rare and expensive.
custom_properties(&self) -> Option<<&Arc<::custom_properties::CustomPropertiesMap>>3262 fn custom_properties(&self) -> Option<<&Arc<::custom_properties::CustomPropertiesMap>> {
3263 self.custom_properties.as_ref()
3264 }
3265
3266 /// Access to various information about our inherited styles. We don't
3267 /// expose an inherited ComputedValues directly, because in the
3268 /// ::first-line case some of the inherited information needs to come from
3269 /// one ComputedValues instance and some from a different one.
3270
3271 /// Inherited writing-mode.
inherited_writing_mode(&self) -> &WritingMode3272 pub fn inherited_writing_mode(&self) -> &WritingMode {
3273 &self.inherited_style.writing_mode
3274 }
3275
3276 /// The computed value flags of our parent.
3277 #[inline]
get_parent_flags(&self) -> ComputedValueFlags3278 pub fn get_parent_flags(&self) -> ComputedValueFlags {
3279 self.inherited_style.flags
3280 }
3281
3282 /// And access to inherited style structs.
3283 % for style_struct in data.active_style_structs():
3284 /// Gets our inherited `${style_struct.name}`. We don't name these
3285 /// accessors `inherited_${style_struct.name_lower}` because we already
3286 /// have things like "box" vs "inherited_box" as struct names. Do the
3287 /// next-best thing and call them `parent_${style_struct.name_lower}`
3288 /// instead.
3289 pub fn get_parent_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
3290 % if style_struct.inherited:
3291 self.inherited_style.get_${style_struct.name_lower}()
3292 % else:
3293 self.inherited_style_ignoring_first_line.get_${style_struct.name_lower}()
3294 % endif
3295 }
3296 % endfor
3297 }
3298
3299 #[cfg(feature = "servo")]
3300 pub use self::lazy_static_module::INITIAL_SERVO_VALUES;
3301
3302 // Use a module to work around #[cfg] on lazy_static! not being applied to every generated item.
3303 #[cfg(feature = "servo")]
3304 #[allow(missing_docs)]
3305 mod lazy_static_module {
3306 use logical_geometry::WritingMode;
3307 use servo_arc::Arc;
3308 use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};
3309 use super::computed_value_flags::ComputedValueFlags;
3310
3311 /// The initial values for all style structs as defined by the specification.
3312 lazy_static! {
3313 pub static ref INITIAL_SERVO_VALUES: ComputedValues = ComputedValues {
3314 inner: ComputedValuesInner {
3315 % for style_struct in data.active_style_structs():
3316 ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {
3317 % for longhand in style_struct.longhands:
3318 ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
3319 % endfor
3320 % if style_struct.name == "InheritedText":
3321 text_decorations_in_effect: ::values::computed::text::TextDecorationsInEffect::default(),
3322 % endif
3323 % if style_struct.name == "Font":
3324 hash: 0,
3325 % endif
3326 % if style_struct.name == "Box":
3327 original_display: longhands::display::get_initial_value(),
3328 % endif
3329 }),
3330 % endfor
3331 custom_properties: None,
3332 writing_mode: WritingMode::empty(),
3333 rules: None,
3334 visited_style: None,
3335 flags: ComputedValueFlags::empty(),
3336 }
3337 };
3338 }
3339 }
3340
3341 /// A per-longhand function that performs the CSS cascade for that longhand.
3342 pub type CascadePropertyFn =
3343 extern "Rust" fn(
3344 declaration: &PropertyDeclaration,
3345 context: &mut computed::Context,
3346 );
3347
3348 /// A per-longhand array of functions to perform the CSS cascade on each of
3349 /// them, effectively doing virtual dispatch.
3350 static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
3351 % for property in data.longhands:
3352 longhands::${property.ident}::cascade_property,
3353 % endfor
3354 ];
3355
3356 bitflags! {
3357 /// A set of flags to tweak the behavior of the `cascade` function.
3358 pub struct CascadeFlags: u8 {
3359 /// Whether to inherit all styles from the parent. If this flag is not
3360 /// present, non-inherited styles are reset to their initial values.
3361 const INHERIT_ALL = 1;
3362
3363 /// Whether to only cascade properties that are visited dependent.
3364 const VISITED_DEPENDENT_ONLY = 1 << 1;
3365 }
3366 }
3367
3368 /// Performs the CSS cascade, computing new styles for an element from its parent style.
3369 ///
3370 /// The arguments are:
3371 ///
3372 /// * `device`: Used to get the initial viewport and other external state.
3373 ///
3374 /// * `rule_node`: The rule node in the tree that represent the CSS rules that
3375 /// matched.
3376 ///
3377 /// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
3378 ///
3379 /// Returns the computed values.
3380 /// * `flags`: Various flags.
3381 ///
cascade<E>( device: &Device, pseudo: Option<<&PseudoElement>, rule_node: &StrongRuleNode, guards: &StylesheetGuards, parent_style: Option<<&ComputedValues>, parent_style_ignoring_first_line: Option<<&ComputedValues>, layout_parent_style: Option<<&ComputedValues>, visited_style: Option<Arc<ComputedValues>>, font_metrics_provider: &FontMetricsProvider, flags: CascadeFlags, quirks_mode: QuirksMode, rule_cache: Option<<&RuleCache>, rule_cache_conditions: &mut RuleCacheConditions, element: Option<E>, ) -> Arc<ComputedValues> where E: TElement,3382 pub fn cascade<E>(
3383 device: &Device,
3384 pseudo: Option<<&PseudoElement>,
3385 rule_node: &StrongRuleNode,
3386 guards: &StylesheetGuards,
3387 parent_style: Option<<&ComputedValues>,
3388 parent_style_ignoring_first_line: Option<<&ComputedValues>,
3389 layout_parent_style: Option<<&ComputedValues>,
3390 visited_style: Option<Arc<ComputedValues>>,
3391 font_metrics_provider: &FontMetricsProvider,
3392 flags: CascadeFlags,
3393 quirks_mode: QuirksMode,
3394 rule_cache: Option<<&RuleCache>,
3395 rule_cache_conditions: &mut RuleCacheConditions,
3396 element: Option<E>,
3397 ) -> Arc<ComputedValues>
3398 where
3399 E: TElement,
3400 {
3401 debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
3402 let empty = SmallBitVec::new();
3403
3404 let property_restriction = pseudo.and_then(|p| p.property_restriction());
3405
3406 let iter_declarations = || {
3407 rule_node.self_and_ancestors().flat_map(|node| {
3408 let cascade_level = node.cascade_level();
3409 let source = node.style_source();
3410
3411 let declarations = if source.is_some() {
3412 source.read(cascade_level.guard(guards)).declaration_importance_iter()
3413 } else {
3414 // The root node has no style source.
3415 DeclarationImportanceIterator::new(&[], &empty)
3416 };
3417 let node_importance = node.importance();
3418
3419 declarations
3420 // Yield declarations later in source order (with more precedence) first.
3421 .rev()
3422 .filter_map(move |(declaration, declaration_importance)| {
3423 if let Some(property_restriction) = property_restriction {
3424 // declaration.id() is either a longhand or a custom
3425 // property. Custom properties are always allowed, but
3426 // longhands are only allowed if they have our
3427 // property_restriction flag set.
3428 if let PropertyDeclarationId::Longhand(id) = declaration.id() {
3429 if !id.flags().contains(property_restriction) {
3430 return None
3431 }
3432 }
3433 }
3434
3435 if declaration_importance == node_importance {
3436 Some((declaration, cascade_level))
3437 } else {
3438 None
3439 }
3440 })
3441 })
3442 };
3443
3444 apply_declarations(
3445 device,
3446 pseudo,
3447 rule_node,
3448 guards,
3449 iter_declarations,
3450 parent_style,
3451 parent_style_ignoring_first_line,
3452 layout_parent_style,
3453 visited_style,
3454 font_metrics_provider,
3455 flags,
3456 quirks_mode,
3457 rule_cache,
3458 rule_cache_conditions,
3459 element,
3460 )
3461 }
3462
3463 /// NOTE: This function expects the declaration with more priority to appear
3464 /// first.
apply_declarations<'a, E, F, I>( device: &Device, pseudo: Option<<&PseudoElement>, rules: &StrongRuleNode, guards: &StylesheetGuards, iter_declarations: F, parent_style: Option<<&ComputedValues>, parent_style_ignoring_first_line: Option<<&ComputedValues>, layout_parent_style: Option<<&ComputedValues>, visited_style: Option<Arc<ComputedValues>>, font_metrics_provider: &FontMetricsProvider, flags: CascadeFlags, quirks_mode: QuirksMode, rule_cache: Option<<&RuleCache>, rule_cache_conditions: &mut RuleCacheConditions, element: Option<E>, ) -> Arc<ComputedValues> where E: TElement, F: Fn() -> I, I: Iterator<Item = (&'a PropertyDeclaration, CascadeLevel)>,3465 pub fn apply_declarations<'a, E, F, I>(
3466 device: &Device,
3467 pseudo: Option<<&PseudoElement>,
3468 rules: &StrongRuleNode,
3469 guards: &StylesheetGuards,
3470 iter_declarations: F,
3471 parent_style: Option<<&ComputedValues>,
3472 parent_style_ignoring_first_line: Option<<&ComputedValues>,
3473 layout_parent_style: Option<<&ComputedValues>,
3474 visited_style: Option<Arc<ComputedValues>>,
3475 font_metrics_provider: &FontMetricsProvider,
3476 flags: CascadeFlags,
3477 quirks_mode: QuirksMode,
3478 rule_cache: Option<<&RuleCache>,
3479 rule_cache_conditions: &mut RuleCacheConditions,
3480 element: Option<E>,
3481 ) -> Arc<ComputedValues>
3482 where
3483 E: TElement,
3484 F: Fn() -> I,
3485 I: Iterator<Item = (&'a PropertyDeclaration, CascadeLevel)>,
3486 {
3487 debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
3488 debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
3489 #[cfg(feature = "gecko")]
3490 debug_assert!(parent_style.is_none() ||
3491 ::std::ptr::eq(parent_style.unwrap(),
3492 parent_style_ignoring_first_line.unwrap()) ||
3493 parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine));
3494 let (inherited_style, layout_parent_style) = match parent_style {
3495 Some(parent_style) => {
3496 (parent_style,
3497 layout_parent_style.unwrap_or(parent_style))
3498 },
3499 None => {
3500 (device.default_computed_values(),
3501 device.default_computed_values())
3502 }
3503 };
3504
3505 let custom_properties = {
3506 let mut builder =
3507 CustomPropertiesBuilder::new(inherited_style.custom_properties());
3508
3509 for (declaration, _cascade_level) in iter_declarations() {
3510 if let PropertyDeclaration::Custom(ref declaration) = *declaration {
3511 builder.cascade(&declaration.name, declaration.value.borrow());
3512 }
3513 }
3514
3515 builder.build()
3516 };
3517
3518 let mut context = computed::Context {
3519 is_root_element: pseudo.is_none() && element.map_or(false, |e| e.is_root()),
3520 // We'd really like to own the rules here to avoid refcount traffic, but
3521 // animation's usage of `apply_declarations` make this tricky. See bug
3522 // 1375525.
3523 builder: StyleBuilder::new(
3524 device,
3525 parent_style,
3526 parent_style_ignoring_first_line,
3527 pseudo,
3528 flags,
3529 Some(rules.clone()),
3530 custom_properties,
3531 visited_style,
3532 ),
3533 cached_system_font: None,
3534 in_media_query: false,
3535 for_smil_animation: false,
3536 for_non_inherited_property: None,
3537 font_metrics_provider,
3538 quirks_mode,
3539 rule_cache_conditions: RefCell::new(rule_cache_conditions),
3540 };
3541
3542 let ignore_colors = !device.use_document_colors();
3543
3544 // Set computed values, overwriting earlier declarations for the same
3545 // property.
3546 let mut seen = LonghandIdSet::new();
3547
3548 // Declaration blocks are stored in increasing precedence order, we want
3549 // them in decreasing order here.
3550 //
3551 // We could (and used to) use a pattern match here, but that bloats this
3552 // function to over 100K of compiled code!
3553 //
3554 // To improve i-cache behavior, we outline the individual functions and use
3555 // virtual dispatch instead.
3556 let mut apply_reset = true;
3557 % for category_to_cascade_now in ["early", "other"]:
3558 % if category_to_cascade_now == "early":
3559 // Pull these out so that we can compute them in a specific order
3560 // without introducing more iterations.
3561 let mut font_size = None;
3562 let mut font_family = None;
3563 % endif
3564 for (declaration, cascade_level) in iter_declarations() {
3565 let declaration_id = declaration.id();
3566 let longhand_id = match declaration_id {
3567 PropertyDeclarationId::Longhand(id) => id,
3568 PropertyDeclarationId::Custom(..) => continue,
3569 };
3570
3571 // Only a few properties are allowed to depend on the visited state
3572 // of links. When cascading visited styles, we can save time by
3573 // only processing these properties.
3574 if flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) &&
3575 !longhand_id.is_visited_dependent() {
3576 continue
3577 }
3578
3579 if !apply_reset && !longhand_id.inherited() {
3580 continue;
3581 }
3582
3583 if
3584 % if category_to_cascade_now == "early":
3585 !
3586 % endif
3587 longhand_id.is_early_property()
3588 {
3589 continue
3590 }
3591
3592 <% maybe_to_physical = ".to_physical(writing_mode)" if category_to_cascade_now != "early" else "" %>
3593 let physical_longhand_id = longhand_id ${maybe_to_physical};
3594 if seen.contains(physical_longhand_id) {
3595 continue
3596 }
3597
3598 let mut declaration = match *declaration {
3599 PropertyDeclaration::WithVariables(ref declaration) => {
3600 if !declaration.id.inherited() {
3601 context.rule_cache_conditions.borrow_mut()
3602 .set_uncacheable();
3603 }
3604 Cow::Owned(declaration.value.substitute_variables(
3605 declaration.id,
3606 context.builder.custom_properties.as_ref(),
3607 context.quirks_mode
3608 ))
3609 }
3610 ref d => Cow::Borrowed(d)
3611 };
3612
3613 // When document colors are disabled, skip properties that are
3614 // marked as ignored in that mode, unless they come from a UA or
3615 // user style sheet.
3616 if ignore_colors &&
3617 longhand_id.is_ignored_when_document_colors_disabled(
3618 cascade_level,
3619 context.builder.pseudo
3620 )
3621 {
3622 let non_transparent_background = match *declaration {
3623 PropertyDeclaration::BackgroundColor(ref color) => {
3624 // Treat background-color a bit differently. If the specified
3625 // color is anything other than a fully transparent color, convert
3626 // it into the Device's default background color.
3627 color.is_non_transparent()
3628 }
3629 _ => continue
3630 };
3631
3632 // FIXME: moving this out of `match` is a work around for
3633 // borrows being lexical.
3634 if non_transparent_background {
3635 let color = device.default_background_color();
3636 declaration =
3637 Cow::Owned(PropertyDeclaration::BackgroundColor(color.into()));
3638 }
3639 }
3640
3641 seen.insert(physical_longhand_id);
3642
3643 % if category_to_cascade_now == "early":
3644 if LonghandId::FontSize == longhand_id {
3645 font_size = Some(declaration.clone());
3646 continue;
3647 }
3648 if LonghandId::FontFamily == longhand_id {
3649 font_family = Some(declaration.clone());
3650 continue;
3651 }
3652 % endif
3653
3654 let discriminant = longhand_id as usize;
3655 (CASCADE_PROPERTY[discriminant])(&*declaration, &mut context);
3656 }
3657 % if category_to_cascade_now == "early":
3658 let writing_mode =
3659 WritingMode::new(context.builder.get_inheritedbox());
3660 context.builder.writing_mode = writing_mode;
3661
3662 let mut _skip_font_family = false;
3663
3664 % if product == "gecko":
3665
3666 // <svg:text> is not affected by text zoom, and it uses a preshint to
3667 // disable it. We fix up the struct when this happens by unzooming
3668 // its contained font values, which will have been zoomed in the parent
3669 if seen.contains(LonghandId::XTextZoom) {
3670 let zoom = context.builder.get_font().gecko().mAllowZoom;
3671 let parent_zoom = context.style().get_parent_font().gecko().mAllowZoom;
3672 if zoom != parent_zoom {
3673 debug_assert!(!zoom,
3674 "We only ever disable text zoom (in svg:text), never enable it");
3675 // can't borrow both device and font, use the take/put machinery
3676 let mut font = context.builder.take_font();
3677 font.unzoom_fonts(context.device());
3678 context.builder.put_font(font);
3679 }
3680 }
3681
3682 // Whenever a single generic value is specified, gecko will do a bunch of
3683 // recalculation walking up the rule tree, including handling the font-size stuff.
3684 // It basically repopulates the font struct with the default font for a given
3685 // generic and language. We handle the font-size stuff separately, so this boils
3686 // down to just copying over the font-family lists (no other aspect of the default
3687 // font can be configured).
3688
3689 if seen.contains(LonghandId::XLang) || font_family.is_some() {
3690 // if just the language changed, the inherited generic is all we need
3691 let mut generic = inherited_style.get_font().gecko().mGenericID;
3692 if let Some(ref declaration) = font_family {
3693 if let PropertyDeclaration::FontFamily(ref fam) = **declaration {
3694 if let Some(id) = fam.single_generic() {
3695 generic = id;
3696 // In case of a specified font family with a single generic, we will
3697 // end up setting font family below, but its value would get
3698 // overwritten later in the pipeline when cascading.
3699 //
3700 // We instead skip cascading font-family in that case.
3701 //
3702 // In case of the language changing, we wish for a specified font-
3703 // family to override this, so we do not skip cascading then.
3704 _skip_font_family = true;
3705 }
3706 }
3707 }
3708
3709 let pres_context = context.builder.device.pres_context();
3710 let gecko_font = context.builder.mutate_font().gecko_mut();
3711 gecko_font.mGenericID = generic;
3712 unsafe {
3713 bindings::Gecko_nsStyleFont_PrefillDefaultForGeneric(
3714 gecko_font,
3715 pres_context,
3716 generic,
3717 );
3718 }
3719 }
3720 % endif
3721
3722 // It is important that font_size is computed before
3723 // the late properties (for em units), but after font-family
3724 // (for the base-font-size dependence for default and keyword font-sizes)
3725 // Additionally, when we support system fonts they will have to be
3726 // computed early, and *before* font_family, so I'm including
3727 // font_family here preemptively instead of keeping it within
3728 // the early properties.
3729 //
3730 // To avoid an extra iteration, we just pull out the property
3731 // during the early iteration and cascade them in order
3732 // after it.
3733 if !_skip_font_family {
3734 if let Some(ref declaration) = font_family {
3735
3736 let discriminant = LonghandId::FontFamily as usize;
3737 (CASCADE_PROPERTY[discriminant])(declaration, &mut context);
3738 % if product == "gecko":
3739 let device = context.builder.device;
3740 if let PropertyDeclaration::FontFamily(ref val) = **declaration {
3741 if val.get_system().is_some() {
3742 let default = context.cached_system_font
3743 .as_ref().unwrap().default_font_type;
3744 context.builder.mutate_font().fixup_system(default);
3745 } else {
3746 context.builder.mutate_font().fixup_none_generic(device);
3747 }
3748 }
3749 % endif
3750 }
3751 }
3752
3753 if let Some(ref declaration) = font_size {
3754 let discriminant = LonghandId::FontSize as usize;
3755 (CASCADE_PROPERTY[discriminant])(declaration, &mut context);
3756 % if product == "gecko":
3757 // Font size must be explicitly inherited to handle lang changes and
3758 // scriptlevel changes.
3759 } else if seen.contains(LonghandId::XLang) ||
3760 seen.contains(LonghandId::MozScriptLevel) ||
3761 seen.contains(LonghandId::MozMinFontSizeRatio) ||
3762 font_family.is_some() {
3763 let discriminant = LonghandId::FontSize as usize;
3764 let size = PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
3765 id: LonghandId::FontSize,
3766 keyword: CSSWideKeyword::Inherit,
3767 });
3768
3769 (CASCADE_PROPERTY[discriminant])(&size, &mut context);
3770 % endif
3771 }
3772
3773 if let Some(style) = rule_cache.and_then(|c| c.find(guards, &context.builder)) {
3774 context.builder.copy_reset_from(style);
3775 apply_reset = false;
3776 }
3777 % endif // category == "early"
3778 % endfor
3779
3780 let mut builder = context.builder;
3781
3782 % if product == "gecko":
3783 if let Some(ref mut bg) = builder.get_background_if_mutated() {
3784 bg.fill_arrays();
3785 }
3786
3787 if let Some(ref mut svg) = builder.get_svg_if_mutated() {
3788 svg.fill_arrays();
3789 }
3790 % endif
3791
3792 % if product == "servo":
3793 if seen.contains(LonghandId::FontStyle) ||
3794 seen.contains(LonghandId::FontWeight) ||
3795 seen.contains(LonghandId::FontStretch) ||
3796 seen.contains(LonghandId::FontFamily) {
3797 builder.mutate_font().compute_font_hash();
3798 }
3799 % endif
3800
3801 builder.clear_modified_reset();
3802
3803 StyleAdjuster::new(&mut builder).adjust(
3804 layout_parent_style,
3805 element,
3806 flags,
3807 );
3808
3809 if builder.modified_reset() || !apply_reset {
3810 // If we adjusted any reset structs, we can't cache this ComputedValues.
3811 //
3812 // Also, if we re-used existing reset structs, don't bother caching it
3813 // back again. (Aside from being wasted effort, it will be wrong, since
3814 // context.rule_cache_conditions won't be set appropriately if we
3815 // didn't compute those reset properties.)
3816 context.rule_cache_conditions.borrow_mut()
3817 .set_uncacheable();
3818 }
3819
3820 builder.build()
3821 }
3822
3823 /// See StyleAdjuster::adjust_for_border_width.
adjust_border_width(style: &mut StyleBuilder)3824 pub fn adjust_border_width(style: &mut StyleBuilder) {
3825 % for side in ["top", "right", "bottom", "left"]:
3826 // Like calling to_computed_value, which wouldn't type check.
3827 if style.get_border().clone_border_${side}_style().none_or_hidden() &&
3828 style.get_border().border_${side}_has_nonzero_width() {
3829 style.set_border_${side}_width(NonNegativeLength::zero());
3830 }
3831 % endfor
3832 }
3833
3834 /// An identifier for a given alias property.
3835 #[derive(Clone, Copy, Eq, PartialEq)]
3836 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
3837 pub enum AliasId {
3838 % for i, property in enumerate(data.all_aliases()):
3839 /// ${property.name}
3840 ${property.camel_case} = ${i},
3841 % endfor
3842 }
3843
3844 impl fmt::Debug for AliasId {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result3845 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
3846 let name = match *self {
3847 % for property in data.all_aliases():
3848 AliasId::${property.camel_case} => "${property.camel_case}",
3849 % endfor
3850 };
3851 formatter.write_str(name)
3852 }
3853 }
3854
3855 // NOTE(emilio): Callers are responsible to deal with prefs.
3856 #[macro_export]
3857 macro_rules! css_properties_accessors {
3858 ($macro_name: ident) => {
3859 $macro_name! {
3860 % for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
3861 % for property in props:
3862 % if property.enabled_in_content():
3863 % for name in [property.name] + property.alias:
3864 % if '-' in name:
3865 [${to_rust_ident(name).capitalize()}, Set${to_rust_ident(name).capitalize()},
3866 PropertyId::${kind}(${kind}Id::${property.camel_case})],
3867 % endif
3868 [${to_camel_case(name)}, Set${to_camel_case(name)},
3869 PropertyId::${kind}(${kind}Id::${property.camel_case})],
3870 % endfor
3871 % endif
3872 % endfor
3873 % endfor
3874 }
3875 }
3876 }
3877
3878 #[macro_export]
3879 macro_rules! longhand_properties_idents {
3880 ($macro_name: ident) => {
3881 $macro_name! {
3882 % for property in data.longhands:
3883 { ${property.ident}, ${"true" if property.boxed else "false"} }
3884 % endfor
3885 }
3886 }
3887 }
3888
3889 % if product == "servo":
3890 % for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]:
3891 macro_rules! restyle_damage_${effect_name} {
3892 ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ]) => ({
3893 if
3894 % for style_struct in data.active_style_structs():
3895 % for longhand in style_struct.longhands:
3896 % if effect_name in longhand.servo_restyle_damage.split() and not longhand.logical:
3897 $old.get_${style_struct.name_lower}().${longhand.ident} !=
3898 $new.get_${style_struct.name_lower}().${longhand.ident} ||
3899 % endif
3900 % endfor
3901 % endfor
3902
3903 false {
3904 $damage.insert($($effect)|*);
3905 true
3906 } else {
3907 false
3908 }
3909 })
3910 }
3911 % endfor
3912 % endif
3913