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,2023 @@
|
|
|
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
|
+
//! Support for [custom properties for cascading variables][custom].
|
|
6
|
+
//!
|
|
7
|
+
//! [custom]: https://drafts.csswg.org/css-variables/
|
|
8
|
+
|
|
9
|
+
use crate::custom_properties_map::{CustomPropertiesMap, OwnMap};
|
|
10
|
+
use crate::device::Device;
|
|
11
|
+
use crate::dom::AttributeTracker;
|
|
12
|
+
use crate::properties::{CSSWideKeyword, PrioritaryPropertyId};
|
|
13
|
+
use crate::properties_and_values::{
|
|
14
|
+
rule::Descriptors as PropertyDescriptors,
|
|
15
|
+
syntax::Descriptor as SyntaxDescriptor,
|
|
16
|
+
value::{
|
|
17
|
+
AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,
|
|
18
|
+
SpecifiedValue as SpecifiedRegisteredValue,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
use crate::stylesheets::UrlExtraData;
|
|
22
|
+
use crate::stylesheets::container_rule::AttrReferenceSet;
|
|
23
|
+
use crate::stylist::Stylist;
|
|
24
|
+
use crate::typed_om::{
|
|
25
|
+
ToTyped, TypedValue, UnparsedSegment, UnparsedValue, VariableReferenceValue,
|
|
26
|
+
};
|
|
27
|
+
use crate::values::computed;
|
|
28
|
+
use crate::values::generics::calc::SortKey as AttrUnit;
|
|
29
|
+
use crate::values::specified::{NoCalcLength, ParsedNamespace};
|
|
30
|
+
use crate::{Atom, LocalName, Namespace, Prefix, derives::*};
|
|
31
|
+
use cssparser::{CowRcStr, Delimiter, Parser, SourcePosition, Token, TokenSerializationType};
|
|
32
|
+
use rustc_hash::FxHashMap;
|
|
33
|
+
use selectors::parser::SelectorParseErrorKind;
|
|
34
|
+
use servo_arc::Arc;
|
|
35
|
+
use smallvec::SmallVec;
|
|
36
|
+
use std::borrow::Cow;
|
|
37
|
+
use std::fmt::{self, Write};
|
|
38
|
+
use std::num;
|
|
39
|
+
use std::ops::{Index, IndexMut};
|
|
40
|
+
use style_traits::{CssString, CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
|
41
|
+
use thin_vec::ThinVec;
|
|
42
|
+
|
|
43
|
+
/// The environment from which to get `env` function values.
|
|
44
|
+
///
|
|
45
|
+
/// TODO(emilio): If this becomes a bit more complex we should probably move it
|
|
46
|
+
/// to the `media_queries` module, or something.
|
|
47
|
+
#[derive(Debug, MallocSizeOf)]
|
|
48
|
+
pub struct CssEnvironment;
|
|
49
|
+
|
|
50
|
+
type EnvironmentEvaluator = fn(device: &Device, url_data: &UrlExtraData) -> VariableValue;
|
|
51
|
+
|
|
52
|
+
struct EnvironmentVariable {
|
|
53
|
+
name: Atom,
|
|
54
|
+
evaluator: EnvironmentEvaluator,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
macro_rules! make_variable {
|
|
58
|
+
($name:expr, $evaluator:expr) => {{
|
|
59
|
+
EnvironmentVariable {
|
|
60
|
+
name: $name,
|
|
61
|
+
evaluator: $evaluator,
|
|
62
|
+
}
|
|
63
|
+
}};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fn get_safearea_inset_top(device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
67
|
+
VariableValue::pixels(device.safe_area_insets().top, url_data)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fn get_safearea_inset_bottom(device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
71
|
+
VariableValue::pixels(device.safe_area_insets().bottom, url_data)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn get_safearea_inset_left(device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
75
|
+
VariableValue::pixels(device.safe_area_insets().left, url_data)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fn get_safearea_inset_right(device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
79
|
+
VariableValue::pixels(device.safe_area_insets().right, url_data)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#[cfg(feature = "gecko")]
|
|
83
|
+
fn get_content_preferred_color_scheme(device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
84
|
+
use crate::queries::values::PrefersColorScheme;
|
|
85
|
+
let prefers_color_scheme = unsafe {
|
|
86
|
+
crate::gecko_bindings::bindings::Gecko_MediaFeatures_PrefersColorScheme(
|
|
87
|
+
device.document(),
|
|
88
|
+
/* use_content = */ true,
|
|
89
|
+
)
|
|
90
|
+
};
|
|
91
|
+
VariableValue::ident(
|
|
92
|
+
match prefers_color_scheme {
|
|
93
|
+
PrefersColorScheme::Light => "light",
|
|
94
|
+
PrefersColorScheme::Dark => "dark",
|
|
95
|
+
},
|
|
96
|
+
url_data,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[cfg(feature = "servo")]
|
|
101
|
+
fn get_content_preferred_color_scheme(_device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
102
|
+
// TODO: Add an implementation for Servo.
|
|
103
|
+
VariableValue::ident("light", url_data)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fn get_scrollbar_inline_size(device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
107
|
+
VariableValue::pixels(device.scrollbar_inline_size().px(), url_data)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fn get_hairline(device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
111
|
+
VariableValue::pixels(
|
|
112
|
+
app_units::Au(device.app_units_per_device_pixel()).to_f32_px(),
|
|
113
|
+
url_data,
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [
|
|
118
|
+
make_variable!(atom!("safe-area-inset-top"), get_safearea_inset_top),
|
|
119
|
+
make_variable!(atom!("safe-area-inset-bottom"), get_safearea_inset_bottom),
|
|
120
|
+
make_variable!(atom!("safe-area-inset-left"), get_safearea_inset_left),
|
|
121
|
+
make_variable!(atom!("safe-area-inset-right"), get_safearea_inset_right),
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
#[cfg(feature = "gecko")]
|
|
125
|
+
macro_rules! lnf_int {
|
|
126
|
+
($id:ident) => {
|
|
127
|
+
unsafe {
|
|
128
|
+
crate::gecko_bindings::bindings::Gecko_GetLookAndFeelInt(
|
|
129
|
+
crate::gecko_bindings::bindings::LookAndFeel_IntID::$id as i32,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#[cfg(feature = "servo")]
|
|
136
|
+
macro_rules! lnf_int {
|
|
137
|
+
($id:ident) => {
|
|
138
|
+
// TODO: Add an implementation for Servo.
|
|
139
|
+
0
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
macro_rules! lnf_int_variable {
|
|
144
|
+
($atom:expr, $id:ident, $ctor:ident) => {{
|
|
145
|
+
fn __eval(_: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
146
|
+
VariableValue::$ctor(lnf_int!($id), url_data)
|
|
147
|
+
}
|
|
148
|
+
make_variable!($atom, __eval)
|
|
149
|
+
}};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fn eval_gtk_csd_titlebar_radius(device: &Device, url_data: &UrlExtraData) -> VariableValue {
|
|
153
|
+
let int_pixels = lnf_int!(TitlebarRadius);
|
|
154
|
+
let unzoomed_scale =
|
|
155
|
+
device.device_pixel_ratio_ignoring_full_zoom().get() / device.device_pixel_ratio().get();
|
|
156
|
+
VariableValue::pixels(int_pixels as f32 * unzoomed_scale, url_data)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 9] = [
|
|
160
|
+
make_variable!(
|
|
161
|
+
atom!("-moz-gtk-csd-titlebar-radius"),
|
|
162
|
+
eval_gtk_csd_titlebar_radius
|
|
163
|
+
),
|
|
164
|
+
lnf_int_variable!(
|
|
165
|
+
atom!("-moz-gtk-csd-tooltip-radius"),
|
|
166
|
+
TooltipRadius,
|
|
167
|
+
int_pixels
|
|
168
|
+
),
|
|
169
|
+
lnf_int_variable!(
|
|
170
|
+
atom!("-moz-gtk-csd-close-button-position"),
|
|
171
|
+
GTKCSDCloseButtonPosition,
|
|
172
|
+
integer
|
|
173
|
+
),
|
|
174
|
+
lnf_int_variable!(
|
|
175
|
+
atom!("-moz-gtk-csd-minimize-button-position"),
|
|
176
|
+
GTKCSDMinimizeButtonPosition,
|
|
177
|
+
integer
|
|
178
|
+
),
|
|
179
|
+
lnf_int_variable!(
|
|
180
|
+
atom!("-moz-gtk-csd-maximize-button-position"),
|
|
181
|
+
GTKCSDMaximizeButtonPosition,
|
|
182
|
+
integer
|
|
183
|
+
),
|
|
184
|
+
lnf_int_variable!(
|
|
185
|
+
atom!("-moz-overlay-scrollbar-fade-duration"),
|
|
186
|
+
ScrollbarFadeDuration,
|
|
187
|
+
int_ms
|
|
188
|
+
),
|
|
189
|
+
make_variable!(
|
|
190
|
+
atom!("-moz-content-preferred-color-scheme"),
|
|
191
|
+
get_content_preferred_color_scheme
|
|
192
|
+
),
|
|
193
|
+
make_variable!(atom!("scrollbar-inline-size"), get_scrollbar_inline_size),
|
|
194
|
+
make_variable!(atom!("hairline"), get_hairline),
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
impl CssEnvironment {
|
|
198
|
+
/// Get an env() variable, either custom or not.
|
|
199
|
+
#[inline]
|
|
200
|
+
pub fn get(
|
|
201
|
+
&self,
|
|
202
|
+
name: &Atom,
|
|
203
|
+
device: &Device,
|
|
204
|
+
url_data: &UrlExtraData,
|
|
205
|
+
) -> Option<VariableValue> {
|
|
206
|
+
#[cfg(feature = "gecko")]
|
|
207
|
+
let is_link_parameter = name.as_slice().starts_with(&[b'-' as u16, b'-' as u16]);
|
|
208
|
+
#[cfg(feature = "servo")]
|
|
209
|
+
let is_link_parameter = name.starts_with("--");
|
|
210
|
+
if is_link_parameter {
|
|
211
|
+
let param = device
|
|
212
|
+
.link_parameters()?
|
|
213
|
+
.0
|
|
214
|
+
.iter()
|
|
215
|
+
.find(|p| p.name.0 == *name)?;
|
|
216
|
+
let mut parser = cssparser::Parser::new(param.value.0.as_ref());
|
|
217
|
+
// need to carry around full variable value https://bugzilla.mozilla.org/show_bug.cgi?id=2028998
|
|
218
|
+
return VariableValue::parse(&mut parser, None, url_data).ok();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if let Some(var) = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name) {
|
|
222
|
+
return Some((var.evaluator)(device, url_data));
|
|
223
|
+
}
|
|
224
|
+
if !url_data.chrome_rules_enabled() {
|
|
225
|
+
return None;
|
|
226
|
+
}
|
|
227
|
+
let var = CHROME_ENVIRONMENT_VARIABLES
|
|
228
|
+
.iter()
|
|
229
|
+
.find(|var| var.name == *name)?;
|
|
230
|
+
Some((var.evaluator)(device, url_data))
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/// A custom property name is just an `Atom`.
|
|
235
|
+
///
|
|
236
|
+
/// Note that this does not include the `--` prefix
|
|
237
|
+
pub type Name = Atom;
|
|
238
|
+
|
|
239
|
+
impl LocalName {
|
|
240
|
+
#[cfg(feature = "gecko")]
|
|
241
|
+
fn with_name<R>(name: &Name, callback: impl FnOnce(&Self) -> R) -> R {
|
|
242
|
+
callback(Self::cast(name))
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
#[cfg(feature = "servo")]
|
|
246
|
+
fn with_name<'a, R>(name: &'a Name, callback: impl FnOnce(&Self) -> R) -> R {
|
|
247
|
+
callback(&name.as_str().into())
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
impl From<Name> for LocalName {
|
|
252
|
+
#[cfg(feature = "gecko")]
|
|
253
|
+
fn from(name: Name) -> Self {
|
|
254
|
+
Self::new(name)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#[cfg(feature = "servo")]
|
|
258
|
+
fn from(name: Name) -> Self {
|
|
259
|
+
name.as_str().into()
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/// Parse a custom property name.
|
|
264
|
+
///
|
|
265
|
+
/// <https://drafts.csswg.org/css-variables/#typedef-custom-property-name>
|
|
266
|
+
pub fn parse_name(s: &str) -> Result<&str, ()> {
|
|
267
|
+
if s.starts_with("--") && s.len() > 2 {
|
|
268
|
+
Ok(&s[2..])
|
|
269
|
+
} else {
|
|
270
|
+
Err(())
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/// A value for a custom property is just a set of tokens.
|
|
275
|
+
///
|
|
276
|
+
/// We preserve the original CSS for serialization, and also the variable
|
|
277
|
+
/// references to other custom property names.
|
|
278
|
+
#[derive(Clone, Debug, MallocSizeOf, ToShmem)]
|
|
279
|
+
pub struct VariableValue {
|
|
280
|
+
/// The raw CSS string.
|
|
281
|
+
pub css: String,
|
|
282
|
+
|
|
283
|
+
/// The url data of the stylesheet where this value came from.
|
|
284
|
+
pub url_data: UrlExtraData,
|
|
285
|
+
|
|
286
|
+
first_token_type: TokenSerializationType,
|
|
287
|
+
last_token_type: TokenSerializationType,
|
|
288
|
+
|
|
289
|
+
/// var(), env(), attr() or non-custom property (e.g. through `em`) references.
|
|
290
|
+
pub references: References,
|
|
291
|
+
|
|
292
|
+
/// Was this variable value attr tainted before. Used to keep attr tainting
|
|
293
|
+
/// information when uncomputing a style that needs to be put back into the
|
|
294
|
+
/// cascade.
|
|
295
|
+
pub explicitly_attr_tainted: bool,
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
trivial_to_computed_value!(VariableValue);
|
|
299
|
+
|
|
300
|
+
/// Given a potentially registered variable value turn it into a computed custom property value.
|
|
301
|
+
pub(crate) fn compute_variable_value(
|
|
302
|
+
value: &Arc<VariableValue>,
|
|
303
|
+
registration: &PropertyDescriptors,
|
|
304
|
+
computed_context: &computed::Context,
|
|
305
|
+
) -> Option<ComputedRegisteredValue> {
|
|
306
|
+
if registration.is_universal() {
|
|
307
|
+
return Some(ComputedRegisteredValue::universal(Arc::clone(value)));
|
|
308
|
+
}
|
|
309
|
+
compute_value(
|
|
310
|
+
&value.css,
|
|
311
|
+
&value.url_data,
|
|
312
|
+
registration,
|
|
313
|
+
computed_context,
|
|
314
|
+
AttrTaint::default(),
|
|
315
|
+
)
|
|
316
|
+
.ok()
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// For all purposes, we want values to be considered equal if their css text is equal.
|
|
320
|
+
impl PartialEq for VariableValue {
|
|
321
|
+
fn eq(&self, other: &Self) -> bool {
|
|
322
|
+
self.css == other.css
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
impl Eq for VariableValue {}
|
|
327
|
+
|
|
328
|
+
impl ToCss for SpecifiedValue {
|
|
329
|
+
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
330
|
+
where
|
|
331
|
+
W: Write,
|
|
332
|
+
{
|
|
333
|
+
dest.write_str(&self.css)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
impl ToTyped for SpecifiedValue {
|
|
338
|
+
fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
|
|
339
|
+
let unparsed_value = reify_variable_value(self)?;
|
|
340
|
+
dest.push(TypedValue::Unparsed(unparsed_value));
|
|
341
|
+
Ok(())
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
fn reify_variable_value(value: &VariableValue) -> Result<UnparsedValue, ()> {
|
|
346
|
+
let mut reference_index = 0;
|
|
347
|
+
reify_variable_value_range(
|
|
348
|
+
&value.css,
|
|
349
|
+
&value.references.refs,
|
|
350
|
+
&mut reference_index,
|
|
351
|
+
0,
|
|
352
|
+
value.css.len(),
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/// Reify a slice of the CSS string into UnparsedSegment entries.
|
|
357
|
+
///
|
|
358
|
+
/// References are stored in source order, with outer substitution functions
|
|
359
|
+
/// inserted before references in their fallback. The shared `reference_index`
|
|
360
|
+
/// relies on this ordering to recurse into fallbacks without reprocessing
|
|
361
|
+
/// nested referecences.
|
|
362
|
+
fn reify_variable_value_range(
|
|
363
|
+
css: &str,
|
|
364
|
+
references: &[SubstitutionFunctionReference],
|
|
365
|
+
reference_index: &mut usize,
|
|
366
|
+
start: usize,
|
|
367
|
+
end: usize,
|
|
368
|
+
) -> Result<UnparsedValue, ()> {
|
|
369
|
+
debug_assert!(start <= end);
|
|
370
|
+
debug_assert!(end <= css.len());
|
|
371
|
+
|
|
372
|
+
let mut values = ThinVec::new();
|
|
373
|
+
let mut cur_pos = start;
|
|
374
|
+
|
|
375
|
+
while *reference_index < references.len() {
|
|
376
|
+
let reference = &references[*reference_index];
|
|
377
|
+
|
|
378
|
+
if reference.start >= end {
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
debug_assert!(reference.start >= cur_pos);
|
|
383
|
+
debug_assert!(reference.start <= reference.end);
|
|
384
|
+
debug_assert!(reference.end <= css.len());
|
|
385
|
+
|
|
386
|
+
if cur_pos < reference.start {
|
|
387
|
+
values.push(UnparsedSegment::String(CssString::from(
|
|
388
|
+
&css[cur_pos..reference.start],
|
|
389
|
+
)));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
*reference_index += 1;
|
|
393
|
+
|
|
394
|
+
if reference.substitution_kind != SubstitutionFunctionKind::Var {
|
|
395
|
+
return Err(());
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
let (fallback, has_fallback) = if let Some(fallback) = &reference.fallback {
|
|
399
|
+
debug_assert!(fallback.start.get() < reference.end);
|
|
400
|
+
|
|
401
|
+
(
|
|
402
|
+
reify_variable_value_range(
|
|
403
|
+
css,
|
|
404
|
+
references,
|
|
405
|
+
reference_index,
|
|
406
|
+
fallback.start.get(),
|
|
407
|
+
reference.end - 1, // Skip the closing ')'.
|
|
408
|
+
)?,
|
|
409
|
+
true,
|
|
410
|
+
)
|
|
411
|
+
} else {
|
|
412
|
+
(ThinVec::new(), false)
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
values.push(UnparsedSegment::VariableReference(VariableReferenceValue {
|
|
416
|
+
variable: CssString::from(format!("--{}", reference.name)),
|
|
417
|
+
fallback,
|
|
418
|
+
has_fallback,
|
|
419
|
+
}));
|
|
420
|
+
|
|
421
|
+
cur_pos = reference.end;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if cur_pos < end {
|
|
425
|
+
values.push(UnparsedSegment::String(CssString::from(&css[cur_pos..end])));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
Ok(values)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/// A pair of separate CustomPropertiesMaps, split between custom properties
|
|
432
|
+
/// that have the inherit flag set and those with the flag unset.
|
|
433
|
+
#[repr(C)]
|
|
434
|
+
#[derive(Clone, Debug, Default, PartialEq)]
|
|
435
|
+
pub struct ComputedCustomProperties {
|
|
436
|
+
/// Map for custom properties with inherit flag set, including non-registered
|
|
437
|
+
/// ones.
|
|
438
|
+
pub inherited: CustomPropertiesMap,
|
|
439
|
+
/// Map for custom properties with inherit flag unset.
|
|
440
|
+
pub non_inherited: CustomPropertiesMap,
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
impl ComputedCustomProperties {
|
|
444
|
+
/// Return whether the inherited and non_inherited maps are none.
|
|
445
|
+
pub fn is_empty(&self) -> bool {
|
|
446
|
+
self.inherited.is_empty() && self.non_inherited.is_empty()
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/// Return the name and value of the property at specified index, if any.
|
|
450
|
+
pub fn property_at(&self, index: usize) -> Option<(&Name, &Option<ComputedRegisteredValue>)> {
|
|
451
|
+
// Just expose the custom property items from custom_properties.inherited, followed
|
|
452
|
+
// by custom property items from custom_properties.non_inherited.
|
|
453
|
+
self.inherited
|
|
454
|
+
.get_index(index)
|
|
455
|
+
.or_else(|| self.non_inherited.get_index(index - self.inherited.len()))
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/// Insert a custom property in the corresponding inherited/non_inherited
|
|
459
|
+
/// map, depending on whether the inherit flag is set or unset.
|
|
460
|
+
pub fn insert(
|
|
461
|
+
&mut self,
|
|
462
|
+
registration: &PropertyDescriptors,
|
|
463
|
+
name: &Name,
|
|
464
|
+
value: ComputedRegisteredValue,
|
|
465
|
+
) {
|
|
466
|
+
self.map_mut(registration).insert(name, value)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/// Remove a custom property from the corresponding inherited/non_inherited
|
|
470
|
+
/// map, depending on whether the inherit flag is set or unset.
|
|
471
|
+
pub fn remove(&mut self, registration: &PropertyDescriptors, name: &Name) {
|
|
472
|
+
self.map_mut(registration).remove(name);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/// Shrink the capacity of the inherited maps as much as possible.
|
|
476
|
+
pub fn shrink_to_fit(&mut self) {
|
|
477
|
+
self.inherited.shrink_to_fit();
|
|
478
|
+
self.non_inherited.shrink_to_fit();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
fn map_mut(&mut self, registration: &PropertyDescriptors) -> &mut CustomPropertiesMap {
|
|
482
|
+
if registration.inherits() {
|
|
483
|
+
&mut self.inherited
|
|
484
|
+
} else {
|
|
485
|
+
&mut self.non_inherited
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/// Returns the relevant custom property value given a registration.
|
|
490
|
+
pub fn get(
|
|
491
|
+
&self,
|
|
492
|
+
registration: &PropertyDescriptors,
|
|
493
|
+
name: &Name,
|
|
494
|
+
) -> Option<&ComputedRegisteredValue> {
|
|
495
|
+
if registration.inherits() {
|
|
496
|
+
self.inherited.get(name)
|
|
497
|
+
} else {
|
|
498
|
+
self.non_inherited.get(name)
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/// Returns a property just by the name. Slightly less efficient than get(), if you already have
|
|
503
|
+
/// a custom registration handy, thus the longer name.
|
|
504
|
+
pub fn get_for_cssom(&self, name: &Name) -> Option<&ComputedRegisteredValue> {
|
|
505
|
+
self.inherited
|
|
506
|
+
.get(name)
|
|
507
|
+
.or_else(|| self.non_inherited.get(name))
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/// Both specified and computed values are VariableValues, the difference is
|
|
512
|
+
/// whether var() functions are expanded.
|
|
513
|
+
pub type SpecifiedValue = VariableValue;
|
|
514
|
+
/// Both specified and computed values are VariableValues, the difference is
|
|
515
|
+
/// whether var() functions are expanded.
|
|
516
|
+
pub type ComputedValue = VariableValue;
|
|
517
|
+
|
|
518
|
+
/// Set of flags to references this custom property makes.
|
|
519
|
+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, MallocSizeOf, ToShmem)]
|
|
520
|
+
pub struct ReferenceFlags(u8);
|
|
521
|
+
|
|
522
|
+
bitflags! {
|
|
523
|
+
impl ReferenceFlags : u8 {
|
|
524
|
+
/// At least one custom property depends on font-relative units.
|
|
525
|
+
const FONT_UNITS = 1 << 0;
|
|
526
|
+
/// At least one custom property depends on root element's font-relative units.
|
|
527
|
+
const ROOT_FONT_UNITS = 1 << 1;
|
|
528
|
+
/// At least one custom property depends on line height units.
|
|
529
|
+
const LH_UNITS = 1 << 2;
|
|
530
|
+
/// At least one custom property depends on root element's line height units.
|
|
531
|
+
const ROOT_LH_UNITS = 1 << 3;
|
|
532
|
+
/// The value depends on the used color-scheme (e.g. a registered `<color>` property).
|
|
533
|
+
const COLOR_SCHEME = 1 << 4;
|
|
534
|
+
/// All dependencies not depending on the root element.
|
|
535
|
+
const NON_ROOT_DEPENDENCIES = Self::FONT_UNITS.0 | Self::LH_UNITS.0;
|
|
536
|
+
/// All dependencies depending on the root element.
|
|
537
|
+
const ROOT_DEPENDENCIES = Self::ROOT_FONT_UNITS.0 | Self::ROOT_LH_UNITS.0;
|
|
538
|
+
/// All non-custom dependencies
|
|
539
|
+
const NON_CUSTOM = Self::NON_ROOT_DEPENDENCIES.0 | Self::ROOT_DEPENDENCIES.0;
|
|
540
|
+
/// At least one attr() reference.
|
|
541
|
+
const ATTR = 1 << 5;
|
|
542
|
+
/// At least one env() reference.
|
|
543
|
+
const ENV = 1 << 6;
|
|
544
|
+
/// At least one var() reference.
|
|
545
|
+
const VAR = 1 << 7;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
impl ReferenceFlags {
|
|
550
|
+
/// Iterate for each non custom
|
|
551
|
+
pub fn for_each_non_custom<F>(mut self, is_root_element: bool, mut f: F)
|
|
552
|
+
where
|
|
553
|
+
F: FnMut(SingleNonCustomReference),
|
|
554
|
+
{
|
|
555
|
+
// On the root element, rem etc refer to the root's own dependencies.
|
|
556
|
+
if is_root_element {
|
|
557
|
+
if self.intersects(Self::ROOT_FONT_UNITS) {
|
|
558
|
+
self.remove(Self::ROOT_FONT_UNITS);
|
|
559
|
+
self |= Self::FONT_UNITS;
|
|
560
|
+
}
|
|
561
|
+
if self.intersects(Self::ROOT_LH_UNITS) {
|
|
562
|
+
self.remove(Self::ROOT_FONT_UNITS);
|
|
563
|
+
self |= Self::LH_UNITS;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
for (_, r) in self.iter_names() {
|
|
568
|
+
let single = match r {
|
|
569
|
+
Self::FONT_UNITS => SingleNonCustomReference::FontUnits,
|
|
570
|
+
Self::LH_UNITS => SingleNonCustomReference::LhUnits,
|
|
571
|
+
Self::COLOR_SCHEME => SingleNonCustomReference::ColorScheme,
|
|
572
|
+
Self::ROOT_FONT_UNITS
|
|
573
|
+
| Self::ROOT_LH_UNITS
|
|
574
|
+
| Self::VAR
|
|
575
|
+
| Self::ENV
|
|
576
|
+
| Self::ATTR => continue,
|
|
577
|
+
_ => unreachable!("Unexpected single bit value"),
|
|
578
|
+
};
|
|
579
|
+
f(single);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
fn from_unit(value: &CowRcStr) -> Self {
|
|
584
|
+
// For registered properties, any reference to font-relative dimensions
|
|
585
|
+
// make it dependent on font-related properties.
|
|
586
|
+
// TODO(dshin): When we unit algebra gets implemented and handled -
|
|
587
|
+
// Is it valid to say that `calc(1em / 2em * 3px)` triggers this?
|
|
588
|
+
if value.eq_ignore_ascii_case(NoCalcLength::LH) {
|
|
589
|
+
return Self::FONT_UNITS | Self::LH_UNITS;
|
|
590
|
+
}
|
|
591
|
+
if value.eq_ignore_ascii_case(NoCalcLength::EM)
|
|
592
|
+
|| value.eq_ignore_ascii_case(NoCalcLength::EX)
|
|
593
|
+
|| value.eq_ignore_ascii_case(NoCalcLength::CAP)
|
|
594
|
+
|| value.eq_ignore_ascii_case(NoCalcLength::CH)
|
|
595
|
+
|| value.eq_ignore_ascii_case(NoCalcLength::IC)
|
|
596
|
+
{
|
|
597
|
+
return Self::FONT_UNITS;
|
|
598
|
+
}
|
|
599
|
+
if value.eq_ignore_ascii_case(NoCalcLength::RLH) {
|
|
600
|
+
return Self::ROOT_FONT_UNITS | Self::ROOT_LH_UNITS;
|
|
601
|
+
}
|
|
602
|
+
if value.eq_ignore_ascii_case(NoCalcLength::REM)
|
|
603
|
+
|| value.eq_ignore_ascii_case(NoCalcLength::REX)
|
|
604
|
+
|| value.eq_ignore_ascii_case(NoCalcLength::RCH)
|
|
605
|
+
|| value.eq_ignore_ascii_case(NoCalcLength::RCAP)
|
|
606
|
+
|| value.eq_ignore_ascii_case(NoCalcLength::RIC)
|
|
607
|
+
{
|
|
608
|
+
return Self::ROOT_FONT_UNITS;
|
|
609
|
+
}
|
|
610
|
+
Self::empty()
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/// A non-custom reference that participates in cycle resolution.
|
|
615
|
+
/// TODO(emilio): This should probably eventually become just PrioritaryPropertyId.
|
|
616
|
+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
617
|
+
#[allow(missing_docs)]
|
|
618
|
+
pub enum SingleNonCustomReference {
|
|
619
|
+
FontUnits = 0,
|
|
620
|
+
LhUnits,
|
|
621
|
+
ColorScheme,
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
impl SingleNonCustomReference {
|
|
625
|
+
/// Returns a prioritary id for this reference.
|
|
626
|
+
pub fn to_prioritary_id(self) -> PrioritaryPropertyId {
|
|
627
|
+
match self {
|
|
628
|
+
Self::FontUnits => PrioritaryPropertyId::FontSize,
|
|
629
|
+
Self::LhUnits => PrioritaryPropertyId::LineHeight,
|
|
630
|
+
Self::ColorScheme => PrioritaryPropertyId::ColorScheme,
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/// A map from NonCustomReferenceMap to a T.
|
|
636
|
+
pub struct NonCustomReferenceMap<T>([Option<T>; 3]);
|
|
637
|
+
|
|
638
|
+
impl<T> Default for NonCustomReferenceMap<T> {
|
|
639
|
+
fn default() -> Self {
|
|
640
|
+
NonCustomReferenceMap(Default::default())
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
impl<T> Index<SingleNonCustomReference> for NonCustomReferenceMap<T> {
|
|
645
|
+
type Output = Option<T>;
|
|
646
|
+
|
|
647
|
+
fn index(&self, reference: SingleNonCustomReference) -> &Self::Output {
|
|
648
|
+
&self.0[reference as usize]
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
impl<T> IndexMut<SingleNonCustomReference> for NonCustomReferenceMap<T> {
|
|
653
|
+
fn index_mut(&mut self, reference: SingleNonCustomReference) -> &mut Self::Output {
|
|
654
|
+
&mut self.0[reference as usize]
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/// Substitution function source: var, env, attr.
|
|
659
|
+
#[derive(Copy, Clone, Debug, MallocSizeOf, Hash, Eq, PartialEq, ToShmem, Parse)]
|
|
660
|
+
pub enum SubstitutionFunctionKind {
|
|
661
|
+
/// CSS variable / custom property
|
|
662
|
+
Var,
|
|
663
|
+
/// Environment variable
|
|
664
|
+
Env,
|
|
665
|
+
/// DOM attribute
|
|
666
|
+
Attr,
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/// A wrapper map that encapsulates both the custom properties and attributes
|
|
670
|
+
/// for a given element.
|
|
671
|
+
#[repr(C)]
|
|
672
|
+
#[derive(Clone, Debug, Default, PartialEq)]
|
|
673
|
+
pub struct ComputedSubstitutionFunctions {
|
|
674
|
+
/// The applicable custom properties (includes inherited and non-inherited).
|
|
675
|
+
pub custom_properties: ComputedCustomProperties,
|
|
676
|
+
/// The applicable DOM attributes.
|
|
677
|
+
pub attributes: OwnMap,
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
impl ComputedSubstitutionFunctions {
|
|
681
|
+
/// Creates a substitution function map from optional custom properties
|
|
682
|
+
/// and DOM attributes.
|
|
683
|
+
#[inline(always)]
|
|
684
|
+
pub fn new(
|
|
685
|
+
custom_properties: Option<ComputedCustomProperties>,
|
|
686
|
+
attributes: Option<OwnMap>,
|
|
687
|
+
) -> Self {
|
|
688
|
+
Self {
|
|
689
|
+
custom_properties: custom_properties.unwrap_or_default(),
|
|
690
|
+
attributes: attributes.unwrap_or_default(),
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
#[inline(always)]
|
|
695
|
+
pub(crate) fn insert_var(
|
|
696
|
+
&mut self,
|
|
697
|
+
registration: &PropertyDescriptors,
|
|
698
|
+
name: &Name,
|
|
699
|
+
value: ComputedRegisteredValue,
|
|
700
|
+
) {
|
|
701
|
+
self.custom_properties.insert(registration, name, value);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
#[inline(always)]
|
|
705
|
+
pub(crate) fn insert_attr(&mut self, name: &Name, value: ComputedRegisteredValue) {
|
|
706
|
+
self.attributes.insert(name.clone(), Some(value));
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
#[inline(always)]
|
|
710
|
+
pub(crate) fn remove_var(&mut self, registration: &PropertyDescriptors, name: &Name) {
|
|
711
|
+
self.custom_properties.remove(registration, name);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
#[inline(always)]
|
|
715
|
+
pub(crate) fn remove_attr(&mut self, name: &Name) {
|
|
716
|
+
self.attributes.insert(name.clone(), None);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
#[inline(always)]
|
|
720
|
+
pub(crate) fn get_var(
|
|
721
|
+
&self,
|
|
722
|
+
registration: &PropertyDescriptors,
|
|
723
|
+
name: &Name,
|
|
724
|
+
) -> Option<&ComputedRegisteredValue> {
|
|
725
|
+
self.custom_properties.get(registration, name)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
#[inline(always)]
|
|
729
|
+
pub(crate) fn get_attr(&self, name: &Name) -> Option<&ComputedRegisteredValue> {
|
|
730
|
+
self.attributes.get(name).and_then(|p| p.as_ref())
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem, Parse)]
|
|
735
|
+
enum AttributeType {
|
|
736
|
+
Invalid,
|
|
737
|
+
None,
|
|
738
|
+
RawString,
|
|
739
|
+
Type(SyntaxDescriptor),
|
|
740
|
+
Unit(AttrUnit),
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/// Data specific to an attr() call like type and namespace.
|
|
744
|
+
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
|
745
|
+
pub struct AttributeData {
|
|
746
|
+
kind: AttributeType,
|
|
747
|
+
namespace: ParsedNamespace,
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/// For a CSS string, the range, counted in bytes, that is attr()-tainted.
|
|
751
|
+
#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem, ToComputedValue)]
|
|
752
|
+
pub struct AttrTaintedRange {
|
|
753
|
+
/// Start of the range, counted in bytes. Inclusive.
|
|
754
|
+
start: usize,
|
|
755
|
+
/// End of the range, counted in bytes. Exclusive.
|
|
756
|
+
end: usize,
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
impl AttrTaintedRange {
|
|
760
|
+
/// Creates a range within a CSS string that is tainted by attr().
|
|
761
|
+
#[inline(always)]
|
|
762
|
+
pub fn new(start: usize, end: usize) -> Self {
|
|
763
|
+
debug_assert!(start <= end);
|
|
764
|
+
Self { start, end }
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/// In CSS Values and Units, values produced by `attr()` are considered attr()-tainted, as are
|
|
769
|
+
/// functions that contain an attr()-tainted value. Using an attr()-tainted value as or in a <url>
|
|
770
|
+
/// makes a declaration invalid at computed-value time.
|
|
771
|
+
/// https://drafts.csswg.org/css-values-5/#attr-security
|
|
772
|
+
#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
|
|
773
|
+
pub struct AttrTaint(SmallVec<[AttrTaintedRange; 1]>);
|
|
774
|
+
|
|
775
|
+
impl AttrTaint {
|
|
776
|
+
/// For a CSS string, determine whether any `<url>` overlapping this `range`
|
|
777
|
+
/// is disallowed due to attr()-tainting.
|
|
778
|
+
#[inline(always)]
|
|
779
|
+
pub fn should_disallow_urls_in_range(&self, range: &AttrTaintedRange) -> bool {
|
|
780
|
+
self.0
|
|
781
|
+
.iter()
|
|
782
|
+
.any(|r| r.start <= range.end && r.end >= range.start)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/// Returns true if the attr()-tainted range contains no elements.
|
|
786
|
+
#[inline(always)]
|
|
787
|
+
pub fn is_empty(&self) -> bool {
|
|
788
|
+
self.0.is_empty()
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
#[inline(always)]
|
|
792
|
+
fn new_fully_tainted(end: usize) -> Self {
|
|
793
|
+
let mut taint = Self::default();
|
|
794
|
+
taint.push(0, end);
|
|
795
|
+
taint
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
#[inline(always)]
|
|
799
|
+
fn push(&mut self, start: usize, end: usize) {
|
|
800
|
+
self.0.push(AttrTaintedRange::new(start, end));
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/// The fallback of a particular value.
|
|
805
|
+
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
|
806
|
+
pub struct VariableFallback {
|
|
807
|
+
// NOTE(emilio): We don't track fallback end, because we rely on the missing closing
|
|
808
|
+
// parenthesis, if any, to be inserted, which means that we can rely on our end being
|
|
809
|
+
// reference.end - 1.
|
|
810
|
+
start: num::NonZeroUsize,
|
|
811
|
+
first_token_type: TokenSerializationType,
|
|
812
|
+
last_token_type: TokenSerializationType,
|
|
813
|
+
/// References from this fallback value.
|
|
814
|
+
pub references: References,
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/// A reference to a substitution function like env() / var() / attr().
|
|
818
|
+
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
|
819
|
+
pub struct SubstitutionFunctionReference {
|
|
820
|
+
/// The identifier for this substitution function (the first argument).
|
|
821
|
+
pub name: Name,
|
|
822
|
+
start: usize,
|
|
823
|
+
end: usize,
|
|
824
|
+
/// The fallback for this function, if any.
|
|
825
|
+
pub fallback: Option<VariableFallback>,
|
|
826
|
+
/// attr() specific data, only relevant if substitution_kind is `Attr`..
|
|
827
|
+
pub attribute_data: AttributeData,
|
|
828
|
+
prev_token_type: TokenSerializationType,
|
|
829
|
+
next_token_type: TokenSerializationType,
|
|
830
|
+
/// The kind of substitution function we are.
|
|
831
|
+
pub substitution_kind: SubstitutionFunctionKind,
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
impl SubstitutionFunctionReference {
|
|
835
|
+
/// Whether we're an attr(... type(...)) reference.
|
|
836
|
+
pub fn is_attr_with_type(&self) -> bool {
|
|
837
|
+
self.substitution_kind == SubstitutionFunctionKind::Attr
|
|
838
|
+
&& matches!(self.attribute_data.kind, AttributeType::Type(..))
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/// A struct holding information about the external references to that a custom property value may
|
|
843
|
+
/// have.
|
|
844
|
+
#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
|
|
845
|
+
pub struct References {
|
|
846
|
+
/// The actual list of references that we end up with.
|
|
847
|
+
pub refs: Vec<SubstitutionFunctionReference>,
|
|
848
|
+
/// Various flags about the kind of references we hold. Note that these include data about our
|
|
849
|
+
/// nested references.
|
|
850
|
+
///
|
|
851
|
+
/// TODO(emilio): Do we need to distinguish between own and nested flags?
|
|
852
|
+
pub flags: ReferenceFlags,
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
impl References {
|
|
856
|
+
fn has_references(&self) -> bool {
|
|
857
|
+
!self.refs.is_empty()
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
pub(crate) fn non_custom_references(&self, is_root_element: bool) -> ReferenceFlags {
|
|
861
|
+
let mut mask = ReferenceFlags::NON_ROOT_DEPENDENCIES;
|
|
862
|
+
if is_root_element {
|
|
863
|
+
mask |= ReferenceFlags::ROOT_DEPENDENCIES
|
|
864
|
+
}
|
|
865
|
+
self.flags & mask
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
impl VariableValue {
|
|
870
|
+
fn empty(url_data: &UrlExtraData) -> Self {
|
|
871
|
+
Self {
|
|
872
|
+
css: String::new(),
|
|
873
|
+
last_token_type: Default::default(),
|
|
874
|
+
first_token_type: Default::default(),
|
|
875
|
+
url_data: url_data.clone(),
|
|
876
|
+
references: Default::default(),
|
|
877
|
+
explicitly_attr_tainted: false,
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/// Create a new custom property without parsing if the CSS is known to be valid and contain no
|
|
882
|
+
/// references.
|
|
883
|
+
pub fn new(
|
|
884
|
+
css: String,
|
|
885
|
+
url_data: &UrlExtraData,
|
|
886
|
+
first_token_type: TokenSerializationType,
|
|
887
|
+
last_token_type: TokenSerializationType,
|
|
888
|
+
) -> Self {
|
|
889
|
+
Self {
|
|
890
|
+
css,
|
|
891
|
+
url_data: url_data.clone(),
|
|
892
|
+
first_token_type,
|
|
893
|
+
last_token_type,
|
|
894
|
+
references: References::default(),
|
|
895
|
+
explicitly_attr_tainted: false,
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
fn push(
|
|
900
|
+
&mut self,
|
|
901
|
+
css: &str,
|
|
902
|
+
css_first_token_type: TokenSerializationType,
|
|
903
|
+
css_last_token_type: TokenSerializationType,
|
|
904
|
+
attr_taint: Option<&mut AttrTaint>,
|
|
905
|
+
) -> Result<(), ()> {
|
|
906
|
+
/// Prevent values from getting terribly big since you can use custom
|
|
907
|
+
/// properties exponentially.
|
|
908
|
+
///
|
|
909
|
+
/// This number (2MB) is somewhat arbitrary, but silly enough that no
|
|
910
|
+
/// reasonable page should hit it. We could limit by number of total
|
|
911
|
+
/// substitutions, but that was very easy to work around in practice
|
|
912
|
+
/// (just choose a larger initial value and boom).
|
|
913
|
+
const MAX_VALUE_LENGTH_IN_BYTES: usize = 2 * 1024 * 1024;
|
|
914
|
+
|
|
915
|
+
if self.css.len() + css.len() > MAX_VALUE_LENGTH_IN_BYTES {
|
|
916
|
+
return Err(());
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// This happens e.g. between two subsequent var() functions:
|
|
920
|
+
// `var(--a)var(--b)`.
|
|
921
|
+
//
|
|
922
|
+
// In that case, css_*_token_type is nonsensical.
|
|
923
|
+
if css.is_empty() {
|
|
924
|
+
return Ok(());
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
self.first_token_type.set_if_nothing(css_first_token_type);
|
|
928
|
+
// If self.first_token_type was nothing,
|
|
929
|
+
// self.last_token_type is also nothing and this will be false:
|
|
930
|
+
if self
|
|
931
|
+
.last_token_type
|
|
932
|
+
.needs_separator_when_before(css_first_token_type)
|
|
933
|
+
{
|
|
934
|
+
self.css.push_str("/**/")
|
|
935
|
+
}
|
|
936
|
+
let start = self.css.len();
|
|
937
|
+
self.css.push_str(css);
|
|
938
|
+
let end = self.css.len();
|
|
939
|
+
if let Some(taint) = attr_taint {
|
|
940
|
+
taint.push(start, end);
|
|
941
|
+
}
|
|
942
|
+
self.last_token_type = css_last_token_type;
|
|
943
|
+
Ok(())
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/// Parse a custom property value.
|
|
947
|
+
pub fn parse(
|
|
948
|
+
input: &mut Parser,
|
|
949
|
+
namespaces: Option<&FxHashMap<Prefix, Namespace>>,
|
|
950
|
+
url_data: &UrlExtraData,
|
|
951
|
+
) -> Result<Self, ParseError> {
|
|
952
|
+
let mut references = References::default();
|
|
953
|
+
let mut missing_closing_characters = String::new();
|
|
954
|
+
let start_position = input.position();
|
|
955
|
+
let (first_token_type, last_token_type) = parse_declaration_value(
|
|
956
|
+
input,
|
|
957
|
+
start_position,
|
|
958
|
+
namespaces,
|
|
959
|
+
&mut references,
|
|
960
|
+
&mut missing_closing_characters,
|
|
961
|
+
)?;
|
|
962
|
+
let mut css = input
|
|
963
|
+
.slice_from(start_position)
|
|
964
|
+
.trim_ascii_start()
|
|
965
|
+
.to_owned();
|
|
966
|
+
if !missing_closing_characters.is_empty() {
|
|
967
|
+
// Unescaped backslash at EOF in a quoted string is ignored.
|
|
968
|
+
if css.ends_with("\\")
|
|
969
|
+
&& matches!(missing_closing_characters.as_bytes()[0], b'"' | b'\'')
|
|
970
|
+
{
|
|
971
|
+
css.pop();
|
|
972
|
+
}
|
|
973
|
+
css.push_str(&missing_closing_characters);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
css.truncate(css.trim_ascii_end().len());
|
|
977
|
+
css.shrink_to_fit();
|
|
978
|
+
references.refs.shrink_to_fit();
|
|
979
|
+
|
|
980
|
+
Ok(Self {
|
|
981
|
+
css,
|
|
982
|
+
url_data: url_data.clone(),
|
|
983
|
+
first_token_type,
|
|
984
|
+
last_token_type,
|
|
985
|
+
references,
|
|
986
|
+
explicitly_attr_tainted: false,
|
|
987
|
+
})
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/// Returns whether this value is tainted by `attr()`.
|
|
991
|
+
pub fn is_attr_tainted(&self) -> bool {
|
|
992
|
+
self.references.flags.intersects(ReferenceFlags::ATTR) | self.explicitly_attr_tainted
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/// Create VariableValue from an int.
|
|
996
|
+
fn integer(number: i32, url_data: &UrlExtraData) -> Self {
|
|
997
|
+
Self::from_token(
|
|
998
|
+
Token::Number {
|
|
999
|
+
has_sign: false,
|
|
1000
|
+
value: number as f32,
|
|
1001
|
+
int_value: Some(number),
|
|
1002
|
+
},
|
|
1003
|
+
url_data,
|
|
1004
|
+
)
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/// Create VariableValue from an int.
|
|
1008
|
+
fn ident(ident: &'static str, url_data: &UrlExtraData) -> Self {
|
|
1009
|
+
Self::from_token(Token::Ident(ident.into()), url_data)
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/// Create VariableValue from a float amount of CSS pixels.
|
|
1013
|
+
fn pixels(number: f32, url_data: &UrlExtraData) -> Self {
|
|
1014
|
+
// FIXME (https://github.com/servo/rust-cssparser/issues/266):
|
|
1015
|
+
// No way to get TokenSerializationType::Dimension without creating
|
|
1016
|
+
// Token object.
|
|
1017
|
+
Self::from_token(
|
|
1018
|
+
Token::Dimension {
|
|
1019
|
+
has_sign: false,
|
|
1020
|
+
value: number,
|
|
1021
|
+
int_value: None,
|
|
1022
|
+
unit: CowRcStr::from("px"),
|
|
1023
|
+
},
|
|
1024
|
+
url_data,
|
|
1025
|
+
)
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/// Create VariableValue from an integer amount of milliseconds.
|
|
1029
|
+
fn int_ms(number: i32, url_data: &UrlExtraData) -> Self {
|
|
1030
|
+
Self::from_token(
|
|
1031
|
+
Token::Dimension {
|
|
1032
|
+
has_sign: false,
|
|
1033
|
+
value: number as f32,
|
|
1034
|
+
int_value: Some(number),
|
|
1035
|
+
unit: CowRcStr::from("ms"),
|
|
1036
|
+
},
|
|
1037
|
+
url_data,
|
|
1038
|
+
)
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/// Create VariableValue from an integer amount of CSS pixels.
|
|
1042
|
+
fn int_pixels(number: i32, url_data: &UrlExtraData) -> Self {
|
|
1043
|
+
Self::from_token(
|
|
1044
|
+
Token::Dimension {
|
|
1045
|
+
has_sign: false,
|
|
1046
|
+
value: number as f32,
|
|
1047
|
+
int_value: Some(number),
|
|
1048
|
+
unit: CowRcStr::from("px"),
|
|
1049
|
+
},
|
|
1050
|
+
url_data,
|
|
1051
|
+
)
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
fn from_token(token: Token, url_data: &UrlExtraData) -> Self {
|
|
1055
|
+
let token_type = token.serialization_type();
|
|
1056
|
+
let mut css = token.to_css_string();
|
|
1057
|
+
css.shrink_to_fit();
|
|
1058
|
+
|
|
1059
|
+
VariableValue {
|
|
1060
|
+
css,
|
|
1061
|
+
url_data: url_data.clone(),
|
|
1062
|
+
first_token_type: token_type,
|
|
1063
|
+
last_token_type: token_type,
|
|
1064
|
+
references: Default::default(),
|
|
1065
|
+
explicitly_attr_tainted: false,
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/// Returns the raw CSS text from this VariableValue
|
|
1070
|
+
pub fn css_text(&self) -> &str {
|
|
1071
|
+
&self.css
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/// Returns whether this variable value has any reference to the environment or other
|
|
1075
|
+
/// variables.
|
|
1076
|
+
pub fn has_references(&self) -> bool {
|
|
1077
|
+
self.references.has_references()
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/// Returns the attribute references in this variable value, if any.
|
|
1081
|
+
pub fn collect_attribute_references(&self, references: &mut AttrReferenceSet) {
|
|
1082
|
+
self.references.refs.iter().for_each(|r| {
|
|
1083
|
+
if r.substitution_kind == SubstitutionFunctionKind::Attr {
|
|
1084
|
+
// For a non-ascii-lowercase attribute, whether we match
|
|
1085
|
+
// case-sensitively depends on whether the element we're
|
|
1086
|
+
// matching against is an HTML element in an HTML document.
|
|
1087
|
+
// So to simplify invalidation we collect both potential
|
|
1088
|
+
// references here.
|
|
1089
|
+
references.insert(r.name.clone().into());
|
|
1090
|
+
let lowercase = r.name.to_ascii_lowercase();
|
|
1091
|
+
if r.name != lowercase {
|
|
1092
|
+
references.insert(lowercase.into());
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
})
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/// <https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value>
|
|
1100
|
+
fn parse_declaration_value(
|
|
1101
|
+
input: &mut Parser,
|
|
1102
|
+
input_start: SourcePosition,
|
|
1103
|
+
namespaces: Option<&FxHashMap<Prefix, Namespace>>,
|
|
1104
|
+
references: &mut References,
|
|
1105
|
+
missing_closing_characters: &mut String,
|
|
1106
|
+
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError> {
|
|
1107
|
+
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
|
1108
|
+
parse_declaration_value_block(
|
|
1109
|
+
input,
|
|
1110
|
+
input_start,
|
|
1111
|
+
namespaces,
|
|
1112
|
+
references,
|
|
1113
|
+
missing_closing_characters,
|
|
1114
|
+
)
|
|
1115
|
+
})
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/// Like parse_declaration_value, but accept `!` and `;` since they are only invalid at the top level.
|
|
1119
|
+
fn parse_declaration_value_block(
|
|
1120
|
+
input: &mut Parser,
|
|
1121
|
+
input_start: SourcePosition,
|
|
1122
|
+
namespaces: Option<&FxHashMap<Prefix, Namespace>>,
|
|
1123
|
+
references: &mut References,
|
|
1124
|
+
missing_closing_characters: &mut String,
|
|
1125
|
+
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError> {
|
|
1126
|
+
let mut is_first = true;
|
|
1127
|
+
let mut first_token_type = TokenSerializationType::Nothing;
|
|
1128
|
+
let mut last_token_type = TokenSerializationType::Nothing;
|
|
1129
|
+
let mut prev_reference_index: Option<usize> = None;
|
|
1130
|
+
loop {
|
|
1131
|
+
let token_start = input.position();
|
|
1132
|
+
let Ok(token) = input.next_including_whitespace_and_comments() else {
|
|
1133
|
+
break;
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
let prev_token_type = last_token_type;
|
|
1137
|
+
let serialization_type = token.serialization_type();
|
|
1138
|
+
last_token_type = serialization_type;
|
|
1139
|
+
if is_first {
|
|
1140
|
+
first_token_type = last_token_type;
|
|
1141
|
+
is_first = false;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
macro_rules! nested {
|
|
1145
|
+
($closing:expr) => {{
|
|
1146
|
+
let mut inner_end_position = None;
|
|
1147
|
+
let result = input.parse_nested_block(|input| {
|
|
1148
|
+
let result = parse_declaration_value_block(
|
|
1149
|
+
input,
|
|
1150
|
+
input_start,
|
|
1151
|
+
namespaces,
|
|
1152
|
+
references,
|
|
1153
|
+
missing_closing_characters,
|
|
1154
|
+
)?;
|
|
1155
|
+
inner_end_position = Some(input.position());
|
|
1156
|
+
Ok(result)
|
|
1157
|
+
})?;
|
|
1158
|
+
if inner_end_position.unwrap() == input.position() {
|
|
1159
|
+
missing_closing_characters.push_str($closing);
|
|
1160
|
+
}
|
|
1161
|
+
result
|
|
1162
|
+
}};
|
|
1163
|
+
}
|
|
1164
|
+
if let Some(index) = prev_reference_index.take() {
|
|
1165
|
+
references.refs[index].next_token_type = serialization_type;
|
|
1166
|
+
}
|
|
1167
|
+
match *token {
|
|
1168
|
+
Token::Comment(_) => {
|
|
1169
|
+
let token_slice = input.slice_from(token_start);
|
|
1170
|
+
if !token_slice.ends_with("*/") {
|
|
1171
|
+
missing_closing_characters.push_str(if token_slice.ends_with('*') {
|
|
1172
|
+
"/"
|
|
1173
|
+
} else {
|
|
1174
|
+
"*/"
|
|
1175
|
+
})
|
|
1176
|
+
}
|
|
1177
|
+
},
|
|
1178
|
+
Token::BadUrl(..) => {
|
|
1179
|
+
let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock;
|
|
1180
|
+
return Err(ParseError::custom(e));
|
|
1181
|
+
},
|
|
1182
|
+
Token::BadString(..) => {
|
|
1183
|
+
let e = StyleParseErrorKind::BadStringInDeclarationValueBlock;
|
|
1184
|
+
return Err(ParseError::custom(e));
|
|
1185
|
+
},
|
|
1186
|
+
Token::CloseParenthesis => {
|
|
1187
|
+
let e = StyleParseErrorKind::UnbalancedCloseParenthesisInDeclarationValueBlock;
|
|
1188
|
+
return Err(ParseError::custom(e));
|
|
1189
|
+
},
|
|
1190
|
+
Token::CloseSquareBracket => {
|
|
1191
|
+
let e = StyleParseErrorKind::UnbalancedCloseSquareBracketInDeclarationValueBlock;
|
|
1192
|
+
return Err(ParseError::custom(e));
|
|
1193
|
+
},
|
|
1194
|
+
Token::CloseCurlyBracket => {
|
|
1195
|
+
let e = StyleParseErrorKind::UnbalancedCloseCurlyBracketInDeclarationValueBlock;
|
|
1196
|
+
return Err(ParseError::custom(e));
|
|
1197
|
+
},
|
|
1198
|
+
Token::Function(ref name) => {
|
|
1199
|
+
let substitution_kind = SubstitutionFunctionKind::from_ident(name).ok();
|
|
1200
|
+
if let Some(substitution_kind) = substitution_kind {
|
|
1201
|
+
let our_ref_index = references.refs.len();
|
|
1202
|
+
let mut input_end_position = None;
|
|
1203
|
+
let fallback = input.parse_nested_block(|input| {
|
|
1204
|
+
let mut namespace = ParsedNamespace::Known(Namespace::default());
|
|
1205
|
+
if substitution_kind == SubstitutionFunctionKind::Attr {
|
|
1206
|
+
if let Some(namespaces) = namespaces {
|
|
1207
|
+
if let Ok(ns) = input
|
|
1208
|
+
.try_parse(|input| ParsedNamespace::parse(namespaces, input))
|
|
1209
|
+
{
|
|
1210
|
+
namespace = ns;
|
|
1211
|
+
let prev = input.state();
|
|
1212
|
+
let next = match *input.next_including_whitespace()? {
|
|
1213
|
+
Token::Ident(_) => Ok(()),
|
|
1214
|
+
_ => Err(ParseError::unexpected_token()),
|
|
1215
|
+
};
|
|
1216
|
+
input.reset(&prev);
|
|
1217
|
+
next?;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
// TODO(emilio): For env() this should be <custom-ident> per spec, but no other browser does
|
|
1222
|
+
// that, see https://github.com/w3c/csswg-drafts/issues/3262.
|
|
1223
|
+
let name = input.expect_ident()?;
|
|
1224
|
+
let name =
|
|
1225
|
+
Atom::from(if substitution_kind == SubstitutionFunctionKind::Var {
|
|
1226
|
+
match parse_name(name.as_ref()) {
|
|
1227
|
+
Ok(name) => name,
|
|
1228
|
+
Err(()) => {
|
|
1229
|
+
return Err(ParseError::custom(
|
|
1230
|
+
SelectorParseErrorKind::UnexpectedIdent,
|
|
1231
|
+
));
|
|
1232
|
+
},
|
|
1233
|
+
}
|
|
1234
|
+
} else {
|
|
1235
|
+
name.as_ref()
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
let attribute_kind = if substitution_kind == SubstitutionFunctionKind::Attr
|
|
1239
|
+
{
|
|
1240
|
+
parse_attr_type(input)
|
|
1241
|
+
} else {
|
|
1242
|
+
AttributeType::None
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
// We want the order of the references to match source order. So we need to reserve our slot
|
|
1246
|
+
// now, _before_ parsing our fallback. Note that we don't care if parsing fails after all, since
|
|
1247
|
+
// if this fails we discard the whole result anyways.
|
|
1248
|
+
let start = token_start.byte_index() - input_start.byte_index();
|
|
1249
|
+
references.refs.push(SubstitutionFunctionReference {
|
|
1250
|
+
name,
|
|
1251
|
+
start,
|
|
1252
|
+
// To be fixed up after parsing fallback and auto-closing via our_ref_index.
|
|
1253
|
+
end: start,
|
|
1254
|
+
prev_token_type,
|
|
1255
|
+
// To be fixed up (if needed) on the next loop iteration via prev_reference_index.
|
|
1256
|
+
next_token_type: TokenSerializationType::Nothing,
|
|
1257
|
+
// To be fixed up after parsing fallback.
|
|
1258
|
+
fallback: None,
|
|
1259
|
+
attribute_data: AttributeData {
|
|
1260
|
+
kind: attribute_kind,
|
|
1261
|
+
namespace,
|
|
1262
|
+
},
|
|
1263
|
+
substitution_kind,
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
let mut fallback = None;
|
|
1267
|
+
if input.try_parse(|input| input.expect_comma()).is_ok() {
|
|
1268
|
+
input.skip_whitespace();
|
|
1269
|
+
let fallback_start = num::NonZeroUsize::new(
|
|
1270
|
+
input.position().byte_index() - input_start.byte_index(),
|
|
1271
|
+
)
|
|
1272
|
+
.unwrap();
|
|
1273
|
+
let mut references = References::default();
|
|
1274
|
+
// NOTE(emilio): Intentionally using parse_declaration_value rather than
|
|
1275
|
+
// parse_declaration_value_block, since that's what parse_fallback used to do.
|
|
1276
|
+
let (first, last) = parse_declaration_value(
|
|
1277
|
+
input,
|
|
1278
|
+
input_start,
|
|
1279
|
+
namespaces,
|
|
1280
|
+
&mut references,
|
|
1281
|
+
missing_closing_characters,
|
|
1282
|
+
)?;
|
|
1283
|
+
fallback = Some(VariableFallback {
|
|
1284
|
+
start: fallback_start,
|
|
1285
|
+
first_token_type: first,
|
|
1286
|
+
last_token_type: last,
|
|
1287
|
+
references,
|
|
1288
|
+
});
|
|
1289
|
+
input_end_position = Some(input.position());
|
|
1290
|
+
} else {
|
|
1291
|
+
let state = input.state();
|
|
1292
|
+
// We still need to consume the rest of the potentially-unclosed
|
|
1293
|
+
// tokens, but make sure to not consume tokens that would otherwise be
|
|
1294
|
+
// invalid, by calling reset().
|
|
1295
|
+
parse_declaration_value_block(
|
|
1296
|
+
input,
|
|
1297
|
+
input_start,
|
|
1298
|
+
namespaces,
|
|
1299
|
+
references,
|
|
1300
|
+
missing_closing_characters,
|
|
1301
|
+
)?;
|
|
1302
|
+
input_end_position = Some(input.position());
|
|
1303
|
+
input.reset(&state);
|
|
1304
|
+
}
|
|
1305
|
+
Ok(fallback)
|
|
1306
|
+
})?;
|
|
1307
|
+
if input_end_position.unwrap() == input.position() {
|
|
1308
|
+
missing_closing_characters.push(')');
|
|
1309
|
+
}
|
|
1310
|
+
prev_reference_index = Some(our_ref_index);
|
|
1311
|
+
let reference = &mut references.refs[our_ref_index];
|
|
1312
|
+
reference.end = input.position().byte_index() - input_start.byte_index()
|
|
1313
|
+
+ missing_closing_characters.len();
|
|
1314
|
+
reference.fallback = fallback;
|
|
1315
|
+
references.flags |= match substitution_kind {
|
|
1316
|
+
SubstitutionFunctionKind::Var => ReferenceFlags::VAR,
|
|
1317
|
+
SubstitutionFunctionKind::Env => ReferenceFlags::ENV,
|
|
1318
|
+
SubstitutionFunctionKind::Attr => ReferenceFlags::ATTR,
|
|
1319
|
+
};
|
|
1320
|
+
// Bubble up flags from our fallback, so we know what we might reference from the outer scope.
|
|
1321
|
+
if let Some(ref fb) = reference.fallback {
|
|
1322
|
+
references.flags |= fb.references.flags;
|
|
1323
|
+
}
|
|
1324
|
+
} else {
|
|
1325
|
+
nested!(")");
|
|
1326
|
+
}
|
|
1327
|
+
},
|
|
1328
|
+
Token::ParenthesisBlock => {
|
|
1329
|
+
nested!(")");
|
|
1330
|
+
},
|
|
1331
|
+
Token::CurlyBracketBlock => {
|
|
1332
|
+
nested!("}");
|
|
1333
|
+
},
|
|
1334
|
+
Token::SquareBracketBlock => {
|
|
1335
|
+
nested!("]");
|
|
1336
|
+
},
|
|
1337
|
+
Token::QuotedString(_) => {
|
|
1338
|
+
let token_slice = input.slice_from(token_start);
|
|
1339
|
+
let quote = &token_slice[..1];
|
|
1340
|
+
debug_assert!(matches!(quote, "\"" | "'"));
|
|
1341
|
+
if !(token_slice.ends_with(quote) && token_slice.len() > 1) {
|
|
1342
|
+
missing_closing_characters.push_str(quote)
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
Token::Ident(ref value)
|
|
1346
|
+
| Token::AtKeyword(ref value)
|
|
1347
|
+
| Token::Hash(ref value)
|
|
1348
|
+
| Token::IDHash(ref value)
|
|
1349
|
+
| Token::UnquotedUrl(ref value)
|
|
1350
|
+
| Token::Dimension {
|
|
1351
|
+
unit: ref value, ..
|
|
1352
|
+
} => {
|
|
1353
|
+
references.flags.insert(ReferenceFlags::from_unit(value));
|
|
1354
|
+
let is_unquoted_url = matches!(token, Token::UnquotedUrl(_));
|
|
1355
|
+
if value.ends_with("�") && input.slice_from(token_start).ends_with("\\") {
|
|
1356
|
+
// Unescaped backslash at EOF in these contexts is interpreted as U+FFFD
|
|
1357
|
+
// Check the value in case the final backslash was itself escaped.
|
|
1358
|
+
// Serialize as escaped U+FFFD, which is also interpreted as U+FFFD.
|
|
1359
|
+
// (Unescaped U+FFFD would also work, but removing the backslash is annoying.)
|
|
1360
|
+
missing_closing_characters.push('�')
|
|
1361
|
+
}
|
|
1362
|
+
if is_unquoted_url && !input.slice_from(token_start).ends_with(")") {
|
|
1363
|
+
missing_closing_characters.push(')');
|
|
1364
|
+
}
|
|
1365
|
+
},
|
|
1366
|
+
_ => {},
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
Ok((first_token_type, last_token_type))
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/// Parse <attr-type> = type( <syntax> ) | raw-string | number | <attr-unit>.
|
|
1373
|
+
/// https://drafts.csswg.org/css-values-5/#attr-notation
|
|
1374
|
+
fn parse_attr_type(input: &mut Parser) -> AttributeType {
|
|
1375
|
+
input
|
|
1376
|
+
.try_parse(|input| {
|
|
1377
|
+
Ok(match input.next()? {
|
|
1378
|
+
Token::Function(name) if name.eq_ignore_ascii_case("type") => AttributeType::Type(
|
|
1379
|
+
input.parse_nested_block(SyntaxDescriptor::from_css_parser)?,
|
|
1380
|
+
),
|
|
1381
|
+
Token::Ident(ident) => {
|
|
1382
|
+
if ident.eq_ignore_ascii_case("raw-string") {
|
|
1383
|
+
AttributeType::RawString
|
|
1384
|
+
} else if let Ok(unit) = AttrUnit::from_ident(ident) {
|
|
1385
|
+
AttributeType::Unit(unit)
|
|
1386
|
+
} else {
|
|
1387
|
+
AttributeType::Invalid
|
|
1388
|
+
}
|
|
1389
|
+
},
|
|
1390
|
+
Token::Delim('%') => AttributeType::Unit(AttrUnit::Percentage),
|
|
1391
|
+
_ => return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
|
|
1392
|
+
})
|
|
1393
|
+
})
|
|
1394
|
+
.unwrap_or(AttributeType::None)
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/// Attribute values may reference other substitution functions we may need to process.
|
|
1398
|
+
/// See step 6: https://drafts.csswg.org/css-values-5/#attr-substitution
|
|
1399
|
+
pub fn get_attr_value_for_cycle_resolution(
|
|
1400
|
+
name: &Name,
|
|
1401
|
+
attribute_data: &AttributeData,
|
|
1402
|
+
url_data: &UrlExtraData,
|
|
1403
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1404
|
+
) -> Result<ComputedRegisteredValue, ()> {
|
|
1405
|
+
let namespace = match attribute_data.namespace {
|
|
1406
|
+
ParsedNamespace::Known(ref ns) => ns,
|
|
1407
|
+
ParsedNamespace::Unknown => return Err(()),
|
|
1408
|
+
};
|
|
1409
|
+
let attr = LocalName::with_name(name, |local_name| {
|
|
1410
|
+
attribute_tracker.query(local_name, namespace).ok_or(())
|
|
1411
|
+
})?;
|
|
1412
|
+
let mut parser = Parser::new(&attr);
|
|
1413
|
+
// TODO(Bug 2021110): Support namespaced attributes in chained references.
|
|
1414
|
+
let value = VariableValue::parse(&mut parser, None, url_data).map_err(|_| ())?;
|
|
1415
|
+
Ok(ComputedRegisteredValue::universal(Arc::new(value)))
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/// See https://drafts.csswg.org/css-variables-2/#invalid-at-computed-value-time
|
|
1419
|
+
pub fn handle_invalid_at_computed_value_time(
|
|
1420
|
+
name: &Name,
|
|
1421
|
+
registration: &PropertyDescriptors,
|
|
1422
|
+
context: &mut computed::Context,
|
|
1423
|
+
) {
|
|
1424
|
+
if !registration.is_universal() {
|
|
1425
|
+
// For the root element, inherited maps are empty. We should just
|
|
1426
|
+
// use the initial value if any, rather than removing the name.
|
|
1427
|
+
if registration.inherits() && !context.builder.is_root_element {
|
|
1428
|
+
let inherited = context.builder.inherited_style.custom_properties();
|
|
1429
|
+
if let Some(value) = inherited.get(registration, name) {
|
|
1430
|
+
context.builder.substitution_functions.insert_var(
|
|
1431
|
+
registration,
|
|
1432
|
+
name,
|
|
1433
|
+
value.clone(),
|
|
1434
|
+
);
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
} else if let Some(ref initial_value) = registration.initial_value {
|
|
1438
|
+
if let Ok(initial_value) = compute_value(
|
|
1439
|
+
&initial_value.css,
|
|
1440
|
+
&initial_value.url_data,
|
|
1441
|
+
registration,
|
|
1442
|
+
context,
|
|
1443
|
+
AttrTaint::default(),
|
|
1444
|
+
) {
|
|
1445
|
+
context.builder.substitution_functions.insert_var(
|
|
1446
|
+
registration,
|
|
1447
|
+
name,
|
|
1448
|
+
initial_value,
|
|
1449
|
+
);
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
context
|
|
1455
|
+
.builder
|
|
1456
|
+
.substitution_functions
|
|
1457
|
+
.remove_var(registration, name);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
/// Replace `var()`, `env()`, and `attr()` functions in a pre-existing variable value.
|
|
1461
|
+
pub fn substitute_references_if_needed_and_apply(
|
|
1462
|
+
name: &Name,
|
|
1463
|
+
kind: SubstitutionFunctionKind,
|
|
1464
|
+
value: &Arc<VariableValue>,
|
|
1465
|
+
stylist: &Stylist,
|
|
1466
|
+
context: &mut computed::Context,
|
|
1467
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1468
|
+
) {
|
|
1469
|
+
debug_assert_ne!(kind, SubstitutionFunctionKind::Env);
|
|
1470
|
+
let is_var = matches!(kind, SubstitutionFunctionKind::Var);
|
|
1471
|
+
let registration = stylist.get_custom_property_registration(name);
|
|
1472
|
+
if is_var && !value.has_references() && registration.is_universal() {
|
|
1473
|
+
// Trivial path: no references and no need to compute the value, just apply it directly.
|
|
1474
|
+
let computed_value = ComputedRegisteredValue::universal(Arc::clone(value));
|
|
1475
|
+
context
|
|
1476
|
+
.builder
|
|
1477
|
+
.substitution_functions
|
|
1478
|
+
.insert_var(registration, name, computed_value);
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
let url_data = &value.url_data;
|
|
1483
|
+
let substitution = substitute_internal(
|
|
1484
|
+
value,
|
|
1485
|
+
&context.builder.substitution_functions,
|
|
1486
|
+
stylist,
|
|
1487
|
+
context,
|
|
1488
|
+
attribute_tracker,
|
|
1489
|
+
&mut SmallVec::new(),
|
|
1490
|
+
None,
|
|
1491
|
+
);
|
|
1492
|
+
|
|
1493
|
+
let Ok(substitution) = substitution else {
|
|
1494
|
+
if is_var {
|
|
1495
|
+
handle_invalid_at_computed_value_time(name, registration, context);
|
|
1496
|
+
} else {
|
|
1497
|
+
context.builder.substitution_functions.remove_attr(name);
|
|
1498
|
+
}
|
|
1499
|
+
return;
|
|
1500
|
+
};
|
|
1501
|
+
|
|
1502
|
+
// If variable fallback results in a wide keyword, deal with it now.
|
|
1503
|
+
let inherited = context.builder.inherited_style.custom_properties();
|
|
1504
|
+
if is_var {
|
|
1505
|
+
let css = &substitution.css;
|
|
1506
|
+
let css_wide_kw = {
|
|
1507
|
+
let mut input = Parser::new(css);
|
|
1508
|
+
input.try_parse(CSSWideKeyword::parse)
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1511
|
+
if let Ok(kw) = css_wide_kw {
|
|
1512
|
+
// TODO: It's unclear what this should do for revert / revert-layer, see
|
|
1513
|
+
// https://github.com/w3c/csswg-drafts/issues/9131. For now treating as unset
|
|
1514
|
+
// seems fine?
|
|
1515
|
+
match (kw, registration.inherits(), context.is_root_element()) {
|
|
1516
|
+
(CSSWideKeyword::Initial, _, _)
|
|
1517
|
+
| (CSSWideKeyword::Revert, false, _)
|
|
1518
|
+
| (CSSWideKeyword::RevertLayer, false, _)
|
|
1519
|
+
| (CSSWideKeyword::RevertRule, false, _)
|
|
1520
|
+
| (CSSWideKeyword::Unset, false, _)
|
|
1521
|
+
| (CSSWideKeyword::Revert, true, true)
|
|
1522
|
+
| (CSSWideKeyword::RevertLayer, true, true)
|
|
1523
|
+
| (CSSWideKeyword::RevertRule, true, true)
|
|
1524
|
+
| (CSSWideKeyword::Unset, true, true)
|
|
1525
|
+
| (CSSWideKeyword::Inherit, _, true) => {
|
|
1526
|
+
remove_and_insert_initial_value(
|
|
1527
|
+
name,
|
|
1528
|
+
registration,
|
|
1529
|
+
&mut context.builder.substitution_functions,
|
|
1530
|
+
);
|
|
1531
|
+
},
|
|
1532
|
+
(CSSWideKeyword::Revert, true, false)
|
|
1533
|
+
| (CSSWideKeyword::RevertLayer, true, false)
|
|
1534
|
+
| (CSSWideKeyword::RevertRule, true, false)
|
|
1535
|
+
| (CSSWideKeyword::Inherit, _, false)
|
|
1536
|
+
| (CSSWideKeyword::Unset, true, false) => {
|
|
1537
|
+
match inherited.get(registration, name) {
|
|
1538
|
+
Some(value) => {
|
|
1539
|
+
context.builder.substitution_functions.insert_var(
|
|
1540
|
+
registration,
|
|
1541
|
+
name,
|
|
1542
|
+
value.clone(),
|
|
1543
|
+
);
|
|
1544
|
+
},
|
|
1545
|
+
None => {
|
|
1546
|
+
context
|
|
1547
|
+
.builder
|
|
1548
|
+
.substitution_functions
|
|
1549
|
+
.remove_var(registration, name);
|
|
1550
|
+
},
|
|
1551
|
+
};
|
|
1552
|
+
},
|
|
1553
|
+
}
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
match kind {
|
|
1559
|
+
SubstitutionFunctionKind::Var => {
|
|
1560
|
+
let value = match substitution.into_value(url_data, registration, context) {
|
|
1561
|
+
Ok(v) => v,
|
|
1562
|
+
Err(()) => {
|
|
1563
|
+
handle_invalid_at_computed_value_time(name, registration, context);
|
|
1564
|
+
return;
|
|
1565
|
+
},
|
|
1566
|
+
};
|
|
1567
|
+
context
|
|
1568
|
+
.builder
|
|
1569
|
+
.substitution_functions
|
|
1570
|
+
.insert_var(registration, name, value);
|
|
1571
|
+
},
|
|
1572
|
+
SubstitutionFunctionKind::Attr => {
|
|
1573
|
+
let mut value = ComputedRegisteredValue::universal(Arc::new(VariableValue::new(
|
|
1574
|
+
substitution.css.into_owned(),
|
|
1575
|
+
url_data,
|
|
1576
|
+
substitution.first_token_type,
|
|
1577
|
+
substitution.last_token_type,
|
|
1578
|
+
)));
|
|
1579
|
+
value.attr_tainted |= substitution.attr_tainted;
|
|
1580
|
+
context
|
|
1581
|
+
.builder
|
|
1582
|
+
.substitution_functions
|
|
1583
|
+
.insert_attr(name, value);
|
|
1584
|
+
},
|
|
1585
|
+
SubstitutionFunctionKind::Env => unreachable!("Kind cannot be env."),
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
#[derive(Default, Debug)]
|
|
1590
|
+
struct Substitution<'a> {
|
|
1591
|
+
css: Cow<'a, str>,
|
|
1592
|
+
first_token_type: TokenSerializationType,
|
|
1593
|
+
last_token_type: TokenSerializationType,
|
|
1594
|
+
attr_tainted: bool,
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
impl<'a> Substitution<'a> {
|
|
1598
|
+
fn from_value(v: VariableValue, attr_tainted: bool) -> Self {
|
|
1599
|
+
Substitution {
|
|
1600
|
+
css: v.css.into(),
|
|
1601
|
+
first_token_type: v.first_token_type,
|
|
1602
|
+
last_token_type: v.last_token_type,
|
|
1603
|
+
attr_tainted,
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
fn into_value(
|
|
1608
|
+
self,
|
|
1609
|
+
url_data: &UrlExtraData,
|
|
1610
|
+
registration: &PropertyDescriptors,
|
|
1611
|
+
computed_context: &computed::Context,
|
|
1612
|
+
) -> Result<ComputedRegisteredValue, ()> {
|
|
1613
|
+
if registration.is_universal() {
|
|
1614
|
+
let mut value = ComputedRegisteredValue::universal(Arc::new(VariableValue::new(
|
|
1615
|
+
self.css.into_owned(),
|
|
1616
|
+
url_data,
|
|
1617
|
+
self.first_token_type,
|
|
1618
|
+
self.last_token_type,
|
|
1619
|
+
)));
|
|
1620
|
+
value.attr_tainted |= self.attr_tainted;
|
|
1621
|
+
return Ok(value);
|
|
1622
|
+
}
|
|
1623
|
+
let taint = if self.attr_tainted {
|
|
1624
|
+
// Per spec: substitution value of an arbitrary substitution function is
|
|
1625
|
+
// attr()-tainted as a whole if any attr()-tainted values were involved
|
|
1626
|
+
// in creating that substitution value.
|
|
1627
|
+
// https://drafts.csswg.org/css-values-5/#attr-security
|
|
1628
|
+
AttrTaint::new_fully_tainted(self.css.len())
|
|
1629
|
+
} else {
|
|
1630
|
+
AttrTaint::default()
|
|
1631
|
+
};
|
|
1632
|
+
let mut v = compute_value(&self.css, url_data, registration, computed_context, taint)?;
|
|
1633
|
+
v.attr_tainted |= self.attr_tainted;
|
|
1634
|
+
Ok(v)
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
fn new(
|
|
1638
|
+
css: Cow<'a, str>,
|
|
1639
|
+
first_token_type: TokenSerializationType,
|
|
1640
|
+
last_token_type: TokenSerializationType,
|
|
1641
|
+
attr_tainted: bool,
|
|
1642
|
+
) -> Self {
|
|
1643
|
+
Self {
|
|
1644
|
+
css,
|
|
1645
|
+
first_token_type,
|
|
1646
|
+
last_token_type,
|
|
1647
|
+
attr_tainted,
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/// Result of var(), env(), and attr() substitution.
|
|
1653
|
+
#[derive(Debug)]
|
|
1654
|
+
pub struct SubstitutionResult<'a> {
|
|
1655
|
+
/// The resolved CSS string after substitution.
|
|
1656
|
+
pub css: Cow<'a, str>,
|
|
1657
|
+
/// Regions in the `css` string that are attr()-tainted, if any.
|
|
1658
|
+
pub attr_taint: AttrTaint,
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
fn compute_value(
|
|
1662
|
+
css: &str,
|
|
1663
|
+
url_data: &UrlExtraData,
|
|
1664
|
+
registration: &PropertyDescriptors,
|
|
1665
|
+
computed_context: &computed::Context,
|
|
1666
|
+
attr_taint: AttrTaint,
|
|
1667
|
+
) -> Result<ComputedRegisteredValue, ()> {
|
|
1668
|
+
debug_assert!(!registration.is_universal());
|
|
1669
|
+
|
|
1670
|
+
let mut input = Parser::new(css);
|
|
1671
|
+
SpecifiedRegisteredValue::compute(
|
|
1672
|
+
&mut input,
|
|
1673
|
+
registration,
|
|
1674
|
+
None,
|
|
1675
|
+
url_data,
|
|
1676
|
+
computed_context,
|
|
1677
|
+
AllowComputationallyDependent::Yes,
|
|
1678
|
+
attr_taint,
|
|
1679
|
+
)
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
/// Removes the named registered custom property and inserts its uncomputed initial value.
|
|
1683
|
+
pub(crate) fn remove_and_insert_initial_value(
|
|
1684
|
+
name: &Name,
|
|
1685
|
+
registration: &PropertyDescriptors,
|
|
1686
|
+
substitution_functions: &mut ComputedSubstitutionFunctions,
|
|
1687
|
+
) {
|
|
1688
|
+
substitution_functions.remove_var(registration, name);
|
|
1689
|
+
if let Some(ref initial_value) = registration.initial_value {
|
|
1690
|
+
let value = ComputedRegisteredValue::universal(Arc::clone(initial_value));
|
|
1691
|
+
substitution_functions.insert_var(registration, name, value);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
fn do_substitute_chunk<'a>(
|
|
1696
|
+
css: &'a str,
|
|
1697
|
+
start: usize,
|
|
1698
|
+
end: usize,
|
|
1699
|
+
first_token_type: TokenSerializationType,
|
|
1700
|
+
last_token_type: TokenSerializationType,
|
|
1701
|
+
url_data: &UrlExtraData,
|
|
1702
|
+
substitution_functions: &'a ComputedSubstitutionFunctions,
|
|
1703
|
+
stylist: &Stylist,
|
|
1704
|
+
computed_context: &computed::Context,
|
|
1705
|
+
references: &'a [SubstitutionFunctionReference],
|
|
1706
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1707
|
+
seen: &mut SmallVec<[&'a Name; 8]>,
|
|
1708
|
+
mut attr_taint: Option<&mut AttrTaint>,
|
|
1709
|
+
) -> Result<Substitution<'a>, ()> {
|
|
1710
|
+
if start == end {
|
|
1711
|
+
// Empty string. Easy.
|
|
1712
|
+
return Ok(Substitution::default());
|
|
1713
|
+
}
|
|
1714
|
+
// Easy case: no references involved.
|
|
1715
|
+
if references.is_empty() {
|
|
1716
|
+
let result = &css[start..end];
|
|
1717
|
+
return Ok(Substitution::new(
|
|
1718
|
+
Cow::Borrowed(result),
|
|
1719
|
+
first_token_type,
|
|
1720
|
+
last_token_type,
|
|
1721
|
+
Default::default(),
|
|
1722
|
+
));
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
let mut substituted = ComputedValue::empty(url_data);
|
|
1726
|
+
let mut next_token_type = first_token_type;
|
|
1727
|
+
let mut cur_pos = start;
|
|
1728
|
+
let mut attr_tainted = false;
|
|
1729
|
+
let references = references.iter();
|
|
1730
|
+
for reference in references {
|
|
1731
|
+
if reference.start != cur_pos {
|
|
1732
|
+
substituted.push(
|
|
1733
|
+
&css[cur_pos..reference.start],
|
|
1734
|
+
next_token_type,
|
|
1735
|
+
reference.prev_token_type,
|
|
1736
|
+
/* attr_taint */ None,
|
|
1737
|
+
)?;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
let substitution = substitute_one_reference(
|
|
1741
|
+
css,
|
|
1742
|
+
url_data,
|
|
1743
|
+
substitution_functions,
|
|
1744
|
+
reference,
|
|
1745
|
+
stylist,
|
|
1746
|
+
computed_context,
|
|
1747
|
+
attribute_tracker,
|
|
1748
|
+
seen,
|
|
1749
|
+
)?;
|
|
1750
|
+
|
|
1751
|
+
// Optimize the property: var(--...) case to avoid allocating at all.
|
|
1752
|
+
if reference.start == start && reference.end == end {
|
|
1753
|
+
if let Some(taint) = attr_taint.filter(|_| substitution.attr_tainted) {
|
|
1754
|
+
taint.push(start, substitution.css.len());
|
|
1755
|
+
}
|
|
1756
|
+
return Ok(substitution);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
substituted.push(
|
|
1760
|
+
&substitution.css,
|
|
1761
|
+
substitution.first_token_type,
|
|
1762
|
+
substitution.last_token_type,
|
|
1763
|
+
attr_taint
|
|
1764
|
+
.as_deref_mut()
|
|
1765
|
+
.filter(|_| substitution.attr_tainted),
|
|
1766
|
+
)?;
|
|
1767
|
+
attr_tainted |= substitution.attr_tainted;
|
|
1768
|
+
next_token_type = reference.next_token_type;
|
|
1769
|
+
cur_pos = reference.end;
|
|
1770
|
+
}
|
|
1771
|
+
// Push the rest of the value if needed.
|
|
1772
|
+
if cur_pos != end {
|
|
1773
|
+
substituted.push(
|
|
1774
|
+
&css[cur_pos..end],
|
|
1775
|
+
next_token_type,
|
|
1776
|
+
last_token_type,
|
|
1777
|
+
/* attr_taint */ None,
|
|
1778
|
+
)?;
|
|
1779
|
+
}
|
|
1780
|
+
Ok(Substitution::from_value(substituted, attr_tainted))
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
fn quoted_css_string(src: &str) -> String {
|
|
1784
|
+
let mut dest = String::with_capacity(src.len() + 2);
|
|
1785
|
+
cssparser::serialize_string(src, &mut dest).unwrap();
|
|
1786
|
+
dest
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
fn substitute_one_reference<'a>(
|
|
1790
|
+
css: &'a str,
|
|
1791
|
+
url_data: &UrlExtraData,
|
|
1792
|
+
substitution_functions: &'a ComputedSubstitutionFunctions,
|
|
1793
|
+
reference: &'a SubstitutionFunctionReference,
|
|
1794
|
+
stylist: &Stylist,
|
|
1795
|
+
computed_context: &computed::Context,
|
|
1796
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1797
|
+
seen: &mut SmallVec<[&'a Name; 8]>,
|
|
1798
|
+
) -> Result<Substitution<'a>, ()> {
|
|
1799
|
+
let simple_attr_subst = |s: &str| {
|
|
1800
|
+
Some(Substitution::new(
|
|
1801
|
+
Cow::Owned(quoted_css_string(s)),
|
|
1802
|
+
TokenSerializationType::Nothing,
|
|
1803
|
+
TokenSerializationType::Nothing,
|
|
1804
|
+
/* attr_tainted */ true,
|
|
1805
|
+
))
|
|
1806
|
+
};
|
|
1807
|
+
let substitution: Option<_> = match reference.substitution_kind {
|
|
1808
|
+
SubstitutionFunctionKind::Var => {
|
|
1809
|
+
let registration = stylist.get_custom_property_registration(&reference.name);
|
|
1810
|
+
match substitution_functions.get_var(registration, &reference.name) {
|
|
1811
|
+
None => None,
|
|
1812
|
+
// If the referenced value is itself still unresolved (i.e. it has references), we
|
|
1813
|
+
// are substituting against a partially-resolved map -- this happens while applying
|
|
1814
|
+
// a prioritary property in the middle of custom-property resolution. Resolve it
|
|
1815
|
+
// recursively, guarding against cycles (which the resolver will also remove, but it
|
|
1816
|
+
// may not have gotten to this variable yet).
|
|
1817
|
+
Some(v) => match v.as_universal() {
|
|
1818
|
+
Some(u) if u.has_references() => {
|
|
1819
|
+
if seen.contains(&&reference.name) {
|
|
1820
|
+
// Cycle: the primary is guaranteed-invalid, so fall through to the
|
|
1821
|
+
// fallback (if any), like any other invalid primary.
|
|
1822
|
+
None
|
|
1823
|
+
} else {
|
|
1824
|
+
seen.push(&reference.name);
|
|
1825
|
+
let result = substitute_internal(
|
|
1826
|
+
u,
|
|
1827
|
+
substitution_functions,
|
|
1828
|
+
stylist,
|
|
1829
|
+
computed_context,
|
|
1830
|
+
attribute_tracker,
|
|
1831
|
+
seen,
|
|
1832
|
+
/* attr_taint */ None,
|
|
1833
|
+
);
|
|
1834
|
+
seen.pop();
|
|
1835
|
+
match result {
|
|
1836
|
+
Ok(mut substitution) => {
|
|
1837
|
+
substitution.attr_tainted |= v.attr_tainted;
|
|
1838
|
+
Some(substitution)
|
|
1839
|
+
},
|
|
1840
|
+
// The primary couldn't be resolved (invalid); use the fallback.
|
|
1841
|
+
Err(()) => None,
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
},
|
|
1845
|
+
_ => Some(Substitution::from_value(
|
|
1846
|
+
v.to_variable_value(),
|
|
1847
|
+
v.attr_tainted,
|
|
1848
|
+
)),
|
|
1849
|
+
},
|
|
1850
|
+
}
|
|
1851
|
+
},
|
|
1852
|
+
SubstitutionFunctionKind::Env => {
|
|
1853
|
+
let device = stylist.device();
|
|
1854
|
+
device
|
|
1855
|
+
.environment()
|
|
1856
|
+
.get(&reference.name, device, url_data)
|
|
1857
|
+
.map(|v| Substitution::from_value(v, /* attr_tainted */ false))
|
|
1858
|
+
},
|
|
1859
|
+
// https://drafts.csswg.org/css-values-5/#attr-substitution
|
|
1860
|
+
SubstitutionFunctionKind::Attr => {
|
|
1861
|
+
let namespace = match reference.attribute_data.namespace {
|
|
1862
|
+
ParsedNamespace::Known(ref ns) => Some(ns),
|
|
1863
|
+
ParsedNamespace::Unknown => None,
|
|
1864
|
+
};
|
|
1865
|
+
namespace
|
|
1866
|
+
.and_then(|namespace| {
|
|
1867
|
+
LocalName::with_name(&reference.name, |local_name| {
|
|
1868
|
+
attribute_tracker.query(local_name, namespace)
|
|
1869
|
+
})
|
|
1870
|
+
})
|
|
1871
|
+
.map_or_else(
|
|
1872
|
+
|| {
|
|
1873
|
+
// Special case when fallback and <attr-type> are omitted.
|
|
1874
|
+
// See FAILURE: https://drafts.csswg.org/css-values-5/#attr-substitution
|
|
1875
|
+
if reference.fallback.is_none()
|
|
1876
|
+
&& reference.attribute_data.kind == AttributeType::None
|
|
1877
|
+
{
|
|
1878
|
+
simple_attr_subst("")
|
|
1879
|
+
} else {
|
|
1880
|
+
None
|
|
1881
|
+
}
|
|
1882
|
+
},
|
|
1883
|
+
|attr| {
|
|
1884
|
+
let attr = if let AttributeType::Type(_) = &reference.attribute_data.kind {
|
|
1885
|
+
// If we're evaluating a container query, we haven't run the cascade
|
|
1886
|
+
// and populated substitution_functions.attributes, so we can't do the
|
|
1887
|
+
// get_attr() lookup here.
|
|
1888
|
+
// TODO: This means chained attr() references will not work reliably in
|
|
1889
|
+
// container style queries:
|
|
1890
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=2028861
|
|
1891
|
+
if computed_context.in_container_query {
|
|
1892
|
+
attr
|
|
1893
|
+
} else {
|
|
1894
|
+
substitution_functions
|
|
1895
|
+
.get_attr(&reference.name)
|
|
1896
|
+
.map(|v| v.to_variable_value())?
|
|
1897
|
+
.css
|
|
1898
|
+
}
|
|
1899
|
+
} else {
|
|
1900
|
+
attr
|
|
1901
|
+
};
|
|
1902
|
+
let mut parser = Parser::new(&attr);
|
|
1903
|
+
match &reference.attribute_data.kind {
|
|
1904
|
+
AttributeType::Unit(unit) => {
|
|
1905
|
+
let css = {
|
|
1906
|
+
// Verify that attribute data is a <number-token>.
|
|
1907
|
+
parser.expect_number().ok()?;
|
|
1908
|
+
let mut s = attr.clone();
|
|
1909
|
+
s.push_str(unit.as_ref());
|
|
1910
|
+
s
|
|
1911
|
+
};
|
|
1912
|
+
let serialization = match unit {
|
|
1913
|
+
AttrUnit::Number => TokenSerializationType::Number,
|
|
1914
|
+
AttrUnit::Percentage => TokenSerializationType::Percentage,
|
|
1915
|
+
_ => TokenSerializationType::Dimension,
|
|
1916
|
+
};
|
|
1917
|
+
let value =
|
|
1918
|
+
ComputedValue::new(css, url_data, serialization, serialization);
|
|
1919
|
+
Some(Substitution::from_value(
|
|
1920
|
+
value, /* attr_tainted */ true,
|
|
1921
|
+
))
|
|
1922
|
+
},
|
|
1923
|
+
AttributeType::Type(syntax) => {
|
|
1924
|
+
let value = SpecifiedRegisteredValue::parse(
|
|
1925
|
+
&mut parser,
|
|
1926
|
+
syntax,
|
|
1927
|
+
url_data,
|
|
1928
|
+
None,
|
|
1929
|
+
AllowComputationallyDependent::Yes,
|
|
1930
|
+
AttrTaint::default(),
|
|
1931
|
+
)
|
|
1932
|
+
.ok()?;
|
|
1933
|
+
let value = value.to_variable_value();
|
|
1934
|
+
Some(Substitution::from_value(
|
|
1935
|
+
value, /* attr_tainted */ true,
|
|
1936
|
+
))
|
|
1937
|
+
},
|
|
1938
|
+
AttributeType::RawString | AttributeType::None => {
|
|
1939
|
+
simple_attr_subst(&attr)
|
|
1940
|
+
},
|
|
1941
|
+
AttributeType::Invalid => None,
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
)
|
|
1945
|
+
},
|
|
1946
|
+
};
|
|
1947
|
+
|
|
1948
|
+
if let Some(s) = substitution {
|
|
1949
|
+
return Ok(s);
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
let Some(ref fallback) = reference.fallback else {
|
|
1953
|
+
return Err(());
|
|
1954
|
+
};
|
|
1955
|
+
|
|
1956
|
+
do_substitute_chunk(
|
|
1957
|
+
css,
|
|
1958
|
+
fallback.start.get(),
|
|
1959
|
+
reference.end - 1, // Skip the closing parenthesis of the reference value.
|
|
1960
|
+
fallback.first_token_type,
|
|
1961
|
+
fallback.last_token_type,
|
|
1962
|
+
url_data,
|
|
1963
|
+
substitution_functions,
|
|
1964
|
+
stylist,
|
|
1965
|
+
computed_context,
|
|
1966
|
+
&fallback.references.refs,
|
|
1967
|
+
attribute_tracker,
|
|
1968
|
+
seen,
|
|
1969
|
+
/* attr_taint */ None,
|
|
1970
|
+
)
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
/// Replace `var()`, `env()`, and `attr()` functions. Return `Err(..)` for invalid at computed time.
|
|
1974
|
+
fn substitute_internal<'a>(
|
|
1975
|
+
variable_value: &'a VariableValue,
|
|
1976
|
+
substitution_functions: &'a ComputedSubstitutionFunctions,
|
|
1977
|
+
stylist: &Stylist,
|
|
1978
|
+
computed_context: &computed::Context,
|
|
1979
|
+
attribute_tracker: &mut AttributeTracker,
|
|
1980
|
+
seen: &mut SmallVec<[&'a Name; 8]>,
|
|
1981
|
+
attr_taint: Option<&mut AttrTaint>,
|
|
1982
|
+
) -> Result<Substitution<'a>, ()> {
|
|
1983
|
+
do_substitute_chunk(
|
|
1984
|
+
&variable_value.css,
|
|
1985
|
+
/* start = */ 0,
|
|
1986
|
+
/* end = */ variable_value.css.len(),
|
|
1987
|
+
variable_value.first_token_type,
|
|
1988
|
+
variable_value.last_token_type,
|
|
1989
|
+
&variable_value.url_data,
|
|
1990
|
+
substitution_functions,
|
|
1991
|
+
stylist,
|
|
1992
|
+
computed_context,
|
|
1993
|
+
&variable_value.references.refs,
|
|
1994
|
+
attribute_tracker,
|
|
1995
|
+
seen,
|
|
1996
|
+
attr_taint,
|
|
1997
|
+
)
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
/// Replace var(), env(), and attr() functions, returning the resulting CSS string.
|
|
2001
|
+
pub fn substitute<'a>(
|
|
2002
|
+
variable_value: &'a VariableValue,
|
|
2003
|
+
substitution_functions: &'a ComputedSubstitutionFunctions,
|
|
2004
|
+
stylist: &Stylist,
|
|
2005
|
+
computed_context: &computed::Context,
|
|
2006
|
+
attribute_tracker: &mut AttributeTracker,
|
|
2007
|
+
) -> Result<SubstitutionResult<'a>, ()> {
|
|
2008
|
+
debug_assert!(variable_value.has_references());
|
|
2009
|
+
let mut attr_taint = AttrTaint::default();
|
|
2010
|
+
let v = substitute_internal(
|
|
2011
|
+
variable_value,
|
|
2012
|
+
substitution_functions,
|
|
2013
|
+
stylist,
|
|
2014
|
+
computed_context,
|
|
2015
|
+
attribute_tracker,
|
|
2016
|
+
&mut SmallVec::new(),
|
|
2017
|
+
Some(&mut attr_taint),
|
|
2018
|
+
)?;
|
|
2019
|
+
Ok(SubstitutionResult {
|
|
2020
|
+
css: v.css,
|
|
2021
|
+
attr_taint,
|
|
2022
|
+
})
|
|
2023
|
+
}
|