blitz-pdf 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +355 -2
- package/THIRD_PARTY_NOTICES.md +25 -0
- package/assets/icon.svg +4 -0
- package/dist/api.d.ts +7 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +46 -0
- package/dist/api.js.map +1 -0
- package/dist/assets.d.ts +7 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +52 -0
- package/dist/assets.js.map +1 -0
- package/dist/browser.d.ts +3 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +3 -0
- package/dist/browser.js.map +1 -0
- package/dist/node.d.ts +3 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +10 -0
- package/dist/node.js.map +1 -0
- package/dist/options.d.ts +24 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +135 -0
- package/dist/options.js.map +1 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/examples/bun.ts +8 -0
- package/examples/german-invoice/README.md +70 -0
- package/examples/german-invoice/data.mjs +83 -0
- package/examples/german-invoice/invoice.css +59 -0
- package/examples/german-invoice/template.mjs +49 -0
- package/examples/german-invoice.mjs +20 -0
- package/examples/invoice.html +29 -0
- package/examples/node.mjs +8 -0
- package/examples/pagination.html +40 -0
- package/examples/pagination.mjs +8 -0
- package/licenses/DejaVu.txt +187 -0
- package/licenses/RUST_DEPENDENCIES.txt +11577 -0
- package/package.json +89 -4
- package/src/api.ts +44 -0
- package/src/assets.ts +43 -0
- package/src/browser.ts +6 -0
- package/src/node.ts +12 -0
- package/src/options.ts +101 -0
- package/src/types.ts +49 -0
- package/vendor/README.md +17 -0
- package/vendor/stylo/.cargo-ok +1 -0
- package/vendor/stylo/.cargo_vcs_info.json +6 -0
- package/vendor/stylo/Cargo.lock +1485 -0
- package/vendor/stylo/Cargo.toml +274 -0
- package/vendor/stylo/LICENSE +373 -0
- package/vendor/stylo/README.md +6 -0
- package/vendor/stylo/applicable_declarations.rs +328 -0
- package/vendor/stylo/author_styles.rs +79 -0
- package/vendor/stylo/bezier.rs +176 -0
- package/vendor/stylo/bloom.rs +414 -0
- package/vendor/stylo/build.rs +91 -0
- package/vendor/stylo/build_gecko.rs +393 -0
- package/vendor/stylo/color/color_function.rs +601 -0
- package/vendor/stylo/color/component.rs +207 -0
- package/vendor/stylo/color/convert.rs +935 -0
- package/vendor/stylo/color/gamut/raytrace.rs +265 -0
- package/vendor/stylo/color/gamut.rs +227 -0
- package/vendor/stylo/color/mix.rs +645 -0
- package/vendor/stylo/color/mod.rs +750 -0
- package/vendor/stylo/color/parsing.rs +770 -0
- package/vendor/stylo/color/to_css.rs +297 -0
- package/vendor/stylo/context.rs +716 -0
- package/vendor/stylo/counter_style/mod.rs +754 -0
- package/vendor/stylo/counter_style/predefined.rs +61 -0
- package/vendor/stylo/counter_style/update_predefined.py +35 -0
- package/vendor/stylo/custom_properties.rs +2023 -0
- package/vendor/stylo/custom_properties_map.rs +220 -0
- package/vendor/stylo/data.rs +693 -0
- package/vendor/stylo/device/gecko.rs +486 -0
- package/vendor/stylo/device/mod.rs +304 -0
- package/vendor/stylo/device/servo.rs +507 -0
- package/vendor/stylo/dom.rs +1164 -0
- package/vendor/stylo/dom_apis.rs +937 -0
- package/vendor/stylo/driver.rs +172 -0
- package/vendor/stylo/error_reporting.rs +484 -0
- package/vendor/stylo/font_face.rs +558 -0
- package/vendor/stylo/font_metrics.rs +124 -0
- package/vendor/stylo/gecko/anon_boxes.toml +113 -0
- package/vendor/stylo/gecko/arc_types.rs +211 -0
- package/vendor/stylo/gecko/conversions.rs +59 -0
- package/vendor/stylo/gecko/data.rs +222 -0
- package/vendor/stylo/gecko/media_features.rs +971 -0
- package/vendor/stylo/gecko/mod.rs +20 -0
- package/vendor/stylo/gecko/non_ts_pseudo_class_list.rs +119 -0
- package/vendor/stylo/gecko/pseudo_element.rs +640 -0
- package/vendor/stylo/gecko/pseudo_element_definition.mako.rs +298 -0
- package/vendor/stylo/gecko/pseudo_elements.py +137 -0
- package/vendor/stylo/gecko/pseudo_elements.toml +197 -0
- package/vendor/stylo/gecko/regen_pseudo_elements.py +33 -0
- package/vendor/stylo/gecko/restyle_damage.rs +127 -0
- package/vendor/stylo/gecko/selector_parser.rs +627 -0
- package/vendor/stylo/gecko/snapshot.rs +202 -0
- package/vendor/stylo/gecko/snapshot_helpers.rs +334 -0
- package/vendor/stylo/gecko/traversal.rs +52 -0
- package/vendor/stylo/gecko/wrapper.rs +2258 -0
- package/vendor/stylo/gecko_bindings/mod.rs +34 -0
- package/vendor/stylo/gecko_bindings/sugar/mod.rs +12 -0
- package/vendor/stylo/gecko_bindings/sugar/ns_com_ptr.rs +15 -0
- package/vendor/stylo/gecko_bindings/sugar/ns_compatibility.rs +19 -0
- package/vendor/stylo/gecko_bindings/sugar/ns_style_auto_array.rs +111 -0
- package/vendor/stylo/gecko_bindings/sugar/origin_flags.rs +31 -0
- package/vendor/stylo/gecko_bindings/sugar/ownership.rs +70 -0
- package/vendor/stylo/gecko_bindings/sugar/refptr.rs +299 -0
- package/vendor/stylo/gecko_string_cache/mod.rs +543 -0
- package/vendor/stylo/gecko_string_cache/namespace.rs +106 -0
- package/vendor/stylo/global_style_data.rs +224 -0
- package/vendor/stylo/invalidation/element/document_state.rs +156 -0
- package/vendor/stylo/invalidation/element/element_wrapper.rs +432 -0
- package/vendor/stylo/invalidation/element/invalidation_map.rs +1631 -0
- package/vendor/stylo/invalidation/element/invalidator.rs +1470 -0
- package/vendor/stylo/invalidation/element/mod.rs +13 -0
- package/vendor/stylo/invalidation/element/relative_selector.rs +1399 -0
- package/vendor/stylo/invalidation/element/restyle_hints.rs +226 -0
- package/vendor/stylo/invalidation/element/state_and_attributes.rs +756 -0
- package/vendor/stylo/invalidation/media_queries.rs +144 -0
- package/vendor/stylo/invalidation/mod.rs +10 -0
- package/vendor/stylo/invalidation/stylesheets.rs +745 -0
- package/vendor/stylo/invalidation/viewport_units.rs +71 -0
- package/vendor/stylo/lib.rs +336 -0
- package/vendor/stylo/logical_geometry.rs +1684 -0
- package/vendor/stylo/macros.rs +70 -0
- package/vendor/stylo/matching.rs +1226 -0
- package/vendor/stylo/media_queries/media_list.rs +168 -0
- package/vendor/stylo/media_queries/media_query.rs +190 -0
- package/vendor/stylo/media_queries/mod.rs +13 -0
- package/vendor/stylo/parallel.rs +205 -0
- package/vendor/stylo/parser.rs +276 -0
- package/vendor/stylo/piecewise_linear.rs +282 -0
- package/vendor/stylo/prefs.rs +33 -0
- package/vendor/stylo/properties/build.py +119 -0
- package/vendor/stylo/properties/cascade.rs +2704 -0
- package/vendor/stylo/properties/computed_value_flags.rs +206 -0
- package/vendor/stylo/properties/counted_unknown_properties.py +104 -0
- package/vendor/stylo/properties/counter_style_descriptors.toml +45 -0
- package/vendor/stylo/properties/data.py +1375 -0
- package/vendor/stylo/properties/declaration_block.rs +1788 -0
- package/vendor/stylo/properties/font_face_descriptors.toml +50 -0
- package/vendor/stylo/properties/gecko.mako.rs +1555 -0
- package/vendor/stylo/properties/helpers/animated_properties.mako.rs +800 -0
- package/vendor/stylo/properties/helpers.mako.rs +812 -0
- package/vendor/stylo/properties/longhands.toml +4305 -0
- package/vendor/stylo/properties/mod.rs +1857 -0
- package/vendor/stylo/properties/properties.html.mako +31 -0
- package/vendor/stylo/properties/properties.mako.rs +3103 -0
- package/vendor/stylo/properties/property_descriptors.toml +18 -0
- package/vendor/stylo/properties/shorthands.rs +4241 -0
- package/vendor/stylo/properties/shorthands.toml +575 -0
- package/vendor/stylo/properties/vendored_python/mako-1.3.10-py3-none-any.whl +0 -0
- package/vendor/stylo/properties/vendored_python/markupsafe/LICENSE.txt +28 -0
- package/vendor/stylo/properties/vendored_python/markupsafe/__init__.py +384 -0
- package/vendor/stylo/properties/vendored_python/toml-0.10.2-py2.py3-none-any.whl +0 -0
- package/vendor/stylo/properties/view_transition_descriptors.toml +11 -0
- package/vendor/stylo/properties_and_values/mod.rs +12 -0
- package/vendor/stylo/properties_and_values/registry.rs +65 -0
- package/vendor/stylo/properties_and_values/rule.rs +331 -0
- package/vendor/stylo/properties_and_values/syntax/ascii.rs +60 -0
- package/vendor/stylo/properties_and_values/syntax/data_type.rs +146 -0
- package/vendor/stylo/properties_and_values/syntax/mod.rs +437 -0
- package/vendor/stylo/properties_and_values/value.rs +758 -0
- package/vendor/stylo/queries/condition.rs +996 -0
- package/vendor/stylo/queries/feature.rs +199 -0
- package/vendor/stylo/queries/feature_expression.rs +1170 -0
- package/vendor/stylo/queries/mod.rs +19 -0
- package/vendor/stylo/queries/values.rs +46 -0
- package/vendor/stylo/rule_cache.rs +267 -0
- package/vendor/stylo/rule_collector.rs +554 -0
- package/vendor/stylo/rule_tree/core.rs +787 -0
- package/vendor/stylo/rule_tree/level.rs +396 -0
- package/vendor/stylo/rule_tree/map.rs +201 -0
- package/vendor/stylo/rule_tree/mod.rs +427 -0
- package/vendor/stylo/rule_tree/source.rs +80 -0
- package/vendor/stylo/rule_tree/unsafe_box.rs +78 -0
- package/vendor/stylo/scoped_tls.rs +80 -0
- package/vendor/stylo/selector_map.rs +1050 -0
- package/vendor/stylo/selector_parser.rs +305 -0
- package/vendor/stylo/servo/animation.rs +1922 -0
- package/vendor/stylo/servo/attr.rs +714 -0
- package/vendor/stylo/servo/encoding_support.rs +81 -0
- package/vendor/stylo/servo/media_features.rs +254 -0
- package/vendor/stylo/servo/mod.rs +16 -0
- package/vendor/stylo/servo/restyle_damage.rs +215 -0
- package/vendor/stylo/servo/selector_parser.rs +1036 -0
- package/vendor/stylo/servo/shadow_parts.rs +207 -0
- package/vendor/stylo/shared_lock.rs +271 -0
- package/vendor/stylo/sharing/checks.rs +281 -0
- package/vendor/stylo/sharing/mod.rs +1000 -0
- package/vendor/stylo/simple_buckets_map.rs +52 -0
- package/vendor/stylo/str.rs +168 -0
- package/vendor/stylo/style_adjuster.rs +1086 -0
- package/vendor/stylo/style_resolver.rs +658 -0
- package/vendor/stylo/stylesheet_set.rs +658 -0
- package/vendor/stylo/stylesheets/appearance_base_rule.rs +51 -0
- package/vendor/stylo/stylesheets/container_rule.rs +769 -0
- package/vendor/stylo/stylesheets/counter_style_rule.rs +7 -0
- package/vendor/stylo/stylesheets/document_rule.rs +272 -0
- package/vendor/stylo/stylesheets/font_face_rule.rs +7 -0
- package/vendor/stylo/stylesheets/font_feature_values_rule.rs +481 -0
- package/vendor/stylo/stylesheets/font_palette_values_rule.rs +304 -0
- package/vendor/stylo/stylesheets/import_rule.rs +259 -0
- package/vendor/stylo/stylesheets/keyframes_rule.rs +733 -0
- package/vendor/stylo/stylesheets/layer_rule.rs +221 -0
- package/vendor/stylo/stylesheets/loader.rs +29 -0
- package/vendor/stylo/stylesheets/margin_rule.rs +190 -0
- package/vendor/stylo/stylesheets/media_rule.rs +191 -0
- package/vendor/stylo/stylesheets/mod.rs +918 -0
- package/vendor/stylo/stylesheets/namespace_rule.rs +43 -0
- package/vendor/stylo/stylesheets/nested_declarations_rule.rs +51 -0
- package/vendor/stylo/stylesheets/origin.rs +249 -0
- package/vendor/stylo/stylesheets/page_rule.rs +354 -0
- package/vendor/stylo/stylesheets/position_try_rule.rs +68 -0
- package/vendor/stylo/stylesheets/property_rule.rs +5 -0
- package/vendor/stylo/stylesheets/rule_list.rs +167 -0
- package/vendor/stylo/stylesheets/rule_parser.rs +1189 -0
- package/vendor/stylo/stylesheets/rules_iterator.rs +397 -0
- package/vendor/stylo/stylesheets/scope_rule.rs +511 -0
- package/vendor/stylo/stylesheets/starting_style_rule.rs +54 -0
- package/vendor/stylo/stylesheets/style_rule.rs +80 -0
- package/vendor/stylo/stylesheets/stylesheet.rs +570 -0
- package/vendor/stylo/stylesheets/supports_rule.rs +486 -0
- package/vendor/stylo/stylesheets/view_transition_rule.rs +81 -0
- package/vendor/stylo/stylist.rs +5076 -0
- package/vendor/stylo/thread_state.rs +87 -0
- package/vendor/stylo/traversal.rs +765 -0
- package/vendor/stylo/traversal_flags.rs +68 -0
- package/vendor/stylo/typed_om/mod.rs +981 -0
- package/vendor/stylo/typed_om/numeric.rs +128 -0
- package/vendor/stylo/typed_om/numeric_declaration.rs +93 -0
- package/vendor/stylo/typed_om/numeric_type.rs +640 -0
- package/vendor/stylo/typed_om/sum_value.rs +426 -0
- package/vendor/stylo/url/gecko.rs +367 -0
- package/vendor/stylo/url/mod.rs +71 -0
- package/vendor/stylo/url/servo.rs +239 -0
- package/vendor/stylo/use_counters/mod.rs +137 -0
- package/vendor/stylo/values/animated/color.rs +103 -0
- package/vendor/stylo/values/animated/effects.rs +26 -0
- package/vendor/stylo/values/animated/font.rs +37 -0
- package/vendor/stylo/values/animated/grid.rs +161 -0
- package/vendor/stylo/values/animated/lists.rs +141 -0
- package/vendor/stylo/values/animated/mod.rs +550 -0
- package/vendor/stylo/values/animated/svg.rs +46 -0
- package/vendor/stylo/values/animated/text.rs +58 -0
- package/vendor/stylo/values/animated/transform.rs +1592 -0
- package/vendor/stylo/values/computed/align.rs +90 -0
- package/vendor/stylo/values/computed/angle.rs +130 -0
- package/vendor/stylo/values/computed/animation.rs +124 -0
- package/vendor/stylo/values/computed/background.rs +13 -0
- package/vendor/stylo/values/computed/basic_shape.rs +267 -0
- package/vendor/stylo/values/computed/border.rs +163 -0
- package/vendor/stylo/values/computed/box.rs +267 -0
- package/vendor/stylo/values/computed/calc.rs +334 -0
- package/vendor/stylo/values/computed/color.rs +161 -0
- package/vendor/stylo/values/computed/column.rs +11 -0
- package/vendor/stylo/values/computed/corner_shape.rs +155 -0
- package/vendor/stylo/values/computed/counters.rs +26 -0
- package/vendor/stylo/values/computed/easing.rs +109 -0
- package/vendor/stylo/values/computed/effects.rs +30 -0
- package/vendor/stylo/values/computed/flex.rs +19 -0
- package/vendor/stylo/values/computed/font.rs +1517 -0
- package/vendor/stylo/values/computed/image.rs +273 -0
- package/vendor/stylo/values/computed/length.rs +576 -0
- package/vendor/stylo/values/computed/length_percentage.rs +1055 -0
- package/vendor/stylo/values/computed/list.rs +16 -0
- package/vendor/stylo/values/computed/mod.rs +1141 -0
- package/vendor/stylo/values/computed/motion.rs +73 -0
- package/vendor/stylo/values/computed/outline.rs +7 -0
- package/vendor/stylo/values/computed/page.rs +77 -0
- package/vendor/stylo/values/computed/param.rs +9 -0
- package/vendor/stylo/values/computed/percentage.rs +143 -0
- package/vendor/stylo/values/computed/position.rs +273 -0
- package/vendor/stylo/values/computed/ratio.rs +88 -0
- package/vendor/stylo/values/computed/rect.rs +19 -0
- package/vendor/stylo/values/computed/resolution.rs +76 -0
- package/vendor/stylo/values/computed/svg.rs +66 -0
- package/vendor/stylo/values/computed/table.rs +7 -0
- package/vendor/stylo/values/computed/text.rs +199 -0
- package/vendor/stylo/values/computed/time.rs +83 -0
- package/vendor/stylo/values/computed/transform.rs +559 -0
- package/vendor/stylo/values/computed/tree_counting.rs +30 -0
- package/vendor/stylo/values/computed/ui.rs +23 -0
- package/vendor/stylo/values/computed/url.rs +12 -0
- package/vendor/stylo/values/distance.rs +139 -0
- package/vendor/stylo/values/generics/animation.rs +363 -0
- package/vendor/stylo/values/generics/background.rs +65 -0
- package/vendor/stylo/values/generics/basic_shape.rs +1389 -0
- package/vendor/stylo/values/generics/border.rs +303 -0
- package/vendor/stylo/values/generics/box.rs +552 -0
- package/vendor/stylo/values/generics/calc.rs +2986 -0
- package/vendor/stylo/values/generics/color.rs +318 -0
- package/vendor/stylo/values/generics/column.rs +44 -0
- package/vendor/stylo/values/generics/counters.rs +320 -0
- package/vendor/stylo/values/generics/easing.rs +132 -0
- package/vendor/stylo/values/generics/effects.rs +131 -0
- package/vendor/stylo/values/generics/flex.rs +34 -0
- package/vendor/stylo/values/generics/font.rs +376 -0
- package/vendor/stylo/values/generics/grid.rs +962 -0
- package/vendor/stylo/values/generics/image.rs +653 -0
- package/vendor/stylo/values/generics/length.rs +656 -0
- package/vendor/stylo/values/generics/mod.rs +303 -0
- package/vendor/stylo/values/generics/motion.rs +274 -0
- package/vendor/stylo/values/generics/page.rs +167 -0
- package/vendor/stylo/values/generics/position.rs +669 -0
- package/vendor/stylo/values/generics/ratio.rs +88 -0
- package/vendor/stylo/values/generics/rect.rs +147 -0
- package/vendor/stylo/values/generics/size.rs +102 -0
- package/vendor/stylo/values/generics/svg.rs +226 -0
- package/vendor/stylo/values/generics/text.rs +268 -0
- package/vendor/stylo/values/generics/transform.rs +1157 -0
- package/vendor/stylo/values/generics/ui.rs +147 -0
- package/vendor/stylo/values/generics/url.rs +51 -0
- package/vendor/stylo/values/mod.rs +820 -0
- package/vendor/stylo/values/resolved/animation.rs +33 -0
- package/vendor/stylo/values/resolved/color.rs +53 -0
- package/vendor/stylo/values/resolved/counters.rs +45 -0
- package/vendor/stylo/values/resolved/mod.rs +316 -0
- package/vendor/stylo/values/specified/align.rs +711 -0
- package/vendor/stylo/values/specified/angle.rs +407 -0
- package/vendor/stylo/values/specified/animation.rs +921 -0
- package/vendor/stylo/values/specified/background.rs +270 -0
- package/vendor/stylo/values/specified/basic_shape.rs +986 -0
- package/vendor/stylo/values/specified/border.rs +430 -0
- package/vendor/stylo/values/specified/box.rs +2231 -0
- package/vendor/stylo/values/specified/calc.rs +1657 -0
- package/vendor/stylo/values/specified/color.rs +1210 -0
- package/vendor/stylo/values/specified/column.rs +11 -0
- package/vendor/stylo/values/specified/corner_shape.rs +152 -0
- package/vendor/stylo/values/specified/counters.rs +242 -0
- package/vendor/stylo/values/specified/easing.rs +225 -0
- package/vendor/stylo/values/specified/effects.rs +421 -0
- package/vendor/stylo/values/specified/flex.rs +42 -0
- package/vendor/stylo/values/specified/font.rs +2103 -0
- package/vendor/stylo/values/specified/frequency.rs +39 -0
- package/vendor/stylo/values/specified/grid.rs +407 -0
- package/vendor/stylo/values/specified/image.rs +1385 -0
- package/vendor/stylo/values/specified/intersection_observer.rs +80 -0
- package/vendor/stylo/values/specified/length.rs +2024 -0
- package/vendor/stylo/values/specified/list.rs +190 -0
- package/vendor/stylo/values/specified/mod.rs +531 -0
- package/vendor/stylo/values/specified/motion.rs +316 -0
- package/vendor/stylo/values/specified/number.rs +603 -0
- package/vendor/stylo/values/specified/outline.rs +69 -0
- package/vendor/stylo/values/specified/page.rs +94 -0
- package/vendor/stylo/values/specified/param.rs +121 -0
- package/vendor/stylo/values/specified/percentage.rs +364 -0
- package/vendor/stylo/values/specified/position.rs +2247 -0
- package/vendor/stylo/values/specified/ratio.rs +29 -0
- package/vendor/stylo/values/specified/rect.rs +28 -0
- package/vendor/stylo/values/specified/resolution.rs +237 -0
- package/vendor/stylo/values/specified/source_size_list.rs +135 -0
- package/vendor/stylo/values/specified/svg.rs +427 -0
- package/vendor/stylo/values/specified/svg_path.rs +1115 -0
- package/vendor/stylo/values/specified/table.rs +38 -0
- package/vendor/stylo/values/specified/text.rs +1561 -0
- package/vendor/stylo/values/specified/time.rs +313 -0
- package/vendor/stylo/values/specified/transform.rs +599 -0
- package/vendor/stylo/values/specified/tree_counting.rs +32 -0
- package/vendor/stylo/values/specified/ui.rs +344 -0
- package/vendor/stylo/values/specified/url.rs +12 -0
- package/vendor/stylo/values/tagged_numeric.rs +253 -0
- package/wasm/blitz_pdf.d.ts +67 -0
- package/wasm/blitz_pdf.js +699 -0
- package/wasm/blitz_pdf_bg.wasm +0 -0
- package/wasm/blitz_pdf_bg.wasm.d.ts +20 -0
|
@@ -0,0 +1,2704 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
//! The main cascading algorithm of the style system.
|
|
6
|
+
|
|
7
|
+
use crate::applicable_declarations::{CascadePriority, RevertKind};
|
|
8
|
+
use crate::color::AbsoluteColor;
|
|
9
|
+
use crate::computed_value_flags::ComputedValueFlags;
|
|
10
|
+
use crate::context::TreeCountingCaches;
|
|
11
|
+
use crate::custom_properties::{
|
|
12
|
+
get_attr_value_for_cycle_resolution, handle_invalid_at_computed_value_time,
|
|
13
|
+
remove_and_insert_initial_value, substitute_references_if_needed_and_apply,
|
|
14
|
+
ComputedCustomProperties, ComputedSubstitutionFunctions, Name, NonCustomReferenceMap,
|
|
15
|
+
ReferenceFlags, References, SingleNonCustomReference, SubstitutionFunctionKind, VariableValue,
|
|
16
|
+
};
|
|
17
|
+
use crate::dom::{AttributeTracker, DummyElementContext, ElementContext, TElement};
|
|
18
|
+
#[cfg(feature = "gecko")]
|
|
19
|
+
use crate::font_metrics::FontMetricsOrientation;
|
|
20
|
+
use crate::properties::{
|
|
21
|
+
property_counts, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, LonghandId,
|
|
22
|
+
LonghandIdSet, PrioritaryPropertyId, PrioritaryPropertyIdSet, PropertyDeclaration,
|
|
23
|
+
PropertyDeclarationId, PropertyFlags, ShorthandsWithPropertyReferencesCache, StyleBuilder,
|
|
24
|
+
CASCADE_PROPERTY,
|
|
25
|
+
};
|
|
26
|
+
use crate::properties::{CustomDeclaration, CustomDeclarationValue, UnparsedValue};
|
|
27
|
+
use crate::properties_and_values::rule::Descriptors as PropertyDescriptors;
|
|
28
|
+
use crate::properties_and_values::value::ComputedValue as ComputedRegisteredValue;
|
|
29
|
+
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
|
30
|
+
use crate::rule_tree::{CascadeLevel, CascadeOrigin, RuleCascadeFlags, StrongRuleNode};
|
|
31
|
+
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};
|
|
32
|
+
use crate::selector_parser::PseudoElement;
|
|
33
|
+
use crate::shared_lock::StylesheetGuards;
|
|
34
|
+
use crate::style_adjuster::StyleAdjuster;
|
|
35
|
+
use crate::stylesheets::container_rule::ContainerSizeQuery;
|
|
36
|
+
use crate::stylesheets::layer_rule::LayerOrder;
|
|
37
|
+
use crate::stylesheets::UrlExtraData;
|
|
38
|
+
use crate::stylist::Stylist;
|
|
39
|
+
use crate::values::computed::ToComputedValue;
|
|
40
|
+
#[cfg(feature = "gecko")]
|
|
41
|
+
use crate::values::specified::length::FontBaseSize;
|
|
42
|
+
use crate::values::specified::position::PositionTryFallbacksTryTactic;
|
|
43
|
+
use crate::values::{computed, specified};
|
|
44
|
+
use rustc_hash::FxHashMap;
|
|
45
|
+
use selectors::matching::ElementSelectorFlags;
|
|
46
|
+
use servo_arc::Arc;
|
|
47
|
+
use smallvec::SmallVec;
|
|
48
|
+
use std::borrow::Cow;
|
|
49
|
+
use std::cmp;
|
|
50
|
+
use std::collections::hash_map::Entry;
|
|
51
|
+
|
|
52
|
+
/// Whether we're resolving a style with the purposes of reparenting for ::first-line.
|
|
53
|
+
#[derive(Copy, Clone)]
|
|
54
|
+
#[allow(missing_docs)]
|
|
55
|
+
pub enum FirstLineReparenting<'a> {
|
|
56
|
+
No,
|
|
57
|
+
Yes {
|
|
58
|
+
/// The style we're re-parenting for ::first-line. ::first-line only affects inherited
|
|
59
|
+
/// properties so we use this to avoid some work and also ensure correctness by copying the
|
|
60
|
+
/// reset structs from this style.
|
|
61
|
+
style_to_reparent: &'a ComputedValues,
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Performs the CSS cascade, computing new styles for an element from its parent style.
|
|
66
|
+
///
|
|
67
|
+
/// The arguments are:
|
|
68
|
+
///
|
|
69
|
+
/// * `device`: Used to get the initial viewport and other external state.
|
|
70
|
+
///
|
|
71
|
+
/// * `rule_node`: The rule node in the tree that represent the CSS rules that
|
|
72
|
+
/// matched.
|
|
73
|
+
///
|
|
74
|
+
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
|
|
75
|
+
///
|
|
76
|
+
/// Returns the computed values.
|
|
77
|
+
/// * `flags`: Various flags.
|
|
78
|
+
///
|
|
79
|
+
pub fn cascade<E>(
|
|
80
|
+
stylist: &Stylist,
|
|
81
|
+
pseudo: Option<&PseudoElement>,
|
|
82
|
+
rule_node: &StrongRuleNode,
|
|
83
|
+
guards: &StylesheetGuards,
|
|
84
|
+
parent_style: Option<&ComputedValues>,
|
|
85
|
+
layout_parent_style: Option<&ComputedValues>,
|
|
86
|
+
first_line_reparenting: FirstLineReparenting,
|
|
87
|
+
try_tactic: &PositionTryFallbacksTryTactic,
|
|
88
|
+
visited_rules: Option<&StrongRuleNode>,
|
|
89
|
+
cascade_input_flags: ComputedValueFlags,
|
|
90
|
+
included_cascade_flags: RuleCascadeFlags,
|
|
91
|
+
rule_cache: Option<&RuleCache>,
|
|
92
|
+
rule_cache_conditions: &mut RuleCacheConditions,
|
|
93
|
+
element: Option<E>,
|
|
94
|
+
tree_counting_caches: &mut TreeCountingCaches,
|
|
95
|
+
) -> Arc<ComputedValues>
|
|
96
|
+
where
|
|
97
|
+
E: TElement,
|
|
98
|
+
{
|
|
99
|
+
cascade_rules(
|
|
100
|
+
stylist,
|
|
101
|
+
pseudo,
|
|
102
|
+
rule_node,
|
|
103
|
+
guards,
|
|
104
|
+
parent_style,
|
|
105
|
+
layout_parent_style,
|
|
106
|
+
first_line_reparenting,
|
|
107
|
+
try_tactic,
|
|
108
|
+
CascadeMode::Unvisited { visited_rules },
|
|
109
|
+
cascade_input_flags,
|
|
110
|
+
included_cascade_flags,
|
|
111
|
+
rule_cache,
|
|
112
|
+
rule_cache_conditions,
|
|
113
|
+
element,
|
|
114
|
+
tree_counting_caches,
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
struct DeclarationIterator<'a> {
|
|
119
|
+
// Global to the iteration.
|
|
120
|
+
guards: &'a StylesheetGuards<'a>,
|
|
121
|
+
restriction: Option<PropertyFlags>,
|
|
122
|
+
// The rule we're iterating over.
|
|
123
|
+
current_rule_node: Option<&'a StrongRuleNode>,
|
|
124
|
+
// Per rule state.
|
|
125
|
+
declarations: DeclarationImportanceIterator<'a>,
|
|
126
|
+
priority: CascadePriority,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
impl<'a> DeclarationIterator<'a> {
|
|
130
|
+
#[inline]
|
|
131
|
+
fn new(
|
|
132
|
+
rule_node: &'a StrongRuleNode,
|
|
133
|
+
guards: &'a StylesheetGuards,
|
|
134
|
+
pseudo: Option<&PseudoElement>,
|
|
135
|
+
) -> Self {
|
|
136
|
+
let restriction = pseudo.and_then(|p| p.property_restriction());
|
|
137
|
+
let mut iter = Self {
|
|
138
|
+
guards,
|
|
139
|
+
current_rule_node: Some(rule_node),
|
|
140
|
+
priority: CascadePriority::new(
|
|
141
|
+
CascadeLevel::new(CascadeOrigin::UA),
|
|
142
|
+
LayerOrder::root(),
|
|
143
|
+
RuleCascadeFlags::empty(),
|
|
144
|
+
),
|
|
145
|
+
declarations: DeclarationImportanceIterator::default(),
|
|
146
|
+
restriction,
|
|
147
|
+
};
|
|
148
|
+
iter.update_for_node(rule_node);
|
|
149
|
+
iter
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fn update_for_node(&mut self, node: &'a StrongRuleNode) {
|
|
153
|
+
self.priority = node.cascade_priority();
|
|
154
|
+
let guard = self.priority.cascade_level().origin().guard(self.guards);
|
|
155
|
+
self.declarations = match node.style_source() {
|
|
156
|
+
Some(source) => source.read(guard).declaration_importance_iter(),
|
|
157
|
+
None => DeclarationImportanceIterator::default(),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
impl<'a> Iterator for DeclarationIterator<'a> {
|
|
163
|
+
type Item = (&'a PropertyDeclaration, CascadePriority);
|
|
164
|
+
|
|
165
|
+
#[inline]
|
|
166
|
+
fn next(&mut self) -> Option<Self::Item> {
|
|
167
|
+
loop {
|
|
168
|
+
if let Some((decl, importance)) = self.declarations.next_back() {
|
|
169
|
+
if self.priority.cascade_level().is_important() != importance.important() {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if let Some(restriction) = self.restriction {
|
|
174
|
+
// decl.id() is either a longhand or a custom
|
|
175
|
+
// property. Custom properties are always allowed, but
|
|
176
|
+
// longhands are only allowed if they have our
|
|
177
|
+
// restriction flag set.
|
|
178
|
+
if let PropertyDeclarationId::Longhand(id) = decl.id() {
|
|
179
|
+
if !id.flags().contains(restriction)
|
|
180
|
+
&& self.priority.cascade_level().origin() != CascadeOrigin::UA
|
|
181
|
+
{
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return Some((decl, self.priority));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let next_node = self.current_rule_node.take()?.parent()?;
|
|
191
|
+
self.current_rule_node = Some(next_node);
|
|
192
|
+
self.update_for_node(next_node);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fn cascade_rules<E>(
|
|
198
|
+
stylist: &Stylist,
|
|
199
|
+
pseudo: Option<&PseudoElement>,
|
|
200
|
+
rule_node: &StrongRuleNode,
|
|
201
|
+
guards: &StylesheetGuards,
|
|
202
|
+
parent_style: Option<&ComputedValues>,
|
|
203
|
+
layout_parent_style: Option<&ComputedValues>,
|
|
204
|
+
first_line_reparenting: FirstLineReparenting,
|
|
205
|
+
try_tactic: &PositionTryFallbacksTryTactic,
|
|
206
|
+
cascade_mode: CascadeMode,
|
|
207
|
+
cascade_input_flags: ComputedValueFlags,
|
|
208
|
+
included_cascade_flags: RuleCascadeFlags,
|
|
209
|
+
rule_cache: Option<&RuleCache>,
|
|
210
|
+
rule_cache_conditions: &mut RuleCacheConditions,
|
|
211
|
+
element: Option<E>,
|
|
212
|
+
tree_counting_caches: &mut TreeCountingCaches,
|
|
213
|
+
) -> Arc<ComputedValues>
|
|
214
|
+
where
|
|
215
|
+
E: TElement,
|
|
216
|
+
{
|
|
217
|
+
apply_declarations(
|
|
218
|
+
stylist,
|
|
219
|
+
pseudo,
|
|
220
|
+
rule_node,
|
|
221
|
+
guards,
|
|
222
|
+
DeclarationIterator::new(rule_node, guards, pseudo),
|
|
223
|
+
parent_style,
|
|
224
|
+
layout_parent_style,
|
|
225
|
+
first_line_reparenting,
|
|
226
|
+
try_tactic,
|
|
227
|
+
cascade_mode,
|
|
228
|
+
cascade_input_flags,
|
|
229
|
+
included_cascade_flags,
|
|
230
|
+
rule_cache,
|
|
231
|
+
rule_cache_conditions,
|
|
232
|
+
element,
|
|
233
|
+
tree_counting_caches,
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/// Whether we're cascading for visited or unvisited styles.
|
|
238
|
+
#[derive(Clone, Copy)]
|
|
239
|
+
pub enum CascadeMode<'a, 'b> {
|
|
240
|
+
/// We're cascading for unvisited styles.
|
|
241
|
+
Unvisited {
|
|
242
|
+
/// The visited rules that should match the visited style.
|
|
243
|
+
visited_rules: Option<&'a StrongRuleNode>,
|
|
244
|
+
},
|
|
245
|
+
/// We're cascading for visited styles.
|
|
246
|
+
Visited {
|
|
247
|
+
/// The cascade for our unvisited style.
|
|
248
|
+
unvisited_context: &'a computed::Context<'b>,
|
|
249
|
+
/// The properties set on the unvisited style. These are useful if we can prove that the
|
|
250
|
+
/// visited rules are the same as the unvisited declarations.
|
|
251
|
+
unvisited_properties: &'a LonghandIdSet,
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
fn iter_declarations<'c, 'decls: 'c>(
|
|
256
|
+
iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
|
|
257
|
+
declarations: &mut Declarations<'decls>,
|
|
258
|
+
mut custom: Option<(&mut Cascade<'c>, &mut computed::Context)>,
|
|
259
|
+
attribute_tracker: &mut AttributeTracker,
|
|
260
|
+
) {
|
|
261
|
+
for (declaration, priority) in iter {
|
|
262
|
+
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
|
263
|
+
if let Some((ref mut cascade, ref mut context)) = custom {
|
|
264
|
+
cascade.cascade_custom_property(context, declaration, priority);
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
let id = declaration.id().as_longhand().unwrap();
|
|
268
|
+
declarations.note_declaration(declaration, priority, id);
|
|
269
|
+
if Cascade::might_have_non_custom_or_attr_dependency(id, declaration) {
|
|
270
|
+
if let Some((ref mut cascade, ref mut context)) = custom {
|
|
271
|
+
cascade.maybe_note_non_custom_dependency(
|
|
272
|
+
context,
|
|
273
|
+
id,
|
|
274
|
+
declaration,
|
|
275
|
+
attribute_tracker,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/// NOTE: This function expects the declaration with more priority to appear
|
|
284
|
+
/// first.
|
|
285
|
+
pub fn apply_declarations<'decls, E, I>(
|
|
286
|
+
stylist: &Stylist,
|
|
287
|
+
pseudo: Option<&PseudoElement>,
|
|
288
|
+
rules: &StrongRuleNode,
|
|
289
|
+
guards: &StylesheetGuards,
|
|
290
|
+
iter: I,
|
|
291
|
+
parent_style: Option<&ComputedValues>,
|
|
292
|
+
layout_parent_style: Option<&ComputedValues>,
|
|
293
|
+
first_line_reparenting: FirstLineReparenting<'_>,
|
|
294
|
+
try_tactic: &PositionTryFallbacksTryTactic,
|
|
295
|
+
cascade_mode: CascadeMode,
|
|
296
|
+
cascade_input_flags: ComputedValueFlags,
|
|
297
|
+
included_cascade_flags: RuleCascadeFlags,
|
|
298
|
+
rule_cache: Option<&RuleCache>,
|
|
299
|
+
rule_cache_conditions: &mut RuleCacheConditions,
|
|
300
|
+
element: Option<E>,
|
|
301
|
+
tree_counting_caches: &mut TreeCountingCaches,
|
|
302
|
+
) -> Arc<ComputedValues>
|
|
303
|
+
where
|
|
304
|
+
E: TElement,
|
|
305
|
+
I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
|
|
306
|
+
{
|
|
307
|
+
debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
|
|
308
|
+
let device = stylist.device();
|
|
309
|
+
let inherited_style = parent_style.unwrap_or(device.default_computed_values());
|
|
310
|
+
let is_root_element = pseudo.is_none() && element.is_some_and(|e| e.is_root());
|
|
311
|
+
let container_size_query =
|
|
312
|
+
ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());
|
|
313
|
+
|
|
314
|
+
let originating_element = element.map(|e| e.ultimate_originating_element());
|
|
315
|
+
let element_context = match originating_element {
|
|
316
|
+
Some(ref e) => e as &dyn ElementContext,
|
|
317
|
+
None => &DummyElementContext {},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
let mut context = computed::Context::new(
|
|
321
|
+
// We'd really like to own the rules here to avoid refcount traffic, but
|
|
322
|
+
// animation's usage of `apply_declarations` make this tricky. See bug
|
|
323
|
+
// 1375525.
|
|
324
|
+
StyleBuilder::new(
|
|
325
|
+
device,
|
|
326
|
+
Some(stylist),
|
|
327
|
+
parent_style,
|
|
328
|
+
pseudo,
|
|
329
|
+
Some(rules.clone()),
|
|
330
|
+
is_root_element,
|
|
331
|
+
),
|
|
332
|
+
stylist.quirks_mode(),
|
|
333
|
+
rule_cache_conditions,
|
|
334
|
+
container_size_query,
|
|
335
|
+
included_cascade_flags,
|
|
336
|
+
element_context,
|
|
337
|
+
tree_counting_caches,
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
context.style().add_flags(cascade_input_flags);
|
|
341
|
+
// Reuse flags from computing registered custom properties initial values, such as
|
|
342
|
+
// whether they depend on viewport units.
|
|
343
|
+
context
|
|
344
|
+
.style()
|
|
345
|
+
.add_flags(stylist.get_custom_property_initial_values_flags());
|
|
346
|
+
|
|
347
|
+
let using_cached_reset_properties;
|
|
348
|
+
let ignore_colors = context.builder.device.forced_colors().is_active();
|
|
349
|
+
let mut cascade = Cascade::new(first_line_reparenting, stylist, ignore_colors);
|
|
350
|
+
let mut declarations = Default::default();
|
|
351
|
+
let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
|
|
352
|
+
let mut attribute_tracker = AttributeTracker::new(element_context);
|
|
353
|
+
|
|
354
|
+
let properties_to_apply = match cascade_mode {
|
|
355
|
+
CascadeMode::Visited {
|
|
356
|
+
unvisited_context,
|
|
357
|
+
unvisited_properties,
|
|
358
|
+
} => {
|
|
359
|
+
context.builder.substitution_functions =
|
|
360
|
+
unvisited_context.builder.substitution_functions.clone();
|
|
361
|
+
context.builder.writing_mode = unvisited_context.builder.writing_mode;
|
|
362
|
+
context.builder.color_scheme = unvisited_context.builder.color_scheme;
|
|
363
|
+
// We never insert visited styles into the cache so we don't need to try looking it up.
|
|
364
|
+
// It also wouldn't be super-profitable, only a handful :visited properties are
|
|
365
|
+
// non-inherited.
|
|
366
|
+
using_cached_reset_properties = false;
|
|
367
|
+
let visited_dependent_props = LonghandIdSet::visited_dependent();
|
|
368
|
+
// If we match the same rules when visited and unvisited, and we know there isn't any
|
|
369
|
+
// visited-dependent properties, we can avoid gathering the declarations, we know none
|
|
370
|
+
// would be relevant.
|
|
371
|
+
if unvisited_context.builder.rules.as_ref() != Some(rules)
|
|
372
|
+
|| unvisited_properties.contains_any(visited_dependent_props)
|
|
373
|
+
{
|
|
374
|
+
iter_declarations(iter, &mut declarations, None, &mut attribute_tracker);
|
|
375
|
+
}
|
|
376
|
+
visited_dependent_props
|
|
377
|
+
},
|
|
378
|
+
CascadeMode::Unvisited { .. } => {
|
|
379
|
+
cascade.init_custom_properties(&mut context);
|
|
380
|
+
iter_declarations(
|
|
381
|
+
iter,
|
|
382
|
+
&mut declarations,
|
|
383
|
+
Some((&mut cascade, &mut context)),
|
|
384
|
+
&mut attribute_tracker,
|
|
385
|
+
);
|
|
386
|
+
// Detect cycles, remove properties participating in them, and resolve custom
|
|
387
|
+
// properties, applying prioritary properties (font-size, color-scheme, line-height)
|
|
388
|
+
// interleaved so that a custom property depending on `em`/`lh`/the used color-scheme is
|
|
389
|
+
// substituted once the prioritary property it needs has been applied.
|
|
390
|
+
cascade.apply_custom_and_prioritary_properties(
|
|
391
|
+
&mut context,
|
|
392
|
+
&declarations,
|
|
393
|
+
&mut shorthand_cache,
|
|
394
|
+
&mut attribute_tracker,
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
using_cached_reset_properties =
|
|
398
|
+
cascade.try_to_use_cached_reset_properties(&mut context, rule_cache, guards);
|
|
399
|
+
|
|
400
|
+
if using_cached_reset_properties {
|
|
401
|
+
LonghandIdSet::late_group_only_inherited()
|
|
402
|
+
} else {
|
|
403
|
+
LonghandIdSet::late_group()
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
cascade.apply_non_prioritary_properties(
|
|
409
|
+
&mut context,
|
|
410
|
+
&declarations.longhand_declarations,
|
|
411
|
+
&mut shorthand_cache,
|
|
412
|
+
properties_to_apply,
|
|
413
|
+
&mut attribute_tracker,
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
context.builder.attribute_references = attribute_tracker.finalize();
|
|
417
|
+
|
|
418
|
+
cascade.finished_applying_properties(&mut context.builder);
|
|
419
|
+
|
|
420
|
+
context.builder.clear_modified_reset();
|
|
421
|
+
|
|
422
|
+
if let CascadeMode::Unvisited { visited_rules } = cascade_mode {
|
|
423
|
+
if let Some(visited_rules) = visited_rules {
|
|
424
|
+
cascade.compute_visited_style_if_needed(
|
|
425
|
+
&mut context,
|
|
426
|
+
element,
|
|
427
|
+
parent_style,
|
|
428
|
+
layout_parent_style,
|
|
429
|
+
try_tactic,
|
|
430
|
+
visited_rules,
|
|
431
|
+
guards,
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
StyleAdjuster::new(&mut context.builder).adjust(
|
|
436
|
+
layout_parent_style.unwrap_or(inherited_style),
|
|
437
|
+
element,
|
|
438
|
+
try_tactic,
|
|
439
|
+
&cascade.author_specified,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if context.builder.modified_reset() || using_cached_reset_properties {
|
|
444
|
+
// If we adjusted any reset structs, we can't cache this ComputedValues.
|
|
445
|
+
//
|
|
446
|
+
// Also, if we re-used existing reset structs, don't bother caching it back again. (Aside
|
|
447
|
+
// from being wasted effort, it will be wrong, since context.rule_cache_conditions won't be
|
|
448
|
+
// set appropriately if we didn't compute those reset properties.)
|
|
449
|
+
context.rule_cache_conditions.borrow_mut().set_uncacheable();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if context
|
|
453
|
+
.builder
|
|
454
|
+
.flags()
|
|
455
|
+
.intersects(ComputedValueFlags::tree_counting_function_flags())
|
|
456
|
+
{
|
|
457
|
+
if let Some(el) = element {
|
|
458
|
+
el.apply_selector_flags(ElementSelectorFlags::MAY_HAVE_TREE_COUNTING_FUNCTION);
|
|
459
|
+
} else {
|
|
460
|
+
debug_assert!(
|
|
461
|
+
false,
|
|
462
|
+
"Tree counting function flag applied without an element?"
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
context.builder.build()
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/// For ignored colors mode, we sometimes want to do something equivalent to
|
|
471
|
+
/// "revert-or-initial", where we `revert` for a given origin, but then apply a
|
|
472
|
+
/// given initial value if nothing in other origins did override it.
|
|
473
|
+
///
|
|
474
|
+
/// This is a bit of a clunky way of achieving this.
|
|
475
|
+
type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
|
|
476
|
+
|
|
477
|
+
fn is_base_appearance(context: &computed::Context) -> bool {
|
|
478
|
+
use computed::Appearance;
|
|
479
|
+
let box_style = context.builder.get_box();
|
|
480
|
+
match box_style.clone_appearance() {
|
|
481
|
+
Appearance::BaseSelect => {
|
|
482
|
+
matches!(
|
|
483
|
+
box_style.clone__moz_default_appearance(),
|
|
484
|
+
Appearance::Listbox | Appearance::Menulist
|
|
485
|
+
)
|
|
486
|
+
},
|
|
487
|
+
Appearance::Base => box_style.clone__moz_default_appearance() != Appearance::None,
|
|
488
|
+
_ => false,
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
fn tweak_when_ignoring_colors(
|
|
493
|
+
context: &computed::Context,
|
|
494
|
+
longhand_id: LonghandId,
|
|
495
|
+
origin: CascadeOrigin,
|
|
496
|
+
declaration: &mut Cow<PropertyDeclaration>,
|
|
497
|
+
declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,
|
|
498
|
+
) {
|
|
499
|
+
use crate::values::computed::ToComputedValue;
|
|
500
|
+
use crate::values::specified::Color;
|
|
501
|
+
|
|
502
|
+
if !longhand_id.ignored_when_document_colors_disabled() {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
let is_ua_or_user_rule = matches!(origin, CascadeOrigin::User | CascadeOrigin::UA);
|
|
507
|
+
if is_ua_or_user_rule {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Always honor colors if forced-color-adjust is set to none.
|
|
512
|
+
let forced = context
|
|
513
|
+
.builder
|
|
514
|
+
.get_inherited_text()
|
|
515
|
+
.clone_forced_color_adjust();
|
|
516
|
+
if forced == computed::ForcedColorAdjust::None {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
|
|
521
|
+
// We assume here currentColor is opaque.
|
|
522
|
+
color
|
|
523
|
+
.to_computed_value(context)
|
|
524
|
+
.resolve_to_absolute(&AbsoluteColor::BLACK)
|
|
525
|
+
.alpha
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// A few special-cases ahead.
|
|
529
|
+
match **declaration {
|
|
530
|
+
// Honor CSS-wide keywords like unset / revert / initial...
|
|
531
|
+
PropertyDeclaration::CSSWideKeyword(..) => return,
|
|
532
|
+
PropertyDeclaration::BackgroundColor(ref color) => {
|
|
533
|
+
// We honor system colors and transparent colors unconditionally.
|
|
534
|
+
//
|
|
535
|
+
// NOTE(emilio): We honor transparent unconditionally, like we do
|
|
536
|
+
// for color, even though it causes issues like bug 1625036. The
|
|
537
|
+
// reasoning is that the conditions that trigger that (having
|
|
538
|
+
// mismatched widget and default backgrounds) are both uncommon, and
|
|
539
|
+
// broken in other applications as well, and not honoring
|
|
540
|
+
// transparent makes stuff uglier or break unconditionally
|
|
541
|
+
// (bug 1666059, bug 1755713).
|
|
542
|
+
if color.honored_in_forced_colors_mode(context, /* allow_transparent = */ true) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
// For background-color, we revert or initial-with-preserved-alpha
|
|
546
|
+
// otherwise, this is needed to preserve semi-transparent
|
|
547
|
+
// backgrounds.
|
|
548
|
+
let alpha = alpha_channel(color, context);
|
|
549
|
+
if alpha == 0.0 {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
let mut color = context.builder.device.default_background_color();
|
|
553
|
+
color.alpha = alpha;
|
|
554
|
+
declarations_to_apply_unless_overridden
|
|
555
|
+
.push(PropertyDeclaration::BackgroundColor(color.into()))
|
|
556
|
+
},
|
|
557
|
+
PropertyDeclaration::Color(ref color) => {
|
|
558
|
+
// We honor color: transparent and system colors.
|
|
559
|
+
if color
|
|
560
|
+
.0
|
|
561
|
+
.honored_in_forced_colors_mode(context, /* allow_transparent = */ true)
|
|
562
|
+
{
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
// If the inherited color would be transparent, but we would
|
|
566
|
+
// override this with a non-transparent color, then override it with
|
|
567
|
+
// the default color. Otherwise just let it inherit through.
|
|
568
|
+
if context
|
|
569
|
+
.builder
|
|
570
|
+
.get_parent_inherited_text()
|
|
571
|
+
.clone_color()
|
|
572
|
+
.alpha
|
|
573
|
+
== 0.0
|
|
574
|
+
{
|
|
575
|
+
let color = context.builder.device.default_color();
|
|
576
|
+
declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(
|
|
577
|
+
specified::ColorPropertyValue(color.into()),
|
|
578
|
+
))
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
// We honor url background-images if backplating.
|
|
582
|
+
#[cfg(feature = "gecko")]
|
|
583
|
+
PropertyDeclaration::BackgroundImage(ref bkg) => {
|
|
584
|
+
use crate::values::generics::image::Image;
|
|
585
|
+
if crate::pref!("browser.display.permit_backplate")
|
|
586
|
+
&& bkg
|
|
587
|
+
.0
|
|
588
|
+
.iter()
|
|
589
|
+
.all(|image| matches!(*image, Image::Url(..) | Image::None))
|
|
590
|
+
{
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
_ => {
|
|
595
|
+
// We honor system colors more generally for all colors.
|
|
596
|
+
//
|
|
597
|
+
// We used to honor transparent but that causes accessibility
|
|
598
|
+
// regressions like bug 1740924.
|
|
599
|
+
//
|
|
600
|
+
// NOTE(emilio): This doesn't handle caret-color and accent-color
|
|
601
|
+
// because those use a slightly different syntax (<color> | auto for
|
|
602
|
+
// example).
|
|
603
|
+
//
|
|
604
|
+
// That's probably fine though, as using a system color for
|
|
605
|
+
// caret-color doesn't make sense (using currentColor is fine), and
|
|
606
|
+
// we ignore accent-color in high-contrast-mode anyways.
|
|
607
|
+
if let Some(color) = declaration.color_value() {
|
|
608
|
+
if color
|
|
609
|
+
.honored_in_forced_colors_mode(context, /* allow_transparent = */ false)
|
|
610
|
+
{
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
*declaration.to_mut() =
|
|
618
|
+
PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/// We track the index only for prioritary properties. For other properties we can just iterate.
|
|
622
|
+
type DeclarationIndex = u16;
|
|
623
|
+
|
|
624
|
+
/// "Prioritary" properties are properties that other properties depend on in one way or another.
|
|
625
|
+
///
|
|
626
|
+
/// We keep track of their position in the declaration vector, in order to be able to cascade them
|
|
627
|
+
/// separately in precise order.
|
|
628
|
+
#[derive(Copy, Clone)]
|
|
629
|
+
struct PrioritaryDeclarationPosition {
|
|
630
|
+
// DeclarationIndex::MAX signals no index.
|
|
631
|
+
most_important: DeclarationIndex,
|
|
632
|
+
least_important: DeclarationIndex,
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
impl Default for PrioritaryDeclarationPosition {
|
|
636
|
+
fn default() -> Self {
|
|
637
|
+
Self {
|
|
638
|
+
most_important: DeclarationIndex::MAX,
|
|
639
|
+
least_important: DeclarationIndex::MAX,
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
#[derive(Copy, Clone)]
|
|
645
|
+
struct Declaration<'a> {
|
|
646
|
+
decl: &'a PropertyDeclaration,
|
|
647
|
+
priority: CascadePriority,
|
|
648
|
+
next_index: DeclarationIndex,
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/// The set of property declarations from our rules.
|
|
652
|
+
#[derive(Default)]
|
|
653
|
+
pub(crate) struct Declarations<'a> {
|
|
654
|
+
/// Whether we have any prioritary property. This is just a minor optimization.
|
|
655
|
+
has_prioritary_properties: bool,
|
|
656
|
+
/// A list of all the applicable longhand declarations.
|
|
657
|
+
longhand_declarations: SmallVec<[Declaration<'a>; 64]>,
|
|
658
|
+
/// The prioritary property position data.
|
|
659
|
+
prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
impl<'a> Declarations<'a> {
|
|
663
|
+
fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
|
|
664
|
+
let new_index = self.longhand_declarations.len();
|
|
665
|
+
if new_index >= DeclarationIndex::MAX as usize {
|
|
666
|
+
// This prioritary property is past the amount of declarations we can track. Let's give
|
|
667
|
+
// up applying it to prevent getting confused.
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
self.has_prioritary_properties = true;
|
|
672
|
+
let new_index = new_index as DeclarationIndex;
|
|
673
|
+
let position = &mut self.prioritary_positions[id as usize];
|
|
674
|
+
if position.most_important == DeclarationIndex::MAX {
|
|
675
|
+
// We still haven't seen this property, record the current position as the most
|
|
676
|
+
// prioritary index.
|
|
677
|
+
position.most_important = new_index;
|
|
678
|
+
} else {
|
|
679
|
+
// Let the previous item in the list know about us.
|
|
680
|
+
self.longhand_declarations[position.least_important as usize].next_index = new_index;
|
|
681
|
+
}
|
|
682
|
+
position.least_important = new_index;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
fn note_declaration(
|
|
686
|
+
&mut self,
|
|
687
|
+
decl: &'a PropertyDeclaration,
|
|
688
|
+
priority: CascadePriority,
|
|
689
|
+
id: LonghandId,
|
|
690
|
+
) {
|
|
691
|
+
if let Some(id) = PrioritaryPropertyId::from_longhand(id) {
|
|
692
|
+
self.note_prioritary_property(id);
|
|
693
|
+
}
|
|
694
|
+
self.longhand_declarations.push(Declaration {
|
|
695
|
+
decl,
|
|
696
|
+
priority,
|
|
697
|
+
next_index: 0,
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
#[derive(Default)]
|
|
703
|
+
struct RevertedSet {
|
|
704
|
+
// Just to avoid the hashmap lookup in the common case.
|
|
705
|
+
longhands_set: LonghandIdSet,
|
|
706
|
+
longhands: FxHashMap<LonghandId, (CascadePriority, RevertKind)>,
|
|
707
|
+
custom: PrecomputedHashMap<Name, (CascadePriority, RevertKind)>,
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
#[derive(Default)]
|
|
711
|
+
struct SeenSubstitutionFunctions<'a> {
|
|
712
|
+
/// The boolean means whether the value may have references. If false, we don't need to bother
|
|
713
|
+
/// performing lookups for cycle detection.
|
|
714
|
+
var: PrecomputedHashMap<&'a Name, bool>,
|
|
715
|
+
attr: PrecomputedHashSet<&'a Name>,
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
#[derive(Default)]
|
|
719
|
+
struct SeenSet<'a> {
|
|
720
|
+
longhands: LonghandIdSet,
|
|
721
|
+
custom: SeenSubstitutionFunctions<'a>,
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/// Only registered (typed) properties emit a non-custom edge, from their own value.
|
|
725
|
+
fn find_non_custom_references(
|
|
726
|
+
registration: &PropertyDescriptors,
|
|
727
|
+
value: &VariableValue,
|
|
728
|
+
is_root_element: bool,
|
|
729
|
+
) -> ReferenceFlags {
|
|
730
|
+
use crate::properties_and_values::syntax::data_type::DependentDataTypes;
|
|
731
|
+
|
|
732
|
+
let mut result = ReferenceFlags::empty();
|
|
733
|
+
let Some(syntax) = registration.syntax.as_ref() else {
|
|
734
|
+
return result;
|
|
735
|
+
};
|
|
736
|
+
let dependent_types = syntax.dependent_types();
|
|
737
|
+
let may_reference_length = dependent_types.intersects(DependentDataTypes::LENGTH);
|
|
738
|
+
if may_reference_length {
|
|
739
|
+
result |= value.references.non_custom_references(is_root_element);
|
|
740
|
+
}
|
|
741
|
+
if dependent_types.intersects(DependentDataTypes::COLOR) {
|
|
742
|
+
// The value depends on the used color-scheme (e.g. a `<color>` property referencing
|
|
743
|
+
// `light-dark()` or a system color).
|
|
744
|
+
result |= ReferenceFlags::COLOR_SCHEME;
|
|
745
|
+
}
|
|
746
|
+
result
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/// Resolves the custom properties for a single `@keyframes` keyframe, layered on top of the base
|
|
750
|
+
/// style's already-computed custom properties.
|
|
751
|
+
///
|
|
752
|
+
/// `@keyframes` aren't part of the regular cascade (see bug 1883255), so this lets the caller seed
|
|
753
|
+
/// the substitution map with the element's computed custom properties, cascade the keyframe's
|
|
754
|
+
/// custom declarations on top, and resolve them (running cycle detection and substitution) into
|
|
755
|
+
/// `context.builder.substitution_functions`, which the subsequent animation-value computation reads
|
|
756
|
+
/// to substitute `var()` references.
|
|
757
|
+
pub struct KeyframeCustomPropertiesBuilder<'a> {
|
|
758
|
+
cascade: Cascade<'a>,
|
|
759
|
+
decls: Declarations<'a>,
|
|
760
|
+
shorthand_cache: ShorthandsWithPropertyReferencesCache,
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
impl<'a> KeyframeCustomPropertiesBuilder<'a> {
|
|
764
|
+
/// Creates a new builder, seeding the substitution map with `base` (typically the element's
|
|
765
|
+
/// computed custom properties).
|
|
766
|
+
pub fn new(
|
|
767
|
+
stylist: &'a Stylist,
|
|
768
|
+
context: &mut computed::Context,
|
|
769
|
+
base: ComputedCustomProperties,
|
|
770
|
+
) -> Self {
|
|
771
|
+
context.builder.substitution_functions =
|
|
772
|
+
ComputedSubstitutionFunctions::new(Some(base), None);
|
|
773
|
+
Self {
|
|
774
|
+
cascade: Cascade::new_for_custom_properties_only(stylist),
|
|
775
|
+
decls: Declarations::default(),
|
|
776
|
+
shorthand_cache: ShorthandsWithPropertyReferencesCache::default(),
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/// Cascades a single custom-property declaration from the keyframe.
|
|
781
|
+
pub fn cascade(
|
|
782
|
+
&mut self,
|
|
783
|
+
context: &mut computed::Context,
|
|
784
|
+
declaration: &'a CustomDeclaration,
|
|
785
|
+
priority: CascadePriority,
|
|
786
|
+
) {
|
|
787
|
+
self.cascade
|
|
788
|
+
.cascade_custom_property(context, declaration, priority);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/// Resolves the cascaded custom properties into `context.builder.substitution_functions`.
|
|
792
|
+
pub fn build(
|
|
793
|
+
mut self,
|
|
794
|
+
context: &mut computed::Context,
|
|
795
|
+
attribute_tracker: &mut AttributeTracker,
|
|
796
|
+
) {
|
|
797
|
+
self.cascade.apply_custom_and_prioritary_properties(
|
|
798
|
+
context,
|
|
799
|
+
&self.decls,
|
|
800
|
+
&mut self.shorthand_cache,
|
|
801
|
+
attribute_tracker,
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
pub(crate) struct Cascade<'a> {
|
|
807
|
+
first_line_reparenting: FirstLineReparenting<'a>,
|
|
808
|
+
stylist: &'a Stylist,
|
|
809
|
+
ignore_colors: bool,
|
|
810
|
+
seen: SeenSet<'a>,
|
|
811
|
+
reverted: RevertedSet,
|
|
812
|
+
author_specified: LonghandIdSet,
|
|
813
|
+
declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,
|
|
814
|
+
may_have_custom_property_cycles: bool,
|
|
815
|
+
references_from_non_custom_properties: NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
|
|
816
|
+
/// Set of prioritary properties that have already been applied.
|
|
817
|
+
ensured_prioritary: PrioritaryPropertyIdSet,
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
impl<'a> Cascade<'a> {
|
|
821
|
+
fn new(
|
|
822
|
+
first_line_reparenting: FirstLineReparenting<'a>,
|
|
823
|
+
stylist: &'a Stylist,
|
|
824
|
+
ignore_colors: bool,
|
|
825
|
+
) -> Self {
|
|
826
|
+
Self {
|
|
827
|
+
first_line_reparenting,
|
|
828
|
+
stylist,
|
|
829
|
+
ignore_colors,
|
|
830
|
+
seen: Default::default(),
|
|
831
|
+
author_specified: Default::default(),
|
|
832
|
+
reverted: Default::default(),
|
|
833
|
+
declarations_to_apply_unless_overridden: Default::default(),
|
|
834
|
+
may_have_custom_property_cycles: false,
|
|
835
|
+
ensured_prioritary: PrioritaryPropertyIdSet::default(),
|
|
836
|
+
references_from_non_custom_properties: Default::default(),
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/// Creates a `Cascade` for resolving custom properties outside of a full cascade (e.g. for
|
|
841
|
+
/// keyframes). The visited-style and position-try paths aren't reachable in this mode.
|
|
842
|
+
fn new_for_custom_properties_only(stylist: &'a Stylist) -> Self {
|
|
843
|
+
Self {
|
|
844
|
+
first_line_reparenting: FirstLineReparenting::No,
|
|
845
|
+
stylist,
|
|
846
|
+
ignore_colors: false,
|
|
847
|
+
seen: Default::default(),
|
|
848
|
+
author_specified: Default::default(),
|
|
849
|
+
reverted: Default::default(),
|
|
850
|
+
declarations_to_apply_unless_overridden: Default::default(),
|
|
851
|
+
may_have_custom_property_cycles: false,
|
|
852
|
+
ensured_prioritary: PrioritaryPropertyIdSet::default(),
|
|
853
|
+
references_from_non_custom_properties: Default::default(),
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
fn substitute_variables_if_needed<'cache, 'decl>(
|
|
858
|
+
&self,
|
|
859
|
+
context: &mut computed::Context,
|
|
860
|
+
shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
|
|
861
|
+
declaration: &'decl PropertyDeclaration,
|
|
862
|
+
attribute_tracker: &mut AttributeTracker,
|
|
863
|
+
) -> Cow<'decl, PropertyDeclaration>
|
|
864
|
+
where
|
|
865
|
+
'cache: 'decl,
|
|
866
|
+
{
|
|
867
|
+
let declaration = match *declaration {
|
|
868
|
+
PropertyDeclaration::WithVariables(ref declaration) => declaration,
|
|
869
|
+
ref d => return Cow::Borrowed(d),
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
if !declaration.id.inherited() {
|
|
873
|
+
context.rule_cache_conditions.borrow_mut().set_uncacheable();
|
|
874
|
+
|
|
875
|
+
// NOTE(emilio): We only really need to add the `display` /
|
|
876
|
+
// `content` flag if the CSS variable has not been specified on our
|
|
877
|
+
// declarations, but we don't have that information at this point,
|
|
878
|
+
// and it doesn't seem like an important enough optimization to
|
|
879
|
+
// warrant it.
|
|
880
|
+
if matches!(declaration.id, LonghandId::Display | LonghandId::Content) {
|
|
881
|
+
context
|
|
882
|
+
.builder
|
|
883
|
+
.add_flags(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
debug_assert!(
|
|
888
|
+
context.builder.stylist.is_some(),
|
|
889
|
+
"Need a Stylist to substitute variables!"
|
|
890
|
+
);
|
|
891
|
+
declaration.value.substitute_variables(
|
|
892
|
+
declaration.id,
|
|
893
|
+
context.builder.substitution_functions(),
|
|
894
|
+
context.builder.stylist.unwrap(),
|
|
895
|
+
context,
|
|
896
|
+
shorthand_cache,
|
|
897
|
+
attribute_tracker,
|
|
898
|
+
)
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
fn apply_one_prioritary_property(
|
|
902
|
+
&mut self,
|
|
903
|
+
context: &mut computed::Context,
|
|
904
|
+
decls: &Declarations,
|
|
905
|
+
cache: &mut ShorthandsWithPropertyReferencesCache,
|
|
906
|
+
id: PrioritaryPropertyId,
|
|
907
|
+
attr_provider: &mut AttributeTracker,
|
|
908
|
+
) {
|
|
909
|
+
let mut index = decls.prioritary_positions[id as usize].most_important;
|
|
910
|
+
if index == DeclarationIndex::MAX {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
let longhand_id = id.to_longhand();
|
|
915
|
+
debug_assert!(
|
|
916
|
+
!longhand_id.is_logical(),
|
|
917
|
+
"That could require more book-keeping"
|
|
918
|
+
);
|
|
919
|
+
loop {
|
|
920
|
+
let decl = decls.longhand_declarations[index as usize];
|
|
921
|
+
self.apply_one_longhand(
|
|
922
|
+
context,
|
|
923
|
+
longhand_id,
|
|
924
|
+
decl.decl,
|
|
925
|
+
decl.priority,
|
|
926
|
+
cache,
|
|
927
|
+
attr_provider,
|
|
928
|
+
);
|
|
929
|
+
if self.seen.longhands.contains(longhand_id) {
|
|
930
|
+
// Found it!
|
|
931
|
+
self.did_apply_prioritary_property(context, id);
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
debug_assert!(
|
|
935
|
+
decl.next_index == 0 || decl.next_index > index,
|
|
936
|
+
"should make progress! {} -> {}",
|
|
937
|
+
index,
|
|
938
|
+
decl.next_index,
|
|
939
|
+
);
|
|
940
|
+
index = decl.next_index;
|
|
941
|
+
if index == 0 {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/// Some prioritary properties need book-keeping, which this takes care of.
|
|
948
|
+
fn did_apply_prioritary_property(
|
|
949
|
+
&mut self,
|
|
950
|
+
context: &mut computed::Context,
|
|
951
|
+
id: PrioritaryPropertyId,
|
|
952
|
+
) {
|
|
953
|
+
use crate::properties::PrioritaryPropertyId::*;
|
|
954
|
+
match id {
|
|
955
|
+
Appearance => {
|
|
956
|
+
if is_base_appearance(context) {
|
|
957
|
+
context
|
|
958
|
+
.style()
|
|
959
|
+
.add_flags(ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE);
|
|
960
|
+
context
|
|
961
|
+
.included_cascade_flags
|
|
962
|
+
.insert(RuleCascadeFlags::APPEARANCE_BASE);
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
WritingMode | Direction | TextOrientation => {
|
|
966
|
+
context.builder.writing_mode =
|
|
967
|
+
crate::logical_geometry::WritingMode::new(context.builder.get_inherited_box());
|
|
968
|
+
},
|
|
969
|
+
Zoom => {
|
|
970
|
+
context.builder.recompute_effective_zooms();
|
|
971
|
+
if !context.builder.effective_zoom_for_inheritance.is_one() {
|
|
972
|
+
// NOTE(emilio): This is a bit of a hack, but matches the shipped WebKit and Blink
|
|
973
|
+
// behavior for now. Ideally, in the future, we have a pass over all
|
|
974
|
+
// implicitly-or-explicitly-inherited properties that can contain lengths and
|
|
975
|
+
// re-compute them properly, see https://github.com/w3c/csswg-drafts/issues/9397.
|
|
976
|
+
// TODO(emilio): we need to eagerly do this for line-height as well, probably.
|
|
977
|
+
self.recompute_font_size_for_zoom_change(&mut context.builder);
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
XLang => {
|
|
981
|
+
#[cfg(feature = "gecko")]
|
|
982
|
+
self.recompute_initial_font_family_if_needed(&mut context.builder);
|
|
983
|
+
self.recompute_keyword_font_size_if_needed(context);
|
|
984
|
+
},
|
|
985
|
+
FontFamily => {
|
|
986
|
+
#[cfg(feature = "gecko")]
|
|
987
|
+
self.prioritize_user_fonts_if_needed(&mut context.builder);
|
|
988
|
+
self.recompute_keyword_font_size_if_needed(context);
|
|
989
|
+
},
|
|
990
|
+
FontSize => {
|
|
991
|
+
if self.seen.longhands.contains(LonghandId::MathDepth) {
|
|
992
|
+
#[cfg(feature = "gecko")]
|
|
993
|
+
Self::recompute_math_font_size_if_needed(context);
|
|
994
|
+
}
|
|
995
|
+
if self.seen.longhands.contains(LonghandId::XLang)
|
|
996
|
+
|| self.seen.longhands.contains(LonghandId::FontFamily)
|
|
997
|
+
{
|
|
998
|
+
self.recompute_keyword_font_size_if_needed(context);
|
|
999
|
+
}
|
|
1000
|
+
#[cfg(feature = "gecko")]
|
|
1001
|
+
self.constrain_font_size_if_needed(&mut context.builder);
|
|
1002
|
+
},
|
|
1003
|
+
XTextScale => {
|
|
1004
|
+
#[cfg(feature = "gecko")]
|
|
1005
|
+
self.unzoom_fonts_if_needed(&mut context.builder);
|
|
1006
|
+
},
|
|
1007
|
+
MozMinFontSizeRatio => {
|
|
1008
|
+
#[cfg(feature = "gecko")]
|
|
1009
|
+
self.constrain_font_size_if_needed(&mut context.builder);
|
|
1010
|
+
},
|
|
1011
|
+
ColorScheme => {
|
|
1012
|
+
context.builder.color_scheme =
|
|
1013
|
+
context.builder.get_inherited_ui().color_scheme_bits();
|
|
1014
|
+
},
|
|
1015
|
+
MozDefaultAppearance | MathDepth | FontWeight | FontWidth | FontStyle
|
|
1016
|
+
| FontSizeAdjust | ForcedColorAdjust | LineHeight => {},
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
fn apply_non_prioritary_properties(
|
|
1021
|
+
&mut self,
|
|
1022
|
+
context: &mut computed::Context,
|
|
1023
|
+
longhand_declarations: &[Declaration],
|
|
1024
|
+
shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
|
|
1025
|
+
properties_to_apply: &LonghandIdSet,
|
|
1026
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1027
|
+
) {
|
|
1028
|
+
debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
|
|
1029
|
+
debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
|
|
1030
|
+
for declaration in longhand_declarations {
|
|
1031
|
+
let mut longhand_id = declaration.decl.id().as_longhand().unwrap();
|
|
1032
|
+
if !properties_to_apply.contains(longhand_id) {
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());
|
|
1036
|
+
let is_logical = longhand_id.is_logical();
|
|
1037
|
+
if is_logical {
|
|
1038
|
+
let wm = context.builder.writing_mode;
|
|
1039
|
+
context
|
|
1040
|
+
.rule_cache_conditions
|
|
1041
|
+
.borrow_mut()
|
|
1042
|
+
.set_writing_mode_dependency(wm);
|
|
1043
|
+
longhand_id = longhand_id.to_physical(wm);
|
|
1044
|
+
}
|
|
1045
|
+
self.apply_one_longhand(
|
|
1046
|
+
context,
|
|
1047
|
+
longhand_id,
|
|
1048
|
+
declaration.decl,
|
|
1049
|
+
declaration.priority,
|
|
1050
|
+
shorthand_cache,
|
|
1051
|
+
attribute_tracker,
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
if !self.declarations_to_apply_unless_overridden.is_empty() {
|
|
1055
|
+
debug_assert!(self.ignore_colors);
|
|
1056
|
+
for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {
|
|
1057
|
+
let longhand_id = declaration.id().as_longhand().unwrap();
|
|
1058
|
+
debug_assert!(!longhand_id.is_logical());
|
|
1059
|
+
if !self.seen.longhands.contains(longhand_id) {
|
|
1060
|
+
unsafe {
|
|
1061
|
+
self.do_apply_declaration(context, longhand_id, &declaration);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
if !context.builder.effective_zoom_for_inheritance.is_one() {
|
|
1068
|
+
self.recompute_zoom_dependent_inherited_lengths(context);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
#[cold]
|
|
1073
|
+
fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {
|
|
1074
|
+
debug_assert!(self.seen.longhands.contains(LonghandId::Zoom));
|
|
1075
|
+
for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {
|
|
1076
|
+
if self.seen.longhands.contains(prop) {
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);
|
|
1080
|
+
unsafe {
|
|
1081
|
+
self.do_apply_declaration(context, prop, &declaration);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
fn apply_one_longhand(
|
|
1087
|
+
&mut self,
|
|
1088
|
+
context: &mut computed::Context,
|
|
1089
|
+
longhand_id: LonghandId,
|
|
1090
|
+
declaration: &PropertyDeclaration,
|
|
1091
|
+
priority: CascadePriority,
|
|
1092
|
+
cache: &mut ShorthandsWithPropertyReferencesCache,
|
|
1093
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1094
|
+
) {
|
|
1095
|
+
debug_assert!(!longhand_id.is_logical());
|
|
1096
|
+
if self.seen.longhands.contains(longhand_id) {
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
if !(priority.flags() - context.included_cascade_flags).is_empty() {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
if self.reverted.longhands_set.contains(longhand_id) {
|
|
1105
|
+
if let Some(&(reverted_priority, revert_kind)) =
|
|
1106
|
+
self.reverted.longhands.get(&longhand_id)
|
|
1107
|
+
{
|
|
1108
|
+
if !reverted_priority.allows_when_reverted(&priority, revert_kind) {
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
let mut declaration =
|
|
1115
|
+
self.substitute_variables_if_needed(context, cache, declaration, attribute_tracker);
|
|
1116
|
+
|
|
1117
|
+
// When document colors are disabled, do special handling of
|
|
1118
|
+
// properties that are marked as ignored in that mode.
|
|
1119
|
+
let origin = priority.cascade_level().origin();
|
|
1120
|
+
if self.ignore_colors {
|
|
1121
|
+
tweak_when_ignoring_colors(
|
|
1122
|
+
context,
|
|
1123
|
+
longhand_id,
|
|
1124
|
+
origin,
|
|
1125
|
+
&mut declaration,
|
|
1126
|
+
&mut self.declarations_to_apply_unless_overridden,
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
let can_skip_apply = match declaration.get_css_wide_keyword() {
|
|
1130
|
+
Some(keyword) => {
|
|
1131
|
+
if let Some(revert_kind) = keyword.revert_kind() {
|
|
1132
|
+
// We intentionally don't want to insert it into
|
|
1133
|
+
// `self.seen.longhands`, `reverted` takes care of rejecting
|
|
1134
|
+
// other declarations as needed.
|
|
1135
|
+
self.reverted.longhands_set.insert(longhand_id);
|
|
1136
|
+
self.reverted
|
|
1137
|
+
.longhands
|
|
1138
|
+
.insert(longhand_id, (priority, revert_kind));
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
let inherited = longhand_id.inherited();
|
|
1143
|
+
let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()
|
|
1144
|
+
&& longhand_id.zoom_dependent();
|
|
1145
|
+
match keyword {
|
|
1146
|
+
CSSWideKeyword::Revert
|
|
1147
|
+
| CSSWideKeyword::RevertLayer
|
|
1148
|
+
| CSSWideKeyword::RevertRule => unreachable!(),
|
|
1149
|
+
CSSWideKeyword::Unset => !zoomed || !inherited,
|
|
1150
|
+
CSSWideKeyword::Inherit => inherited && !zoomed,
|
|
1151
|
+
CSSWideKeyword::Initial => !inherited,
|
|
1152
|
+
}
|
|
1153
|
+
},
|
|
1154
|
+
None => false,
|
|
1155
|
+
};
|
|
1156
|
+
|
|
1157
|
+
self.seen.longhands.insert(longhand_id);
|
|
1158
|
+
if origin.is_author_origin() {
|
|
1159
|
+
self.author_specified.insert(longhand_id);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
if !can_skip_apply {
|
|
1163
|
+
// Set context.scope to this declaration's cascade level so that
|
|
1164
|
+
// tree-scoped properties (anchor-name, position-anchor, anchor-scope)
|
|
1165
|
+
// get the correct scope when converted to computed values.
|
|
1166
|
+
let old_scope = context.scope;
|
|
1167
|
+
let cascade_level = priority.cascade_level();
|
|
1168
|
+
context.scope = cascade_level;
|
|
1169
|
+
unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }
|
|
1170
|
+
context.scope = old_scope;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
#[inline]
|
|
1175
|
+
unsafe fn do_apply_declaration(
|
|
1176
|
+
&self,
|
|
1177
|
+
context: &mut computed::Context,
|
|
1178
|
+
longhand_id: LonghandId,
|
|
1179
|
+
declaration: &PropertyDeclaration,
|
|
1180
|
+
) {
|
|
1181
|
+
debug_assert!(!longhand_id.is_logical());
|
|
1182
|
+
unsafe {
|
|
1183
|
+
// We could (and used to) use a pattern match here, but that bloats this
|
|
1184
|
+
// function to over 100K of compiled code!
|
|
1185
|
+
//
|
|
1186
|
+
// To improve i-cache behavior, we outline the individual functions and
|
|
1187
|
+
// use virtual dispatch instead.
|
|
1188
|
+
(CASCADE_PROPERTY[longhand_id as usize])(declaration, context);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
fn compute_visited_style_if_needed<E>(
|
|
1193
|
+
&self,
|
|
1194
|
+
context: &mut computed::Context,
|
|
1195
|
+
element: Option<E>,
|
|
1196
|
+
parent_style: Option<&ComputedValues>,
|
|
1197
|
+
layout_parent_style: Option<&ComputedValues>,
|
|
1198
|
+
try_tactic: &PositionTryFallbacksTryTactic,
|
|
1199
|
+
visited_rules: &StrongRuleNode,
|
|
1200
|
+
guards: &StylesheetGuards,
|
|
1201
|
+
) where
|
|
1202
|
+
E: TElement,
|
|
1203
|
+
{
|
|
1204
|
+
let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();
|
|
1205
|
+
|
|
1206
|
+
macro_rules! visited_parent {
|
|
1207
|
+
($parent:expr) => {
|
|
1208
|
+
if is_link {
|
|
1209
|
+
$parent
|
|
1210
|
+
} else {
|
|
1211
|
+
$parent.map(|p| p.visited_style().unwrap_or(p))
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// We could call apply_declarations directly, but that'd cause
|
|
1217
|
+
// another instantiation of this function which is not great.
|
|
1218
|
+
let style = cascade_rules(
|
|
1219
|
+
context.builder.stylist.unwrap(),
|
|
1220
|
+
context.builder.pseudo,
|
|
1221
|
+
visited_rules,
|
|
1222
|
+
guards,
|
|
1223
|
+
visited_parent!(parent_style),
|
|
1224
|
+
visited_parent!(layout_parent_style),
|
|
1225
|
+
self.first_line_reparenting,
|
|
1226
|
+
try_tactic,
|
|
1227
|
+
CascadeMode::Visited {
|
|
1228
|
+
unvisited_context: &*context,
|
|
1229
|
+
unvisited_properties: &self.seen.longhands,
|
|
1230
|
+
},
|
|
1231
|
+
// Cascade input flags don't matter for the visited style, they are
|
|
1232
|
+
// in the main (unvisited) style.
|
|
1233
|
+
Default::default(),
|
|
1234
|
+
context.included_cascade_flags,
|
|
1235
|
+
// The rule cache doesn't care about caching :visited
|
|
1236
|
+
// styles, we cache the unvisited style instead. We still do
|
|
1237
|
+
// need to set the caching dependencies properly if present
|
|
1238
|
+
// though, so the cache conditions need to match.
|
|
1239
|
+
None, // rule_cache
|
|
1240
|
+
&mut *context.rule_cache_conditions.borrow_mut(),
|
|
1241
|
+
element,
|
|
1242
|
+
&mut *context.tree_counting_caches.borrow_mut(),
|
|
1243
|
+
);
|
|
1244
|
+
context.builder.visited_style = Some(style);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
fn finished_applying_properties(&self, builder: &mut StyleBuilder) {
|
|
1248
|
+
#[cfg(feature = "gecko")]
|
|
1249
|
+
{
|
|
1250
|
+
if let Some(bg) = builder.get_background_if_mutated() {
|
|
1251
|
+
bg.fill_arrays();
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
if let Some(svg) = builder.get_svg_if_mutated() {
|
|
1255
|
+
svg.fill_arrays();
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
if self
|
|
1260
|
+
.author_specified
|
|
1261
|
+
.contains_any(LonghandIdSet::border_background_properties())
|
|
1262
|
+
{
|
|
1263
|
+
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
if self.author_specified.contains(LonghandId::Color) {
|
|
1267
|
+
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
if self.author_specified.contains(LonghandId::TextShadow) {
|
|
1271
|
+
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_SHADOW);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
if self.author_specified.contains(LonghandId::GridAutoFlow) {
|
|
1275
|
+
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW);
|
|
1276
|
+
}
|
|
1277
|
+
#[cfg(feature = "servo")]
|
|
1278
|
+
{
|
|
1279
|
+
if let Some(font) = builder.get_font_if_mutated() {
|
|
1280
|
+
font.compute_font_hash();
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
fn try_to_use_cached_reset_properties(
|
|
1286
|
+
&self,
|
|
1287
|
+
context: &mut computed::Context<'a>,
|
|
1288
|
+
cache: Option<&'a RuleCache>,
|
|
1289
|
+
guards: &StylesheetGuards,
|
|
1290
|
+
) -> bool {
|
|
1291
|
+
let style = match self.first_line_reparenting {
|
|
1292
|
+
FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,
|
|
1293
|
+
FirstLineReparenting::No => {
|
|
1294
|
+
let Some(cache) = cache else { return false };
|
|
1295
|
+
let Some(style) = cache.find(guards, context) else {
|
|
1296
|
+
return false;
|
|
1297
|
+
};
|
|
1298
|
+
style
|
|
1299
|
+
},
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
context.builder.copy_reset_from(style);
|
|
1303
|
+
|
|
1304
|
+
// We're using the same reset style as another element, and we'll skip
|
|
1305
|
+
// applying the relevant properties. So we need to do the relevant
|
|
1306
|
+
// bookkeeping here to keep these bits correct.
|
|
1307
|
+
//
|
|
1308
|
+
// Note that the border/background properties are non-inherited, so we
|
|
1309
|
+
// don't need to do anything else other than just copying the bits over.
|
|
1310
|
+
//
|
|
1311
|
+
// When using this optimization, we also need to copy whether the old
|
|
1312
|
+
// style specified viewport units / used font-relative lengths, this one
|
|
1313
|
+
// would as well. It matches the same rules, so it is the right thing
|
|
1314
|
+
// to do anyways, even if it's only used on inherited properties.
|
|
1315
|
+
let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND
|
|
1316
|
+
| ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW
|
|
1317
|
+
| ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS
|
|
1318
|
+
| ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS
|
|
1319
|
+
| ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE
|
|
1320
|
+
| ComputedValueFlags::USES_CONTAINER_UNITS
|
|
1321
|
+
| ComputedValueFlags::USES_VIEWPORT_UNITS
|
|
1322
|
+
| ComputedValueFlags::USES_FONT_OR_WM_RELATIVE_UNITS
|
|
1323
|
+
| ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY
|
|
1324
|
+
| ComputedValueFlags::USES_SIBLING_COUNT
|
|
1325
|
+
| ComputedValueFlags::USES_SIBLING_INDEX;
|
|
1326
|
+
context.builder.add_flags(style.flags & bits_to_copy);
|
|
1327
|
+
|
|
1328
|
+
true
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/// The initial font depends on the current lang group so we may need to
|
|
1332
|
+
/// recompute it if the language changed.
|
|
1333
|
+
#[inline]
|
|
1334
|
+
#[cfg(feature = "gecko")]
|
|
1335
|
+
fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {
|
|
1336
|
+
use crate::gecko_bindings::bindings;
|
|
1337
|
+
use crate::values::computed::font::FontFamily;
|
|
1338
|
+
|
|
1339
|
+
let default_font_type = {
|
|
1340
|
+
let font = builder.get_font();
|
|
1341
|
+
|
|
1342
|
+
if !font.mFont.family.is_initial {
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
let default_font_type = unsafe {
|
|
1347
|
+
bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
|
|
1348
|
+
builder.device.document(),
|
|
1349
|
+
font.mLanguage.mRawPtr,
|
|
1350
|
+
)
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
let initial_generic = font.mFont.family.families.single_generic();
|
|
1354
|
+
debug_assert!(
|
|
1355
|
+
initial_generic.is_some(),
|
|
1356
|
+
"Initial font should be just one generic font"
|
|
1357
|
+
);
|
|
1358
|
+
if initial_generic == Some(default_font_type) {
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
default_font_type
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
// NOTE: Leaves is_initial untouched.
|
|
1366
|
+
builder.mutate_font().mFont.family.families =
|
|
1367
|
+
FontFamily::generic(default_font_type).families.clone();
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/// Prioritize user fonts if needed by pref.
|
|
1371
|
+
#[inline]
|
|
1372
|
+
#[cfg(feature = "gecko")]
|
|
1373
|
+
fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {
|
|
1374
|
+
use crate::gecko_bindings::bindings;
|
|
1375
|
+
|
|
1376
|
+
// Check the use_document_fonts setting for content, but for chrome
|
|
1377
|
+
// documents they're treated as always enabled.
|
|
1378
|
+
if crate::pref!("browser.display.use_document_fonts") != 0
|
|
1379
|
+
|| builder.device.chrome_rules_enabled_for_document()
|
|
1380
|
+
{
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
let default_font_type = {
|
|
1385
|
+
let font = builder.get_font();
|
|
1386
|
+
|
|
1387
|
+
if font.mFont.family.is_system_font {
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
if !font.mFont.family.families.needs_user_font_prioritization() {
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
unsafe {
|
|
1396
|
+
bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
|
|
1397
|
+
builder.device.document(),
|
|
1398
|
+
font.mLanguage.mRawPtr,
|
|
1399
|
+
)
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
let font = builder.mutate_font();
|
|
1404
|
+
font.mFont
|
|
1405
|
+
.family
|
|
1406
|
+
.families
|
|
1407
|
+
.prioritize_first_generic_or_prepend(default_font_type);
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
/// Some keyword sizes depend on the font family and language.
|
|
1411
|
+
fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {
|
|
1412
|
+
use crate::values::computed::ToComputedValue;
|
|
1413
|
+
|
|
1414
|
+
if !self.seen.longhands.contains(LonghandId::XLang)
|
|
1415
|
+
&& !self.seen.longhands.contains(LonghandId::FontFamily)
|
|
1416
|
+
{
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
let new_size = {
|
|
1421
|
+
let font = context.builder.get_font();
|
|
1422
|
+
let info = font.clone_font_size().keyword_info;
|
|
1423
|
+
let new_size = match info.kw {
|
|
1424
|
+
specified::FontSizeKeyword::None => return,
|
|
1425
|
+
_ => {
|
|
1426
|
+
context.for_non_inherited_property = false;
|
|
1427
|
+
specified::FontSize::Keyword(info).to_computed_value(context)
|
|
1428
|
+
},
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
#[cfg(feature = "gecko")]
|
|
1432
|
+
if font.mScriptUnconstrainedSize == new_size.computed_size {
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
new_size
|
|
1437
|
+
};
|
|
1438
|
+
|
|
1439
|
+
context.builder.mutate_font().set_font_size(new_size);
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
/// Some properties, plus setting font-size itself, may make us go out of
|
|
1443
|
+
/// our minimum font-size range.
|
|
1444
|
+
#[cfg(feature = "gecko")]
|
|
1445
|
+
fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {
|
|
1446
|
+
use crate::gecko_bindings::bindings;
|
|
1447
|
+
use crate::values::generics::NonNegative;
|
|
1448
|
+
|
|
1449
|
+
let min_font_size = {
|
|
1450
|
+
let font = builder.get_font();
|
|
1451
|
+
let min_font_size = unsafe {
|
|
1452
|
+
bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
|
|
1453
|
+
};
|
|
1454
|
+
|
|
1455
|
+
if font.mFont.size.0 >= min_font_size {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
NonNegative(min_font_size)
|
|
1460
|
+
};
|
|
1461
|
+
|
|
1462
|
+
builder.mutate_font().mFont.size = min_font_size;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/// <svg:text> is not affected by text zoom, and it uses a preshint to disable it. We fix up
|
|
1466
|
+
/// the struct when this happens by unzooming its contained font values, which will have been
|
|
1467
|
+
/// zoomed in the parent.
|
|
1468
|
+
#[cfg(feature = "gecko")]
|
|
1469
|
+
fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
|
|
1470
|
+
debug_assert!(self.seen.longhands.contains(LonghandId::XTextScale));
|
|
1471
|
+
|
|
1472
|
+
let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
|
|
1473
|
+
let text_scale = builder.get_font().clone__x_text_scale();
|
|
1474
|
+
if parent_text_scale == text_scale {
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
debug_assert_ne!(
|
|
1478
|
+
parent_text_scale.text_zoom_enabled(),
|
|
1479
|
+
text_scale.text_zoom_enabled(),
|
|
1480
|
+
"There's only one value that disables it"
|
|
1481
|
+
);
|
|
1482
|
+
debug_assert!(
|
|
1483
|
+
!text_scale.text_zoom_enabled(),
|
|
1484
|
+
"We only ever disable text zoom never enable it"
|
|
1485
|
+
);
|
|
1486
|
+
let device = builder.device;
|
|
1487
|
+
builder.mutate_font().unzoom_fonts(device);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
|
|
1491
|
+
debug_assert!(self.seen.longhands.contains(LonghandId::Zoom));
|
|
1492
|
+
// NOTE(emilio): Intentionally not using the effective zoom here, since all the inherited
|
|
1493
|
+
// zooms are already applied.
|
|
1494
|
+
let old_size = builder.get_font().clone_font_size();
|
|
1495
|
+
let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);
|
|
1496
|
+
if old_size == new_size {
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
builder.mutate_font().set_font_size(new_size);
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
/// Special handling of font-size: math (used for MathML).
|
|
1503
|
+
/// https://w3c.github.io/mathml-core/#the-math-script-level-property
|
|
1504
|
+
/// TODO: Bug: 1548471: MathML Core also does not specify a script min size
|
|
1505
|
+
/// should we unship that feature or standardize it?
|
|
1506
|
+
#[cfg(feature = "gecko")]
|
|
1507
|
+
fn recompute_math_font_size_if_needed(context: &mut computed::Context) {
|
|
1508
|
+
use crate::values::generics::NonNegative;
|
|
1509
|
+
|
|
1510
|
+
// Do not do anything if font-size: math or math-depth is not set.
|
|
1511
|
+
if context.builder.get_font().clone_font_size().keyword_info.kw
|
|
1512
|
+
!= specified::FontSizeKeyword::Math
|
|
1513
|
+
{
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
|
|
1518
|
+
|
|
1519
|
+
// Helper function that calculates the scale factor applied to font-size
|
|
1520
|
+
// when math-depth goes from parent_math_depth to computed_math_depth.
|
|
1521
|
+
// This function is essentially a modification of the MathML3's formula
|
|
1522
|
+
// 0.71^(parent_math_depth - computed_math_depth) so that a scale factor
|
|
1523
|
+
// of parent_script_percent_scale_down is applied when math-depth goes
|
|
1524
|
+
// from 0 to 1 and parent_script_script_percent_scale_down is applied
|
|
1525
|
+
// when math-depth goes from 0 to 2. This is also a straightforward
|
|
1526
|
+
// implementation of the specification's algorithm:
|
|
1527
|
+
// https://w3c.github.io/mathml-core/#the-math-script-level-property
|
|
1528
|
+
fn scale_factor_for_math_depth_change(
|
|
1529
|
+
parent_math_depth: i32,
|
|
1530
|
+
computed_math_depth: i32,
|
|
1531
|
+
parent_script_percent_scale_down: Option<f32>,
|
|
1532
|
+
parent_script_script_percent_scale_down: Option<f32>,
|
|
1533
|
+
) -> f32 {
|
|
1534
|
+
let mut a = parent_math_depth;
|
|
1535
|
+
let mut b = computed_math_depth;
|
|
1536
|
+
let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
|
|
1537
|
+
let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or(c);
|
|
1538
|
+
let scale_between_0_and_2 = parent_script_script_percent_scale_down.unwrap_or(c * c);
|
|
1539
|
+
let mut s = 1.0;
|
|
1540
|
+
let mut invert_scale_factor = false;
|
|
1541
|
+
if a == b {
|
|
1542
|
+
return s;
|
|
1543
|
+
}
|
|
1544
|
+
if b < a {
|
|
1545
|
+
std::mem::swap(&mut a, &mut b);
|
|
1546
|
+
invert_scale_factor = true;
|
|
1547
|
+
}
|
|
1548
|
+
let mut e = b - a;
|
|
1549
|
+
if a <= 0 && b >= 2 {
|
|
1550
|
+
s *= scale_between_0_and_2;
|
|
1551
|
+
e -= 2;
|
|
1552
|
+
} else if a == 1 {
|
|
1553
|
+
s *= scale_between_0_and_2 / scale_between_0_and_1;
|
|
1554
|
+
e -= 1;
|
|
1555
|
+
} else if b == 1 {
|
|
1556
|
+
s *= scale_between_0_and_1;
|
|
1557
|
+
e -= 1;
|
|
1558
|
+
}
|
|
1559
|
+
s *= c.powi(e);
|
|
1560
|
+
if invert_scale_factor {
|
|
1561
|
+
1.0 / s.max(f32::MIN_POSITIVE)
|
|
1562
|
+
} else {
|
|
1563
|
+
s
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
let (new_size, new_unconstrained_size) = {
|
|
1568
|
+
use crate::values::specified::font::QueryFontMetricsFlags;
|
|
1569
|
+
|
|
1570
|
+
let builder = &context.builder;
|
|
1571
|
+
let font = builder.get_font();
|
|
1572
|
+
let parent_font = builder.get_parent_font();
|
|
1573
|
+
|
|
1574
|
+
let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
|
|
1575
|
+
|
|
1576
|
+
if delta == 0 {
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
let mut min = parent_font.mScriptMinSize;
|
|
1581
|
+
if font.mXTextScale.text_zoom_enabled() {
|
|
1582
|
+
min = builder.device.zoom_text(min);
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// Calculate scale factor following MathML Core's algorithm.
|
|
1586
|
+
let scale = {
|
|
1587
|
+
// Script scale factors are independent of orientation.
|
|
1588
|
+
let font_metrics = context.query_font_metrics(
|
|
1589
|
+
FontBaseSize::InheritedStyle,
|
|
1590
|
+
FontMetricsOrientation::Horizontal,
|
|
1591
|
+
QueryFontMetricsFlags::NEEDS_MATH_SCALES,
|
|
1592
|
+
);
|
|
1593
|
+
scale_factor_for_math_depth_change(
|
|
1594
|
+
parent_font.mMathDepth as i32,
|
|
1595
|
+
font.mMathDepth as i32,
|
|
1596
|
+
font_metrics.script_percent_scale_down,
|
|
1597
|
+
font_metrics.script_script_percent_scale_down,
|
|
1598
|
+
)
|
|
1599
|
+
};
|
|
1600
|
+
|
|
1601
|
+
let parent_size = parent_font.mSize.0;
|
|
1602
|
+
let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
|
|
1603
|
+
let new_size = parent_size.scale_by(scale);
|
|
1604
|
+
let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
|
|
1605
|
+
|
|
1606
|
+
if scale <= 1. {
|
|
1607
|
+
// The parent size can be smaller than scriptminsize, e.g. if it
|
|
1608
|
+
// was specified explicitly. Don't scale in this case, but we
|
|
1609
|
+
// don't want to set it to scriptminsize either since that will
|
|
1610
|
+
// make it larger.
|
|
1611
|
+
if parent_size <= min {
|
|
1612
|
+
(parent_size, new_unconstrained_size)
|
|
1613
|
+
} else {
|
|
1614
|
+
(min.max(new_size), new_unconstrained_size)
|
|
1615
|
+
}
|
|
1616
|
+
} else {
|
|
1617
|
+
// If the new unconstrained size is larger than the min size,
|
|
1618
|
+
// this means we have escaped the grasp of scriptminsize and can
|
|
1619
|
+
// revert to using the unconstrained size.
|
|
1620
|
+
// However, if the new size is even larger (perhaps due to usage
|
|
1621
|
+
// of em units), use that instead.
|
|
1622
|
+
(
|
|
1623
|
+
new_size.min(new_unconstrained_size.max(min)),
|
|
1624
|
+
new_unconstrained_size,
|
|
1625
|
+
)
|
|
1626
|
+
}
|
|
1627
|
+
};
|
|
1628
|
+
let font = context.builder.mutate_font();
|
|
1629
|
+
font.mFont.size = NonNegative(new_size);
|
|
1630
|
+
font.mSize = NonNegative(new_size);
|
|
1631
|
+
font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
/// Seeds `context.builder.substitution_functions` with the inherited custom properties and the
|
|
1635
|
+
/// registered initial values, before custom-property declarations are cascaded into it.
|
|
1636
|
+
fn init_custom_properties(&mut self, context: &mut computed::Context) {
|
|
1637
|
+
let is_root_element = context.is_root_element();
|
|
1638
|
+
let initial_values = self.stylist.get_custom_property_initial_values();
|
|
1639
|
+
let inherited = if is_root_element {
|
|
1640
|
+
debug_assert!(context.inherited_custom_properties().is_empty());
|
|
1641
|
+
initial_values.inherited.clone()
|
|
1642
|
+
} else {
|
|
1643
|
+
context.inherited_custom_properties().inherited.clone()
|
|
1644
|
+
};
|
|
1645
|
+
let properties = ComputedCustomProperties {
|
|
1646
|
+
inherited,
|
|
1647
|
+
non_inherited: initial_values.non_inherited.clone(),
|
|
1648
|
+
};
|
|
1649
|
+
context.builder.substitution_functions =
|
|
1650
|
+
ComputedSubstitutionFunctions::new(Some(properties), None);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
/// Resolves the custom properties and applies the prioritary properties in a single
|
|
1654
|
+
/// cycle-tracked walk: as a custom property depending on `em`/`lh`/the used color-scheme
|
|
1655
|
+
/// becomes resolvable, `substitute_all` applies the prioritary property it needs (via
|
|
1656
|
+
/// `ensure_prioritary_property`) so it computes against the right value. Any prioritary
|
|
1657
|
+
/// property not triggered that way is applied at the end.
|
|
1658
|
+
fn apply_custom_and_prioritary_properties(
|
|
1659
|
+
&mut self,
|
|
1660
|
+
context: &mut computed::Context,
|
|
1661
|
+
decls: &Declarations,
|
|
1662
|
+
shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
|
|
1663
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1664
|
+
) {
|
|
1665
|
+
if self.may_have_custom_property_cycles {
|
|
1666
|
+
let stylist = self.stylist;
|
|
1667
|
+
let seen = std::mem::take(&mut self.seen.custom);
|
|
1668
|
+
let references = std::mem::take(&mut self.references_from_non_custom_properties);
|
|
1669
|
+
substitute_all(
|
|
1670
|
+
&seen,
|
|
1671
|
+
&references,
|
|
1672
|
+
stylist,
|
|
1673
|
+
context,
|
|
1674
|
+
self,
|
|
1675
|
+
decls,
|
|
1676
|
+
shorthand_cache,
|
|
1677
|
+
attribute_tracker,
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
// Apply any prioritary property that wasn't applied while resolving custom properties.
|
|
1681
|
+
if decls.has_prioritary_properties {
|
|
1682
|
+
for id in PrioritaryPropertyId::each() {
|
|
1683
|
+
self.ensure_prioritary_property(
|
|
1684
|
+
context,
|
|
1685
|
+
decls,
|
|
1686
|
+
shorthand_cache,
|
|
1687
|
+
attribute_tracker,
|
|
1688
|
+
id,
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
self.finish_cascade_custom_properties(context);
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
/// Applies a prioritary property and the prioritary properties it depends on.
|
|
1696
|
+
fn ensure_prioritary_property(
|
|
1697
|
+
&mut self,
|
|
1698
|
+
context: &mut computed::Context,
|
|
1699
|
+
decls: &Declarations,
|
|
1700
|
+
cache: &mut ShorthandsWithPropertyReferencesCache,
|
|
1701
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1702
|
+
id: PrioritaryPropertyId,
|
|
1703
|
+
) {
|
|
1704
|
+
if self.ensured_prioritary.contains(id) {
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
self.ensured_prioritary.insert(id);
|
|
1708
|
+
let deps = id.dependencies();
|
|
1709
|
+
if !self.ensured_prioritary.contains_all(deps) {
|
|
1710
|
+
for dep in deps.iter() {
|
|
1711
|
+
self.ensure_prioritary_property(context, decls, cache, attribute_tracker, dep);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
self.apply_one_prioritary_property(context, decls, cache, id, attribute_tracker);
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/// Cascade a given custom property declaration.
|
|
1718
|
+
fn cascade_custom_property(
|
|
1719
|
+
&mut self,
|
|
1720
|
+
context: &mut computed::Context,
|
|
1721
|
+
declaration: &'a CustomDeclaration,
|
|
1722
|
+
priority: CascadePriority,
|
|
1723
|
+
) {
|
|
1724
|
+
let CustomDeclaration {
|
|
1725
|
+
ref name,
|
|
1726
|
+
ref value,
|
|
1727
|
+
} = *declaration;
|
|
1728
|
+
|
|
1729
|
+
if let Some(&(reverted_priority, revert_kind)) = self.reverted.custom.get(name) {
|
|
1730
|
+
if !reverted_priority.allows_when_reverted(&priority, revert_kind) {
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
if !(priority.flags() - context.included_cascade_flags).is_empty() {
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
let entry = match self.seen.custom.var.entry(name) {
|
|
1740
|
+
Entry::Occupied(..) => return,
|
|
1741
|
+
Entry::Vacant(v) => v,
|
|
1742
|
+
};
|
|
1743
|
+
|
|
1744
|
+
let registration = self.stylist.get_custom_property_registration(name);
|
|
1745
|
+
let initial_values = self.stylist.get_custom_property_initial_values();
|
|
1746
|
+
if !Self::value_may_affect_style(context, name, registration, initial_values, value) {
|
|
1747
|
+
entry.insert(false);
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
let has_references = match value {
|
|
1752
|
+
CustomDeclarationValue::Unparsed(unparsed_value) => {
|
|
1753
|
+
// Non-custom dependency is really relevant for registered custom properties
|
|
1754
|
+
// that require computed value of such dependencies.
|
|
1755
|
+
unparsed_value
|
|
1756
|
+
.references
|
|
1757
|
+
.flags
|
|
1758
|
+
.intersects(ReferenceFlags::ATTR | ReferenceFlags::VAR)
|
|
1759
|
+
|| !find_non_custom_references(
|
|
1760
|
+
registration,
|
|
1761
|
+
unparsed_value,
|
|
1762
|
+
context.is_root_element(),
|
|
1763
|
+
)
|
|
1764
|
+
.is_empty()
|
|
1765
|
+
},
|
|
1766
|
+
// XXX This matches the previous behavior but I don't think it's fully sound. Parsed
|
|
1767
|
+
// values may have non-custom references, can't they? But also how do we get a parsed
|
|
1768
|
+
// value at all here?
|
|
1769
|
+
CustomDeclarationValue::Parsed(..) => false,
|
|
1770
|
+
// If we're a wide keyword we either get an initial value or an inherited one (no
|
|
1771
|
+
// references). Revert might have references, but we remove the `seen` entry, so it
|
|
1772
|
+
// doesn't matter.
|
|
1773
|
+
CustomDeclarationValue::CSSWideKeyword(..) => false,
|
|
1774
|
+
};
|
|
1775
|
+
self.may_have_custom_property_cycles |= has_references;
|
|
1776
|
+
entry.insert(has_references);
|
|
1777
|
+
|
|
1778
|
+
match value {
|
|
1779
|
+
CustomDeclarationValue::Unparsed(unparsed_value) => {
|
|
1780
|
+
if !has_references {
|
|
1781
|
+
// If the variable value has no references to other properties, perform
|
|
1782
|
+
// substitution here instead of forcing a full traversal in `substitute_all`
|
|
1783
|
+
// afterwards.
|
|
1784
|
+
substitute_references_if_needed_and_apply(
|
|
1785
|
+
name,
|
|
1786
|
+
SubstitutionFunctionKind::Var,
|
|
1787
|
+
unparsed_value,
|
|
1788
|
+
self.stylist,
|
|
1789
|
+
context,
|
|
1790
|
+
// We just checked there are no attr dependencies.
|
|
1791
|
+
&mut AttributeTracker::new_dummy(),
|
|
1792
|
+
);
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
let value = ComputedRegisteredValue::universal(Arc::clone(unparsed_value));
|
|
1796
|
+
context
|
|
1797
|
+
.builder
|
|
1798
|
+
.substitution_functions
|
|
1799
|
+
.insert_var(registration, name, value);
|
|
1800
|
+
},
|
|
1801
|
+
CustomDeclarationValue::Parsed(parsed_value) => {
|
|
1802
|
+
let value = parsed_value.to_computed_value(context);
|
|
1803
|
+
context
|
|
1804
|
+
.builder
|
|
1805
|
+
.substitution_functions
|
|
1806
|
+
.insert_var(registration, name, value);
|
|
1807
|
+
},
|
|
1808
|
+
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword.revert_kind() {
|
|
1809
|
+
Some(revert_kind) => {
|
|
1810
|
+
self.seen.custom.var.remove(name);
|
|
1811
|
+
self.reverted
|
|
1812
|
+
.custom
|
|
1813
|
+
.insert(name.clone(), (priority, revert_kind));
|
|
1814
|
+
},
|
|
1815
|
+
None => match keyword {
|
|
1816
|
+
CSSWideKeyword::Initial => {
|
|
1817
|
+
// For non-inherited custom properties, 'initial' was handled in value_may_affect_style.
|
|
1818
|
+
debug_assert!(registration.inherits(), "Should've been handled earlier");
|
|
1819
|
+
remove_and_insert_initial_value(
|
|
1820
|
+
name,
|
|
1821
|
+
registration,
|
|
1822
|
+
&mut context.builder.substitution_functions,
|
|
1823
|
+
);
|
|
1824
|
+
},
|
|
1825
|
+
CSSWideKeyword::Inherit => {
|
|
1826
|
+
// For inherited custom properties, 'inherit' was handled in value_may_affect_style.
|
|
1827
|
+
debug_assert!(!registration.inherits(), "Should've been handled earlier");
|
|
1828
|
+
context
|
|
1829
|
+
.style()
|
|
1830
|
+
.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
|
|
1831
|
+
let inherited_value = context
|
|
1832
|
+
.inherited_custom_properties()
|
|
1833
|
+
.non_inherited
|
|
1834
|
+
.get(name)
|
|
1835
|
+
.cloned();
|
|
1836
|
+
if let Some(inherited_value) = inherited_value {
|
|
1837
|
+
context.builder.substitution_functions.insert_var(
|
|
1838
|
+
registration,
|
|
1839
|
+
name,
|
|
1840
|
+
inherited_value,
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
// handled in value_may_affect_style or in the revert_kind branch above.
|
|
1845
|
+
CSSWideKeyword::Revert
|
|
1846
|
+
| CSSWideKeyword::RevertLayer
|
|
1847
|
+
| CSSWideKeyword::RevertRule
|
|
1848
|
+
| CSSWideKeyword::Unset => unreachable!(),
|
|
1849
|
+
},
|
|
1850
|
+
},
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
/// Fast check to avoid calling maybe_note_non_custom_dependency in ~all cases.
|
|
1855
|
+
#[inline]
|
|
1856
|
+
pub fn might_have_non_custom_or_attr_dependency(
|
|
1857
|
+
id: LonghandId,
|
|
1858
|
+
decl: &PropertyDeclaration,
|
|
1859
|
+
) -> bool {
|
|
1860
|
+
if let PropertyDeclaration::WithVariables(v) = decl {
|
|
1861
|
+
return matches!(id, LonghandId::LineHeight | LonghandId::FontSize)
|
|
1862
|
+
|| v.value
|
|
1863
|
+
.variable_value
|
|
1864
|
+
.references
|
|
1865
|
+
.flags
|
|
1866
|
+
.intersects(ReferenceFlags::ATTR);
|
|
1867
|
+
}
|
|
1868
|
+
false
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
/// Note a non-custom property with variable reference that may in turn depend on that property.
|
|
1872
|
+
/// e.g. `font-size` depending on a custom property that may be a registered property using `em`.
|
|
1873
|
+
pub fn maybe_note_non_custom_dependency(
|
|
1874
|
+
&mut self,
|
|
1875
|
+
context: &mut computed::Context,
|
|
1876
|
+
id: LonghandId,
|
|
1877
|
+
decl: &'a PropertyDeclaration,
|
|
1878
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1879
|
+
) {
|
|
1880
|
+
debug_assert!(Self::might_have_non_custom_or_attr_dependency(id, decl));
|
|
1881
|
+
let PropertyDeclaration::WithVariables(v) = decl else {
|
|
1882
|
+
return;
|
|
1883
|
+
};
|
|
1884
|
+
let value = &v.value.variable_value;
|
|
1885
|
+
let refs = &value.references;
|
|
1886
|
+
|
|
1887
|
+
if !refs
|
|
1888
|
+
.flags
|
|
1889
|
+
.intersects(ReferenceFlags::VAR | ReferenceFlags::ATTR)
|
|
1890
|
+
{
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
// Attributes in non-custom properties may reference `var()` or `attr()` in their
|
|
1895
|
+
// values, which we need to track to support chained references and detect cycles.
|
|
1896
|
+
if refs.flags.intersects(ReferenceFlags::ATTR) {
|
|
1897
|
+
self.update_attributes_map(context, value, attribute_tracker);
|
|
1898
|
+
if !refs.flags.intersects(ReferenceFlags::VAR) {
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
// The non-custom node(s) this property feeds. We don't try to figure out here which
|
|
1904
|
+
// referenced custom properties can actually cycle back to it: instead we record the whole
|
|
1905
|
+
// declaration value, and let `substitute_all` traverse its references (following fallbacks
|
|
1906
|
+
// only when the primary is invalid). This way, references that only appear in an unused
|
|
1907
|
+
// fallback (e.g. `var(--exists, var(--font-size-em))`) don't create a spurious cycle, while
|
|
1908
|
+
// references in a used fallback (e.g. `var(--noexist, var(--font-size-em))`) do.
|
|
1909
|
+
//
|
|
1910
|
+
// With unit algebra in `calc()`, references aren't limited to `font-size`. For example,
|
|
1911
|
+
// `--foo: 100ex; font-weight: calc(var(--foo) / 1ex);`, or
|
|
1912
|
+
// `--foo: 1em; zoom: calc(var(--foo) * 30px / 2em);`
|
|
1913
|
+
let references = match id {
|
|
1914
|
+
LonghandId::FontSize => ReferenceFlags::FONT_UNITS,
|
|
1915
|
+
LonghandId::LineHeight => ReferenceFlags::LH_UNITS | ReferenceFlags::FONT_UNITS,
|
|
1916
|
+
LonghandId::ColorScheme => ReferenceFlags::COLOR_SCHEME,
|
|
1917
|
+
_ => return,
|
|
1918
|
+
};
|
|
1919
|
+
|
|
1920
|
+
references.for_each_non_custom(context.is_root_element(), |idx| {
|
|
1921
|
+
self.references_from_non_custom_properties[idx]
|
|
1922
|
+
.get_or_insert_with(Vec::new)
|
|
1923
|
+
.push(v.value.clone());
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
fn value_may_affect_style(
|
|
1928
|
+
context: &computed::Context,
|
|
1929
|
+
name: &Name,
|
|
1930
|
+
registration: &PropertyDescriptors,
|
|
1931
|
+
initial_values: &ComputedCustomProperties,
|
|
1932
|
+
value: &CustomDeclarationValue,
|
|
1933
|
+
) -> bool {
|
|
1934
|
+
match *value {
|
|
1935
|
+
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
|
|
1936
|
+
// For inherited custom properties, explicit 'inherit' means we
|
|
1937
|
+
// can just use any existing value in the inherited
|
|
1938
|
+
// CustomPropertiesMap.
|
|
1939
|
+
if registration.inherits() {
|
|
1940
|
+
return false;
|
|
1941
|
+
}
|
|
1942
|
+
},
|
|
1943
|
+
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial) => {
|
|
1944
|
+
// For non-inherited custom properties, explicit 'initial' means
|
|
1945
|
+
// we can just use any initial value in the registration.
|
|
1946
|
+
if !registration.inherits() {
|
|
1947
|
+
return false;
|
|
1948
|
+
}
|
|
1949
|
+
},
|
|
1950
|
+
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) => {
|
|
1951
|
+
// Explicit 'unset' means we can either just use any existing
|
|
1952
|
+
// value in the inherited CustomPropertiesMap or the initial
|
|
1953
|
+
// value in the registration.
|
|
1954
|
+
return false;
|
|
1955
|
+
},
|
|
1956
|
+
_ => {},
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
let existing_value = context
|
|
1960
|
+
.builder
|
|
1961
|
+
.substitution_functions
|
|
1962
|
+
.get_var(registration, name);
|
|
1963
|
+
let Some(existing_value) = existing_value else {
|
|
1964
|
+
if matches!(
|
|
1965
|
+
value,
|
|
1966
|
+
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial)
|
|
1967
|
+
) {
|
|
1968
|
+
debug_assert!(registration.inherits(), "Should've been handled earlier");
|
|
1969
|
+
// The initial value of a custom property without a
|
|
1970
|
+
// guaranteed-invalid initial value is the same as it
|
|
1971
|
+
// not existing in the map.
|
|
1972
|
+
if registration.initial_value.is_none() {
|
|
1973
|
+
return false;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
return true;
|
|
1977
|
+
};
|
|
1978
|
+
match value {
|
|
1979
|
+
CustomDeclarationValue::Unparsed(value) => {
|
|
1980
|
+
// Don't bother overwriting an existing value with the same
|
|
1981
|
+
// specified value.
|
|
1982
|
+
if let Some(existing_value) = existing_value.as_universal() {
|
|
1983
|
+
return existing_value != value;
|
|
1984
|
+
}
|
|
1985
|
+
},
|
|
1986
|
+
CustomDeclarationValue::Parsed(..) => {
|
|
1987
|
+
// If the value has dependencies, context might not yield the same
|
|
1988
|
+
// result as the eventual value.
|
|
1989
|
+
},
|
|
1990
|
+
CustomDeclarationValue::CSSWideKeyword(kw) => {
|
|
1991
|
+
match kw {
|
|
1992
|
+
CSSWideKeyword::Inherit => {
|
|
1993
|
+
debug_assert!(!registration.inherits(), "Should've been handled earlier");
|
|
1994
|
+
// existing_value is the registered initial value.
|
|
1995
|
+
// Don't bother adding it to self.custom_properties.non_inherited
|
|
1996
|
+
// if the key is also absent from self.inherited.non_inherited.
|
|
1997
|
+
if context
|
|
1998
|
+
.inherited_custom_properties()
|
|
1999
|
+
.non_inherited
|
|
2000
|
+
.get(name)
|
|
2001
|
+
.is_none()
|
|
2002
|
+
{
|
|
2003
|
+
return false;
|
|
2004
|
+
}
|
|
2005
|
+
},
|
|
2006
|
+
CSSWideKeyword::Initial => {
|
|
2007
|
+
debug_assert!(registration.inherits(), "Should've been handled earlier");
|
|
2008
|
+
// Don't bother overwriting an existing value with the initial value
|
|
2009
|
+
// specified in the registration.
|
|
2010
|
+
if let Some(initial_value) = initial_values.get(registration, name) {
|
|
2011
|
+
return existing_value != initial_value;
|
|
2012
|
+
}
|
|
2013
|
+
},
|
|
2014
|
+
CSSWideKeyword::Unset => {
|
|
2015
|
+
debug_assert!(false, "Should've been handled earlier");
|
|
2016
|
+
},
|
|
2017
|
+
CSSWideKeyword::Revert
|
|
2018
|
+
| CSSWideKeyword::RevertLayer
|
|
2019
|
+
| CSSWideKeyword::RevertRule => {},
|
|
2020
|
+
}
|
|
2021
|
+
},
|
|
2022
|
+
};
|
|
2023
|
+
|
|
2024
|
+
true
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
/// For a given unparsed variable, update the attributes map with its attr references.
|
|
2028
|
+
pub fn update_attributes_map(
|
|
2029
|
+
&mut self,
|
|
2030
|
+
context: &mut computed::Context,
|
|
2031
|
+
value: &'a VariableValue,
|
|
2032
|
+
attribute_tracker: &mut AttributeTracker,
|
|
2033
|
+
) {
|
|
2034
|
+
let refs = &value.references;
|
|
2035
|
+
if !refs.flags.intersects(ReferenceFlags::ATTR) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
self.may_have_custom_property_cycles = true;
|
|
2039
|
+
|
|
2040
|
+
for next in &refs.refs {
|
|
2041
|
+
if !next.is_attr_with_type() || !self.seen.custom.attr.insert(&next.name) {
|
|
2042
|
+
// Only type() can have nested references, so we don't need to eagerly look at
|
|
2043
|
+
// others.
|
|
2044
|
+
continue;
|
|
2045
|
+
}
|
|
2046
|
+
if let Ok(v) = get_attr_value_for_cycle_resolution(
|
|
2047
|
+
&next.name,
|
|
2048
|
+
&next.attribute_data,
|
|
2049
|
+
&value.url_data,
|
|
2050
|
+
attribute_tracker,
|
|
2051
|
+
) {
|
|
2052
|
+
context
|
|
2053
|
+
.builder
|
|
2054
|
+
.substitution_functions
|
|
2055
|
+
.insert_attr(&next.name, v);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
/// Computes the map of applicable custom properties, saving the result into the computed
|
|
2061
|
+
/// context, and applies the prioritary properties interleaved with custom-property resolution.
|
|
2062
|
+
pub fn finish_cascade_custom_properties(&mut self, context: &mut computed::Context) {
|
|
2063
|
+
context
|
|
2064
|
+
.builder
|
|
2065
|
+
.substitution_functions
|
|
2066
|
+
.custom_properties
|
|
2067
|
+
.shrink_to_fit();
|
|
2068
|
+
|
|
2069
|
+
// Some pages apply a lot of redundant custom properties, see e.g.
|
|
2070
|
+
// bug 1758974 comment 5. Try to detect the case where the values
|
|
2071
|
+
// haven't really changed, and save some memory by reusing the inherited
|
|
2072
|
+
// map in that case.
|
|
2073
|
+
let initial_values = self.stylist.get_custom_property_initial_values();
|
|
2074
|
+
let reuse_inherited = context.inherited_custom_properties().inherited
|
|
2075
|
+
== context
|
|
2076
|
+
.builder
|
|
2077
|
+
.substitution_functions
|
|
2078
|
+
.custom_properties
|
|
2079
|
+
.inherited;
|
|
2080
|
+
if reuse_inherited {
|
|
2081
|
+
let inherited = context.inherited_custom_properties().inherited.clone();
|
|
2082
|
+
context
|
|
2083
|
+
.builder
|
|
2084
|
+
.substitution_functions
|
|
2085
|
+
.custom_properties
|
|
2086
|
+
.inherited = inherited;
|
|
2087
|
+
}
|
|
2088
|
+
if initial_values.non_inherited
|
|
2089
|
+
== context
|
|
2090
|
+
.builder
|
|
2091
|
+
.substitution_functions
|
|
2092
|
+
.custom_properties
|
|
2093
|
+
.non_inherited
|
|
2094
|
+
{
|
|
2095
|
+
let non_inherited = initial_values.non_inherited.clone();
|
|
2096
|
+
context
|
|
2097
|
+
.builder
|
|
2098
|
+
.substitution_functions
|
|
2099
|
+
.custom_properties
|
|
2100
|
+
.non_inherited = non_inherited;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
fn substitute_all(
|
|
2106
|
+
seen: &SeenSubstitutionFunctions,
|
|
2107
|
+
references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
|
|
2108
|
+
stylist: &Stylist,
|
|
2109
|
+
computed_context: &mut computed::Context,
|
|
2110
|
+
cascade: &mut Cascade,
|
|
2111
|
+
decls: &Declarations,
|
|
2112
|
+
shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
|
|
2113
|
+
attr_tracker: &mut AttributeTracker,
|
|
2114
|
+
) {
|
|
2115
|
+
// The cycle dependencies removal in this function is a variant
|
|
2116
|
+
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
|
2117
|
+
// listed in
|
|
2118
|
+
// https://en.wikipedia.org/w/index.php?
|
|
2119
|
+
// title=Tarjan%27s_strongly_connected_components_algorithm&oldid=801728495
|
|
2120
|
+
|
|
2121
|
+
#[derive(Clone, Eq, PartialEq, Debug)]
|
|
2122
|
+
enum VarType {
|
|
2123
|
+
Attr(Name),
|
|
2124
|
+
Custom(Name),
|
|
2125
|
+
NonCustom(SingleNonCustomReference),
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
/// Struct recording necessary information for each variable.
|
|
2129
|
+
#[derive(Debug)]
|
|
2130
|
+
struct VarInfo {
|
|
2131
|
+
/// The name of the variable. It will be taken when the corresponding variable is popped
|
|
2132
|
+
/// from the stack, which serves as a mark for whether the variable is currently in the
|
|
2133
|
+
/// stack below.
|
|
2134
|
+
var: Option<VarType>,
|
|
2135
|
+
/// If the variable is in a dependency cycle, lowlink represents a smaller index which
|
|
2136
|
+
/// corresponds to a variable in the same strong connected component, which is known to be
|
|
2137
|
+
/// accessible from this variable. It is not necessarily the root, though.
|
|
2138
|
+
lowlink: usize,
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
#[derive(Debug, Default)]
|
|
2142
|
+
struct OrderIndexMap {
|
|
2143
|
+
/// The map from the custom property name to its order index.
|
|
2144
|
+
var: PrecomputedHashMap<Name, usize>,
|
|
2145
|
+
/// The map from the attribute name to its order index.
|
|
2146
|
+
attr: PrecomputedHashMap<Name, usize>,
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
impl OrderIndexMap {
|
|
2150
|
+
fn clear(&mut self) {
|
|
2151
|
+
self.var.clear();
|
|
2152
|
+
self.attr.clear();
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
/// Context struct for traversing the variable graph, so that we can
|
|
2157
|
+
/// avoid referencing all the fields multiple times.
|
|
2158
|
+
struct Context<'a, 'b: 'a, 'c, 'd> {
|
|
2159
|
+
/// Number of variables visited. This is used as the order index
|
|
2160
|
+
/// when we visit a new unresolved variable.
|
|
2161
|
+
count: usize,
|
|
2162
|
+
/// The map from a substitution function name to its order index.
|
|
2163
|
+
index_map: OrderIndexMap,
|
|
2164
|
+
/// Mapping from a non-custom dependency to its order index.
|
|
2165
|
+
non_custom_index_map: NonCustomReferenceMap<usize>,
|
|
2166
|
+
/// Information of each variable indexed by the order index.
|
|
2167
|
+
var_info: SmallVec<[VarInfo; 5]>,
|
|
2168
|
+
/// The stack of order index of visited variables. It contains
|
|
2169
|
+
/// all unfinished strong connected components.
|
|
2170
|
+
stack: SmallVec<[usize; 5]>,
|
|
2171
|
+
/// The stylist is used to get registered properties, and to resolve the environment to
|
|
2172
|
+
/// substitute `env()` variables.
|
|
2173
|
+
stylist: &'a Stylist,
|
|
2174
|
+
/// The computed context is used to get inherited custom properties, compute registered
|
|
2175
|
+
/// custom properties, and apply prioritary properties.
|
|
2176
|
+
computed_context: &'a mut computed::Context<'b>,
|
|
2177
|
+
/// The cascade owns prioritary-property application; when a `NonCustom` node (or a
|
|
2178
|
+
/// color-scheme-dependent custom property) becomes resolvable, we apply the corresponding
|
|
2179
|
+
/// prioritary property directly through it.
|
|
2180
|
+
cascade: &'a mut Cascade<'c>,
|
|
2181
|
+
/// The declarations, needed to apply prioritary properties.
|
|
2182
|
+
decls: &'a Declarations<'d>,
|
|
2183
|
+
/// Shorthand cache, needed to apply prioritary properties.
|
|
2184
|
+
cache: &'a mut ShorthandsWithPropertyReferencesCache,
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
impl<'a, 'b: 'a, 'c, 'd> Context<'a, 'b, 'c, 'd> {
|
|
2188
|
+
fn reset(&mut self) {
|
|
2189
|
+
self.count = 0;
|
|
2190
|
+
self.index_map.clear();
|
|
2191
|
+
self.non_custom_index_map = Default::default();
|
|
2192
|
+
self.var_info.clear();
|
|
2193
|
+
self.stack.clear();
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
fn map(&self) -> &ComputedSubstitutionFunctions {
|
|
2197
|
+
&self.computed_context.builder.substitution_functions
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
fn map_mut(&mut self) -> &mut ComputedSubstitutionFunctions {
|
|
2201
|
+
&mut self.computed_context.builder.substitution_functions
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
/// Marks a given `name` as being in a loop.
|
|
2205
|
+
fn handle_loop(&mut self, name: &VarType) {
|
|
2206
|
+
match name {
|
|
2207
|
+
VarType::Attr(name) => {
|
|
2208
|
+
self.computed_context
|
|
2209
|
+
.builder
|
|
2210
|
+
.substitution_functions
|
|
2211
|
+
.remove_attr(name);
|
|
2212
|
+
},
|
|
2213
|
+
VarType::Custom(name) => {
|
|
2214
|
+
// This variable is in a loop. Resolve to invalid.
|
|
2215
|
+
handle_invalid_at_computed_value_time(
|
|
2216
|
+
name,
|
|
2217
|
+
self.stylist.get_custom_property_registration(name),
|
|
2218
|
+
self.computed_context,
|
|
2219
|
+
);
|
|
2220
|
+
},
|
|
2221
|
+
VarType::NonCustom(non_custom) => {
|
|
2222
|
+
self.computed_context
|
|
2223
|
+
.builder
|
|
2224
|
+
.invalid_non_custom_properties
|
|
2225
|
+
.insert(non_custom.to_prioritary_id().to_longhand());
|
|
2226
|
+
},
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
/// Applies a prioritary property (and its dependencies) while resolving custom properties.
|
|
2231
|
+
///
|
|
2232
|
+
/// The in-progress map lives outside `computed_context` during traversal; we move it into
|
|
2233
|
+
/// `computed_context.builder.substitution_functions` so the prioritary declaration's `var()`
|
|
2234
|
+
/// references resolve against the custom properties resolved so far, then take it back out.
|
|
2235
|
+
fn apply_prioritary_property(
|
|
2236
|
+
&mut self,
|
|
2237
|
+
id: PrioritaryPropertyId,
|
|
2238
|
+
attr_tracker: &mut AttributeTracker,
|
|
2239
|
+
) {
|
|
2240
|
+
self.cascade.ensure_prioritary_property(
|
|
2241
|
+
self.computed_context,
|
|
2242
|
+
self.decls,
|
|
2243
|
+
self.cache,
|
|
2244
|
+
attr_tracker,
|
|
2245
|
+
id,
|
|
2246
|
+
);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
/// Traverse the references in `root` (the value of a custom property or of a non-custom
|
|
2251
|
+
/// property feeding a `NonCustom` node), creating graph edges to the referenced substitution
|
|
2252
|
+
/// functions and updating `lowlink`/`self_ref` accordingly.
|
|
2253
|
+
///
|
|
2254
|
+
/// We follow var()/attr()/env() fallbacks only when the primary substitution function is
|
|
2255
|
+
/// guaranteed-invalid (i.e. when the fallback is actually used). This matches the substitution
|
|
2256
|
+
/// order and the resolution of https://github.com/w3c/csswg-drafts/issues/11500: cycles (or
|
|
2257
|
+
/// dependencies) that only exist through an unused fallback don't count.
|
|
2258
|
+
///
|
|
2259
|
+
/// We need to bubble up non custom references from unregistered properties.
|
|
2260
|
+
fn visit_value_references<'a, 'b, 'c, 'd>(
|
|
2261
|
+
var: &VarType,
|
|
2262
|
+
root: &References,
|
|
2263
|
+
url_data: &UrlExtraData,
|
|
2264
|
+
index: usize,
|
|
2265
|
+
references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
|
|
2266
|
+
context: &mut Context<'a, 'b, 'c, 'd>,
|
|
2267
|
+
lowlink: &mut usize,
|
|
2268
|
+
self_ref: &mut bool,
|
|
2269
|
+
attribute_tracker: &mut AttributeTracker,
|
|
2270
|
+
non_custom_references: &mut ReferenceFlags,
|
|
2271
|
+
) {
|
|
2272
|
+
// FIXME: Maybe avoid visiting the same var twice if not needed?
|
|
2273
|
+
let mut refs_stack = SmallVec::<[&References; 5]>::new();
|
|
2274
|
+
refs_stack.push(root);
|
|
2275
|
+
while let Some(refs) = refs_stack.pop() {
|
|
2276
|
+
*non_custom_references |= refs.flags;
|
|
2277
|
+
for next in &refs.refs {
|
|
2278
|
+
if next.substitution_kind == SubstitutionFunctionKind::Env {
|
|
2279
|
+
// env() doesn't reference custom properties, so it never participates in
|
|
2280
|
+
// cycles; its fallback is used only when the environment doesn't provide
|
|
2281
|
+
// the variable.
|
|
2282
|
+
let device = context.stylist.device();
|
|
2283
|
+
let present = device
|
|
2284
|
+
.environment()
|
|
2285
|
+
.get(&next.name, device, url_data)
|
|
2286
|
+
.is_some();
|
|
2287
|
+
if !present {
|
|
2288
|
+
if let Some(ref fallback) = next.fallback {
|
|
2289
|
+
refs_stack.push(&fallback.references);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
continue;
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
let next_var = if next.substitution_kind == SubstitutionFunctionKind::Attr {
|
|
2296
|
+
// An type()-less attr() within attr(... type()) can still have nested
|
|
2297
|
+
// references.
|
|
2298
|
+
let can_chain = next.is_attr_with_type() || matches!(var, VarType::Attr(..));
|
|
2299
|
+
if !can_chain {
|
|
2300
|
+
continue;
|
|
2301
|
+
}
|
|
2302
|
+
if context.map().get_attr(&next.name).is_none() {
|
|
2303
|
+
if let Ok(val) = get_attr_value_for_cycle_resolution(
|
|
2304
|
+
&next.name,
|
|
2305
|
+
&next.attribute_data,
|
|
2306
|
+
url_data,
|
|
2307
|
+
attribute_tracker,
|
|
2308
|
+
) {
|
|
2309
|
+
context.map_mut().insert_attr(&next.name, val);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
VarType::Attr(next.name.clone())
|
|
2313
|
+
} else {
|
|
2314
|
+
VarType::Custom(next.name.clone())
|
|
2315
|
+
};
|
|
2316
|
+
|
|
2317
|
+
visit_link(
|
|
2318
|
+
next_var,
|
|
2319
|
+
index,
|
|
2320
|
+
references_from_non_custom_properties,
|
|
2321
|
+
context,
|
|
2322
|
+
lowlink,
|
|
2323
|
+
self_ref,
|
|
2324
|
+
attribute_tracker,
|
|
2325
|
+
);
|
|
2326
|
+
|
|
2327
|
+
// Now that the primary reference has been resolved, classify it to
|
|
2328
|
+
// decide whether its fallback is used.
|
|
2329
|
+
let kind = next.substitution_kind;
|
|
2330
|
+
let resolved = match kind {
|
|
2331
|
+
SubstitutionFunctionKind::Var => {
|
|
2332
|
+
let registration =
|
|
2333
|
+
context.stylist.get_custom_property_registration(&next.name);
|
|
2334
|
+
context.map().get_var(registration, &next.name)
|
|
2335
|
+
},
|
|
2336
|
+
SubstitutionFunctionKind::Attr => context.map().get_attr(&next.name),
|
|
2337
|
+
SubstitutionFunctionKind::Env => unreachable!("Handled above"),
|
|
2338
|
+
};
|
|
2339
|
+
// The primary is guaranteed-invalid if it's absent from the map, or still
|
|
2340
|
+
// present but unresolved (i.e. part of a cycle currently being resolved).
|
|
2341
|
+
let mut primary_valid = false;
|
|
2342
|
+
if let Some(resolved) = resolved {
|
|
2343
|
+
if let Some(v) = resolved.as_universal() {
|
|
2344
|
+
primary_valid = !v.has_references();
|
|
2345
|
+
*non_custom_references |= v.references.flags;
|
|
2346
|
+
} else {
|
|
2347
|
+
// Resolved and computed value, no other custom references left.
|
|
2348
|
+
primary_valid = true;
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
if !primary_valid {
|
|
2353
|
+
if let Some(ref fallback) = next.fallback {
|
|
2354
|
+
refs_stack.push(&fallback.references);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
/// Traverse a single dependency `var` of the variable at order index `index`, updating its
|
|
2362
|
+
/// `lowlink`/`self_ref` from the result.
|
|
2363
|
+
fn visit_link<'a, 'b, 'c, 'd>(
|
|
2364
|
+
var: VarType,
|
|
2365
|
+
index: usize,
|
|
2366
|
+
non_custom_references: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
|
|
2367
|
+
context: &mut Context<'a, 'b, 'c, 'd>,
|
|
2368
|
+
lowlink: &mut usize,
|
|
2369
|
+
self_ref: &mut bool,
|
|
2370
|
+
attr_tracker: &mut AttributeTracker,
|
|
2371
|
+
) {
|
|
2372
|
+
let next_index = match traverse(var, non_custom_references, context, attr_tracker) {
|
|
2373
|
+
Some(index) => index,
|
|
2374
|
+
// There is nothing to do if the next variable has been
|
|
2375
|
+
// fully resolved at this point.
|
|
2376
|
+
None => return,
|
|
2377
|
+
};
|
|
2378
|
+
let next_info = &context.var_info[next_index];
|
|
2379
|
+
if next_index > index {
|
|
2380
|
+
// The next variable has a larger index than us, so it
|
|
2381
|
+
// must be inserted in the recursive call above. We want
|
|
2382
|
+
// to get its lowlink.
|
|
2383
|
+
*lowlink = cmp::min(*lowlink, next_info.lowlink);
|
|
2384
|
+
} else if next_index == index {
|
|
2385
|
+
*self_ref = true;
|
|
2386
|
+
} else if next_info.var.is_some() {
|
|
2387
|
+
// The next variable has a smaller order index and it is
|
|
2388
|
+
// in the stack, so we are at the same component.
|
|
2389
|
+
*lowlink = cmp::min(*lowlink, next_index);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
/// This function combines the traversal for cycle removal and value
|
|
2394
|
+
/// substitution. It returns either a signal None if this variable
|
|
2395
|
+
/// has been fully resolved (to either having no reference or being
|
|
2396
|
+
/// marked invalid), or the order index for the given name.
|
|
2397
|
+
///
|
|
2398
|
+
/// When it returns, the variable corresponds to the name would be
|
|
2399
|
+
/// in one of the following states:
|
|
2400
|
+
/// * It is still in context.stack, which means it is part of an
|
|
2401
|
+
/// potentially incomplete dependency circle.
|
|
2402
|
+
/// * It has been removed from the map. It can be either that the
|
|
2403
|
+
/// substitution failed, or it is inside a dependency circle.
|
|
2404
|
+
/// When this function removes a variable from the map because
|
|
2405
|
+
/// of dependency circle, it would put all variables in the same
|
|
2406
|
+
/// strong connected component to the set together.
|
|
2407
|
+
/// * It doesn't have any reference, because either this variable
|
|
2408
|
+
/// doesn't have reference at all in specified value, or it has
|
|
2409
|
+
/// been completely resolved.
|
|
2410
|
+
/// * There is no such variable at all.
|
|
2411
|
+
fn traverse<'a, 'b, 'c, 'd>(
|
|
2412
|
+
var: VarType,
|
|
2413
|
+
references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Arc<UnparsedValue>>>,
|
|
2414
|
+
context: &mut Context<'a, 'b, 'c, 'd>,
|
|
2415
|
+
attribute_tracker: &mut AttributeTracker,
|
|
2416
|
+
) -> Option<usize> {
|
|
2417
|
+
// Some shortcut checks.
|
|
2418
|
+
// The non-custom (font/line-height/color-scheme) dependencies of this value, carried out
|
|
2419
|
+
// of the match so we can create the corresponding nodes once we've pushed this variable.
|
|
2420
|
+
let mut value_non_custom_refs = ReferenceFlags::empty();
|
|
2421
|
+
let mut registered = false;
|
|
2422
|
+
let value = match var {
|
|
2423
|
+
VarType::Custom(ref name) | VarType::Attr(ref name) => {
|
|
2424
|
+
let map = &context.computed_context.builder.substitution_functions;
|
|
2425
|
+
let (registration, value, kind) = if matches!(var, VarType::Custom(..)) {
|
|
2426
|
+
let registration = context.stylist.get_custom_property_registration(name);
|
|
2427
|
+
(
|
|
2428
|
+
registration,
|
|
2429
|
+
map.get_var(registration, name)?.as_universal()?,
|
|
2430
|
+
SubstitutionFunctionKind::Var,
|
|
2431
|
+
)
|
|
2432
|
+
} else {
|
|
2433
|
+
// `attr()` is always treated as unregistered.
|
|
2434
|
+
(
|
|
2435
|
+
PropertyDescriptors::unregistered(),
|
|
2436
|
+
map.get_attr(name)?.as_universal()?,
|
|
2437
|
+
SubstitutionFunctionKind::Attr,
|
|
2438
|
+
)
|
|
2439
|
+
};
|
|
2440
|
+
let is_root = context.computed_context.is_root_element();
|
|
2441
|
+
value_non_custom_refs = find_non_custom_references(registration, value, is_root);
|
|
2442
|
+
registered = !registration.is_universal();
|
|
2443
|
+
let has_dependency = value
|
|
2444
|
+
.references
|
|
2445
|
+
.flags
|
|
2446
|
+
.intersects(ReferenceFlags::ATTR | ReferenceFlags::VAR)
|
|
2447
|
+
|| !value_non_custom_refs.is_empty();
|
|
2448
|
+
// Nothing to resolve.
|
|
2449
|
+
if !has_dependency {
|
|
2450
|
+
debug_assert!(
|
|
2451
|
+
!value.references.flags.intersects(ReferenceFlags::ENV),
|
|
2452
|
+
"Should've been handled earlier"
|
|
2453
|
+
);
|
|
2454
|
+
if kind == SubstitutionFunctionKind::Attr || registered {
|
|
2455
|
+
// We might still need to compute the value if this is not an universal
|
|
2456
|
+
// registration but we thought it had a dependency during the cascade and it
|
|
2457
|
+
// turned out not to. Note that if this was already computed we would've
|
|
2458
|
+
// bailed out in the as_universal() check.
|
|
2459
|
+
let value = value.clone();
|
|
2460
|
+
substitute_references_if_needed_and_apply(
|
|
2461
|
+
name,
|
|
2462
|
+
kind,
|
|
2463
|
+
&value,
|
|
2464
|
+
context.stylist,
|
|
2465
|
+
context.computed_context,
|
|
2466
|
+
attribute_tracker,
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
return None;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
// Has this variable been visited?
|
|
2473
|
+
let index_map = if kind == SubstitutionFunctionKind::Var {
|
|
2474
|
+
&mut context.index_map.var
|
|
2475
|
+
} else {
|
|
2476
|
+
&mut context.index_map.attr
|
|
2477
|
+
};
|
|
2478
|
+
match index_map.entry(name.clone()) {
|
|
2479
|
+
Entry::Occupied(entry) => {
|
|
2480
|
+
return Some(*entry.get());
|
|
2481
|
+
},
|
|
2482
|
+
Entry::Vacant(entry) => {
|
|
2483
|
+
entry.insert(context.count);
|
|
2484
|
+
},
|
|
2485
|
+
}
|
|
2486
|
+
// Hold a strong reference to the value so that we don't
|
|
2487
|
+
// need to keep reference to context.map.
|
|
2488
|
+
Some(value.clone())
|
|
2489
|
+
},
|
|
2490
|
+
VarType::NonCustom(ref non_custom) => {
|
|
2491
|
+
let entry = &mut context.non_custom_index_map[*non_custom];
|
|
2492
|
+
if let Some(v) = entry {
|
|
2493
|
+
return Some(*v);
|
|
2494
|
+
}
|
|
2495
|
+
*entry = Some(context.count);
|
|
2496
|
+
None
|
|
2497
|
+
},
|
|
2498
|
+
};
|
|
2499
|
+
|
|
2500
|
+
// Add new entry to the information table.
|
|
2501
|
+
let index = context.count;
|
|
2502
|
+
context.count += 1;
|
|
2503
|
+
debug_assert_eq!(index, context.var_info.len());
|
|
2504
|
+
context.var_info.push(VarInfo {
|
|
2505
|
+
var: Some(var.clone()),
|
|
2506
|
+
lowlink: index,
|
|
2507
|
+
});
|
|
2508
|
+
context.stack.push(index);
|
|
2509
|
+
|
|
2510
|
+
let mut self_ref = false;
|
|
2511
|
+
let mut lowlink = index;
|
|
2512
|
+
if let Some(v) = value.as_ref() {
|
|
2513
|
+
debug_assert!(
|
|
2514
|
+
matches!(var, VarType::Custom(_) | VarType::Attr(_)),
|
|
2515
|
+
"Non-custom property has references?"
|
|
2516
|
+
);
|
|
2517
|
+
|
|
2518
|
+
// Visit the references in this value...
|
|
2519
|
+
visit_value_references(
|
|
2520
|
+
&var,
|
|
2521
|
+
&v.references,
|
|
2522
|
+
&v.url_data,
|
|
2523
|
+
index,
|
|
2524
|
+
references_from_non_custom_properties,
|
|
2525
|
+
context,
|
|
2526
|
+
&mut lowlink,
|
|
2527
|
+
&mut self_ref,
|
|
2528
|
+
attribute_tracker,
|
|
2529
|
+
&mut value_non_custom_refs,
|
|
2530
|
+
);
|
|
2531
|
+
|
|
2532
|
+
// ... Then the non-custom properties this value depends on (font-size, line-height,
|
|
2533
|
+
// color-scheme), as computed by `find_non_custom_references` above.
|
|
2534
|
+
let is_root = context.computed_context.is_root_element();
|
|
2535
|
+
if registered && !value_non_custom_refs.is_empty() {
|
|
2536
|
+
value_non_custom_refs.for_each_non_custom(is_root, |r| {
|
|
2537
|
+
visit_link(
|
|
2538
|
+
VarType::NonCustom(r),
|
|
2539
|
+
index,
|
|
2540
|
+
references_from_non_custom_properties,
|
|
2541
|
+
context,
|
|
2542
|
+
&mut lowlink,
|
|
2543
|
+
&mut self_ref,
|
|
2544
|
+
attribute_tracker,
|
|
2545
|
+
);
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
2548
|
+
} else if let VarType::NonCustom(non_custom) = var {
|
|
2549
|
+
// line-height's resolution depends on font-size's, so its node has a graph edge to the
|
|
2550
|
+
// font-size node. This is needed for cycle detection (not just application order, which
|
|
2551
|
+
// `prioritary_property_dependencies(LineHeight)` handles): it puts the line-height node
|
|
2552
|
+
// in the same strongly-connected component as a font-size<->custom cycle, so the node
|
|
2553
|
+
// doesn't apply line-height (and, transitively, font-size) before that cycle is
|
|
2554
|
+
// removed.
|
|
2555
|
+
//
|
|
2556
|
+
// TODO(emilio): I think the right fix for this is
|
|
2557
|
+
// s/SingleNonCustomReference/PrioritaryPropertyId in VarType::NonCustom.
|
|
2558
|
+
if non_custom == SingleNonCustomReference::LhUnits {
|
|
2559
|
+
visit_link(
|
|
2560
|
+
VarType::NonCustom(SingleNonCustomReference::FontUnits),
|
|
2561
|
+
index,
|
|
2562
|
+
references_from_non_custom_properties,
|
|
2563
|
+
context,
|
|
2564
|
+
&mut lowlink,
|
|
2565
|
+
&mut self_ref,
|
|
2566
|
+
attribute_tracker,
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
let entry = &references_from_non_custom_properties[non_custom];
|
|
2570
|
+
if let Some(values) = entry.as_ref() {
|
|
2571
|
+
for value in values {
|
|
2572
|
+
// Traverse the non-custom property's declaration value(s), creating edges to
|
|
2573
|
+
// the custom properties they reference (directly or through a used fallback)
|
|
2574
|
+
// that might cycle back to this node. Note we traverse unregistered/universal
|
|
2575
|
+
// references too: while their own font-relative units are textual (resolved
|
|
2576
|
+
// against the parent, see `find_non_custom_references`), they may chain to a
|
|
2577
|
+
// registered property that does cycle back.
|
|
2578
|
+
let value = &value.variable_value;
|
|
2579
|
+
visit_value_references(
|
|
2580
|
+
&var,
|
|
2581
|
+
&value.references,
|
|
2582
|
+
&value.url_data,
|
|
2583
|
+
index,
|
|
2584
|
+
references_from_non_custom_properties,
|
|
2585
|
+
context,
|
|
2586
|
+
&mut lowlink,
|
|
2587
|
+
&mut self_ref,
|
|
2588
|
+
attribute_tracker,
|
|
2589
|
+
&mut Default::default(),
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
context.var_info[index].lowlink = lowlink;
|
|
2596
|
+
if lowlink != index {
|
|
2597
|
+
// This variable is in a loop, but it is not the root of this strong connected
|
|
2598
|
+
// component. We simply return for now, and the root would remove it from the map.
|
|
2599
|
+
//
|
|
2600
|
+
// This cannot be removed from the map here, because otherwise the shortcut check at the
|
|
2601
|
+
// beginning of this function would return the wrong value.
|
|
2602
|
+
return Some(index);
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
// This is the root of a strong-connected component.
|
|
2606
|
+
let mut in_loop = self_ref;
|
|
2607
|
+
loop {
|
|
2608
|
+
let var_index = context
|
|
2609
|
+
.stack
|
|
2610
|
+
.pop()
|
|
2611
|
+
.expect("The current variable should still be in stack");
|
|
2612
|
+
let var_info = &mut context.var_info[var_index];
|
|
2613
|
+
// We should never visit the variable again, so it's safe to take the name away.
|
|
2614
|
+
let var_name = var_info
|
|
2615
|
+
.var
|
|
2616
|
+
.take()
|
|
2617
|
+
.expect("Variable should not be popped from stack twice");
|
|
2618
|
+
if var_index != index {
|
|
2619
|
+
// Anything here is in a loop which can traverse to the node we are handling, so
|
|
2620
|
+
// it's invalid at computed-value time.
|
|
2621
|
+
in_loop = true;
|
|
2622
|
+
}
|
|
2623
|
+
if in_loop {
|
|
2624
|
+
context.handle_loop(&var_name);
|
|
2625
|
+
}
|
|
2626
|
+
if var_index == index {
|
|
2627
|
+
debug_assert_eq!(var_name, var);
|
|
2628
|
+
break;
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
if in_loop {
|
|
2633
|
+
return None;
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
// Not in a loop, apply the value.
|
|
2637
|
+
match var {
|
|
2638
|
+
VarType::Custom(ref name) | VarType::Attr(ref name) => {
|
|
2639
|
+
if let Some(ref v) = value {
|
|
2640
|
+
let kind = if matches!(var, VarType::Custom(..)) {
|
|
2641
|
+
SubstitutionFunctionKind::Var
|
|
2642
|
+
} else {
|
|
2643
|
+
SubstitutionFunctionKind::Attr
|
|
2644
|
+
};
|
|
2645
|
+
// We know we're not in a loop now, perform substitution.
|
|
2646
|
+
// TODO(emilio): Merge with the cycle detection loop instead?
|
|
2647
|
+
substitute_references_if_needed_and_apply(
|
|
2648
|
+
name,
|
|
2649
|
+
kind,
|
|
2650
|
+
v,
|
|
2651
|
+
context.stylist,
|
|
2652
|
+
context.computed_context,
|
|
2653
|
+
attribute_tracker,
|
|
2654
|
+
);
|
|
2655
|
+
}
|
|
2656
|
+
},
|
|
2657
|
+
VarType::NonCustom(non_custom) => {
|
|
2658
|
+
context.apply_prioritary_property(non_custom.to_prioritary_id(), attribute_tracker);
|
|
2659
|
+
},
|
|
2660
|
+
}
|
|
2661
|
+
// All resolved, so return the signal value.
|
|
2662
|
+
None
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
let mut context = Context {
|
|
2666
|
+
count: 0,
|
|
2667
|
+
index_map: OrderIndexMap::default(),
|
|
2668
|
+
non_custom_index_map: NonCustomReferenceMap::default(),
|
|
2669
|
+
stack: SmallVec::new(),
|
|
2670
|
+
var_info: SmallVec::new(),
|
|
2671
|
+
stylist,
|
|
2672
|
+
computed_context: &mut *computed_context,
|
|
2673
|
+
cascade: &mut *cascade,
|
|
2674
|
+
decls,
|
|
2675
|
+
cache: &mut *shorthand_cache,
|
|
2676
|
+
};
|
|
2677
|
+
let mut first = true;
|
|
2678
|
+
let mut run_one = |var: VarType| {
|
|
2679
|
+
if !first {
|
|
2680
|
+
context.reset();
|
|
2681
|
+
}
|
|
2682
|
+
first = false;
|
|
2683
|
+
traverse(
|
|
2684
|
+
var,
|
|
2685
|
+
references_from_non_custom_properties,
|
|
2686
|
+
&mut context,
|
|
2687
|
+
attr_tracker,
|
|
2688
|
+
);
|
|
2689
|
+
};
|
|
2690
|
+
// Note that `seen` doesn't contain names inherited from our parent, but
|
|
2691
|
+
// those can't have variable references (since we inherit the computed
|
|
2692
|
+
// variables) so we don't want to spend cycles traversing them anyway.
|
|
2693
|
+
for (var, has_refs) in &seen.var {
|
|
2694
|
+
if !has_refs {
|
|
2695
|
+
continue;
|
|
2696
|
+
}
|
|
2697
|
+
run_one(VarType::Custom((*var).clone()));
|
|
2698
|
+
}
|
|
2699
|
+
// Traverse potentially untraversed chained references from `attr(type())` in non-custom
|
|
2700
|
+
// properties.
|
|
2701
|
+
for attr in &seen.attr {
|
|
2702
|
+
run_one(VarType::Attr((*attr).clone()));
|
|
2703
|
+
}
|
|
2704
|
+
}
|